I am using a metaclass to define read-only properties (accessor methods) for a class, by adding a property with only a getter (a lambda) for each field declared by the class. I am finding different behavior depending on where I define the lambda. It works if I define the getter lambda in an external function that is called by the __new__ method of the metaclass, and not if I define the lambda in directly in the __new__ method of the metaclass.
def _getter(key):
meth = lambda self : self.__dict__[key]
print "_getter: created lambda %s for key %s" % (meth, key)
return meth
class ReadOnlyAccessors(type):
def __new__(cls, clsName, bases, dict):
for fname in dict.get('_fields',[]):
key = "_%s" % fname
# the way that works
dict[fname] = property(_getter(key))
# the way that doesn't
# meth = lambda self : self.__dict__[key]
# print "ReadOnlyAccessors.__new__: created lambda %s for key %s" % (meth, key)
# dict[fname] = property(meth)
return type.__new__(cls, clsName, bases, dict)
class ROThingy(object):
__metaclass__ = ReadOnlyAccessors
_fields = ("name", "number")
def __init__(self, **initializers):
for fname in self._fields:
self.__dict__[ "_%s" % fname ] = initializers.get(fname, None)
print self.__dict__
if __name__ == "__main__":
rot = ROThingy(name="Fred", number=100)
print "name = %s\nnumber = %d\n" % (rot.name, rot.number)
As currently written, execution looks like this:
[slass@zax src]$ python ReadOnlyAccessors.py
_getter: created lambda <function <lambda> at 0x7f652a4d88c0> for key _name
_getter: created lambda <function <lambda> at 0x7f652a4d8a28> for key _number
{'_number': 100, '_name': 'Fred'}
name = Fred
number = 100
Commenting out the line that follows "the way that works" and uncommenting the three lines following "the way that doesn't" produces this:
[slass@zax src]$ python ReadOnlyAccessors.py
ReadOnlyAccessors.__new__: created lambda <function <lambda> at 0x7f40f5db1938> for key _name
ReadOnlyAccessors.__new__: created lambda <function <lambda> at 0x7f40f5db1aa0> for key _number
{'_number': 100, '_name': 'Fred'}
name = 100
number = 100
Note that even though the rot.__dict__ shows that _name is 'Fred', the value returned by the name Property is 100.
Clearly I'm not understanding something about the scope in which I'm creating the lambdas.
I've been reading Guido's document about metaclass for accessors here: https://www.python.org/download/releases/2.2.3/descrintro/#cooperation as well as the Python docs for the Python Data Model and this http://code.activestate.com/recipes/307969-generating-getset-methods-using-a-metaclass/ recipe for creating accessors using a metaclass, and finally everything on StackOverflow that I can find, but I'm just not getting it.
Thank you.
-Mike