Creating properties dynamically that point to an attribute's properties

Question:

I want to make properties from an attribute of my class accessible directly through the instance of the class (without inheriting from it). So basically if I have:

class A:
    @property
    def foo(self):
        print("foo")
    @property
    def bar(self):
        print("bar")

class B:
    def __init__(self):
        self._a = A()

Instead of doing b._a.bar I want to be able to do b.bar. Based on this answer here, I tried the following in class B:

class B:
    def __init__(self):
        self._a = A()
        attrs = [attr for attr in dir(self._a) 
                 if not callable(self._a.__getattribute__(attr)) 
                 and not attr.startswith("__")]
        for attr in attrs:
            setattr(self.__class__, attr, 
                    property(lambda s: s._a.__getattribute__(attr)))

But when instantiating and testing it out, I get one of those weird python moments:

>>> b = B()
foo
bar
>>> b.foo
bar
>>> b.bar
bar
  1. Why are both ‘foo’ and ‘bar’ printed out when creating the instance ?
  2. How does the ‘foo’ property point to the same getter as ‘bar’ ?
Asked By: Valentin B.

||

Answers:

bar and foo are printed when you create the instance because doing _a.__getattribute__("foo") and _a.foo will both call the property object to get a value.

Both of the attributes you set up in B use lambdas to get the correct property from A. This is a common mistake when calling lambdas. Because the attr value is inherited from the outside scope, it isn’t frozen when the lambda is evaluated. Instead, it is simply the same attr reference as the enclosing scope’s attr, and changes accordingly. So all of your lambdas will have the same attr value.

You can define a B.__getattr__ method instead. This method is called when ordinary attribute lookup fails.

class B:
    def __init__(self):
        self._a = A()
    def __getattr__(self, name):
        return getattr(self._a, name)

b = B()
b.bar # bar
b.foo # foo
Answered By: Patrick Haugh
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.