Динамическое создание свойств, которые указывают на свойства атрибута - PullRequest
0 голосов
/ 02 июля 2018

Я хочу, чтобы свойства из атрибута моего класса были доступны напрямую через экземпляр класса (без наследования от него). Так что в основном, если у меня есть:

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

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

Вместо того, чтобы делать b._a.bar Я хочу иметь возможность сделать b.bar. Основываясь на этом ответе здесь , я попробовал следующее в классе 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)))

Но когда я создаю и проверяю его, я получаю один из странных моментов Python:

>>> b = B()
foo
bar
>>> b.foo
bar
>>> b.bar
bar
  1. Почему при создании экземпляра распечатываются как 'foo', так и 'bar'?
  2. Как свойство 'foo' указывает на тот же метод получения, что и 'bar'?

1 Ответ

0 голосов
/ 02 июля 2018

bar и foo печатаются при создании экземпляра, потому что выполнение _a.__getattribue__("foo") и _a.foo вызовет объект свойства для получения значения.

Оба атрибута, которые вы задали в B, используют лямбда-выражения для получения правильных property из A. Это распространенная ошибка при вызове лямбд. Поскольку значение attr унаследовано от внешней области, оно не останавливается при вычислении лямбда-выражения. Вместо этого это просто та же самая ссылка attr, что и в attr включенной области видимости, и изменяется соответствующим образом. Таким образом, все ваши лямбды будут иметь одинаковое значение attr.

Вместо этого вы можете определить метод B.__getattr__. Этот метод вызывается при сбое обычного поиска атрибутов.

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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...