2

I want to be able to create python objects with instance attributes defined dynamically from a dictionary. I am aware of at least two ways of doing this, simply calling setattr(self, k, v) or alternatively calling setattr(FooClass, k, property(lambda x: v)). The first option works completely fine, but I can't get the second to do what I want. Consider the following code

class A(object):
    def __init__(self, d):
        for k in d:
            setattr(A, k, property(lambda x: ('Value(%s)' % d[k], 'Key(%s)' % k, d)))

>>> a = A({'foo':'1','bar':'2'})
>>> a.foo
('Value(2)', 'Key(bar)', {'bar': '2', 'foo': '1'})

Why does a.foo not return the value of foo in the dictionary? The property objects and their getters are distinct.

>>> A.foo, A.bar
<property at 0x1106c1d60>, <property at 0x1106c1d08>
>>> A.foo.__get__
<method-wrapper '__get__' of property object at 0x1106c1d60>
>>> A.bar.__get__
<method-wrapper '__get__' of property object at 0x1106c1d08>

There are quite a few related questions on SO, the best one being this (How to add property to a class dynamically?) and Eevee's answer. That doesn't quite explain the behaviour though.

These deal with attributes on a class instance, not Property objects on the Class

UPDATE

Thanks to Martijn Pieters for his help (answer). The answer lies in the scope of the lambda function.

>>> A.bar.fget.__closure__[0], A.foo.fget.__closure__[0]
<cell at 0x1106c63d0: dict object at 0x7f90f699fe80>, <cell at 0x1106c63d0: dict object at 0x7f90f699fe80>

Working version:

class A(object):
    def __init__(self, d):
        for k, v in d.items():
            setattr(A, k, property(lambda x, v=v, k=k: ('Value(%s)' % v, 'Key(%s)' % k, d)))

Although this actually modifies every instance of the class to have the same attribute values.

Lesson:

be careful with lambda functions inside loops

6
  • 1
    You are running into closures here: Local scope, beyond the scope of the enclosing Commented Oct 23, 2013 at 11:29
  • Another possible duplicate: Local variables in Python nested functions Commented Oct 23, 2013 at 11:35
  • @MattiLyra Please dont delete it, lot of useful links. Commented Oct 23, 2013 at 11:42
  • @MartijnPieters So, what is the best way to fix this problem? Commented Oct 23, 2013 at 11:51
  • @thefourtheye: See the linked posts; bind the value as a keyword argument default (so the value is stored on the lambda object), or create a new scope with a separate factory function. Commented Oct 23, 2013 at 11:52

0

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.