In Python, as I understand it, variables are really references to objects in the given namespace. So in the following example, it is unsurprising that when noise changes in the global namespace, the value returned by cat.noise changes, as the reference in the setattr line is using the reference of noise, not its underlying value.
class Cat(object):
pass
noise = "meow"
setattr(Cat, "noise", property(lambda self: noise))
cat = Cat()
cat.noise
# Outputs "meow"
noise = "purrrrr"
cat.noise
# Outputs "purrrrr"
That being said, is there a way to pass the value of noise when calling setattr as above? I figured that I could isolate the namespace by using a function, and that did work:
class Cat(object):
pass
noise = "meow"
def setproperties(cls, k, v):
setattr(cls, k, property(lambda self: v))
setproperties(Cat, "noise", noise)
cat = Cat()
cat.noise
# Outputs "meow"
noise = "purrrrr"
cat.noise
# Still outputs "meow"
Is it possible to do so without passing the object through a function (without using eval or the like)? And as a secondary question, is my reasoning about what goes on under the hood correct?
EDIT
As per the request for a less contrived example in the comments, consider the following. Imagine I am trying to dynamically set attributes in Cat, based on the values of its friend Dog:
class Dog(object):
noise = 'woof'
adorable = True
class Cat(object):
friend = Dog
friend_attrs = filter(lambda attr: not attr.startswith('__'), Dog.__dict__)
for attr in friend_attrs:
setattr(Cat, "friend_" + attr, property(lambda self: getattr(self.friend, attr)))
cat = Cat()
cat.friend_noise
# Outputs True
cat.friend_adorable
# Outputs True