1

I am trying to set new properties to a class at runtime and attaching a lambda which behaviour depends on some property specific data. If I set the properties in a loop, as in class B, the first property gets overwritten. While setting them sequentially works. Can't find the problem. There is mention in other question that I should use __getattr__ instead of properties, as in link. But I would like to understand what the issue here.

import time
class A():
    def __init__(self, dynprops = ['a', 'b']):

        setattr(A, 'a', property(lambda x: self.print_prop('a')))
        setattr(A, 'b', property(lambda x: self.print_prop('b')))

    def print_prop(self, dynprop):
        print(time.time(), dynprop)

class B():
    def __init__(self, dynprops = ['a', 'b']):

        for prop in dynprops:
            setattr(B, prop, property(lambda x: self.print_prop(prop)))

    def print_prop(self, dynprop):
        print(time.time(), dynprop)

a = A()
print(a.a)
print(a.b)
b = B()
print(b.a)
print(b.b)

gives

>> 1481137121.6755576 a
>> None
>> 1481137121.6756482 b
>> None
>> 1481137121.6757774 b
>> None
>> 1481137121.6758344 b
>> None
Community
  • 1
  • 1
toine
  • 1,946
  • 18
  • 24

1 Answers1

3

It happens because prop is changed to 'b' and then stays like that. The lambda function returns the current value of prop, not the value that was assigned to it during the creation of the lambda. To avoid this behaviour you should use a closure:

class B():
    def __init__(self, dynprops = ['a', 'b']):
        for prop in dynprops:
            setattr(B, prop, property(self.printer(prop)))

    def print_prop(self, dynprop):
        print(time.time(), dynprop)

    def printer(self, prop):
        return lambda x: self.print_prop(prop)
toine
  • 1,946
  • 18
  • 24
kmaork
  • 5,722
  • 2
  • 23
  • 40