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

Community
  • 1
  • 1
Matti Lyra
  • 12,828
  • 8
  • 49
  • 67

0 Answers0