python scope issue with anonymous lambda in metaclass -


i using metaclass define read-only properties (accessor methods) class, adding property getter (a lambda) each field declared class. finding different behavior depending on define lambda. works if define getter lambda in external function called __new__ method of metaclass, , not if define lambda in directly in __new__ method of metaclass.

def _getter(key):     meth =  lambda self : self.__dict__[key]     print "_getter: created lambda %s key %s" % (meth, key)     return meth   class readonlyaccessors(type):      def __new__(cls, clsname, bases, dict):          fname in dict.get('_fields',[]):             key = "_%s" % fname              # way works             dict[fname] = property(_getter(key))               # way doesn't             # meth = lambda self : self.__dict__[key]             # print "readonlyaccessors.__new__: created lambda %s 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):         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 written, execution looks this:

[slass@zax src]$ python readonlyaccessors.py _getter: created lambda <function <lambda> @ 0x7f652a4d88c0> key _name _getter: created lambda <function <lambda> @ 0x7f652a4d8a28> key _number {'_number': 100, '_name': 'fred'} name = fred number = 100 

commenting out line follows "the way works" , uncommenting 3 lines following "the way doesn't" produces this:

    [slass@zax src]$ python readonlyaccessors.py     readonlyaccessors.__new__: created lambda <function <lambda> @ 0x7f40f5db1938> key _name     readonlyaccessors.__new__: created lambda <function <lambda> @ 0x7f40f5db1aa0> key _number     {'_number': 100, '_name': 'fred'}     name = 100     number = 100 

note though rot.__dict__ shows _name 'fred', value returned name property 100.

clearly i'm not understanding scope in i'm creating lambdas.

i've been reading guido's document metaclass accessors here: https://www.python.org/download/releases/2.2.3/descrintro/#cooperation python docs python data model , http://code.activestate.com/recipes/307969-generating-getset-methods-using-a-metaclass/ recipe creating accessors using metaclass, , on stackoverflow can find, i'm not getting it.

thank you.

-mike

the problem has scope. when define meth with

meth = lambda self : self.__dict__[key] 

the key variable not variable in meth's local scope. when meth function called, key must searched in enclosing scope. (see legb rule.) finds in scope of __new__ method. however, time meth gets called, value of key not value of key when meth defined. rather value of key the last value assigned due for-loop. happens '_number'. no matter meth call, value of self.__dict__['_number'] being returned.

you can see happening defining meth way inside __new__:

    fname in dict.get('_fields',[]):         key = "_%s" % fname          def meth(self):             print(key) # see `meth` believes `key`             return self.__dict__[key] 

yields

_number    # key `_number` _number name = 100 number = 100 

the reason why _getter works because key gets passed _getter. when meth gets called, finds value of key in _getter's scope, key retains value got when _getter called.


if want use lambda instead of _getter, can using default value key:

meth = lambda self, key=key: self.__dict__[key] 

now, inside meth, key local variable. when meth called, value of key value of key in local scope. default value bound function @ definition-time, right value being bound each meth lambda function.


Comments

Popular posts from this blog

java - How to specify maven bin in eclipse maven plugin? -

single sign on - Logging into Plone site with credentials passed through HTTP -

php - Why does AJAX not process login form? -