Подтвердить что ты не робот

Можно ли получить ссылку на свойство Python?

Если у меня есть это:

class foo(object):
    @property
    def bar(self):
        return 0

f = foo()

Как получить ссылку на f.bar без фактического вызова метода, если это возможно?

Отредактировано для добавления: то, что я хочу сделать, это написать функцию, которая выполняет итерацию над членами f и что-то делает с ними (что не важно). Свойства меня отключают, потому что простое их имя в getattr() вызывает их метод __get __().

4b9b3361

Ответ 1

get_dict_attr (ниже) просматривает attr в заданном объекте __dict__ и возвращает связанное значение, если оно есть. Если attr не является ключом в этом __dict__, поиск объекта MRO __dict__ выполняется. Если ключ не найден, повышается AttributeError.

def get_dict_attr(obj, attr):
    for obj in [obj] + obj.__class__.mro():
        if attr in obj.__dict__:
            return obj.__dict__[attr]
    raise AttributeError

Например,

class Foo(object):
    x=1
    def bar(self):
        pass
    @property
    def baz(self):
        return 0

foo=Foo()
print(get_dict_attr(foo,'x'))
# 1
print(get_dict_attr(foo,'bar'))
# <unbound method Foo.bar>
print(get_dict_attr(foo,'baz'))
# <property object at 0xb77c0dc4>
print(get_dict_attr(foo,'y'))
# AttributeError

Обратите внимание, что это очень отличается от обычных правил поиска атрибутов. Во-первых, дескрипторы данных в obj.__class__.__dict__ (дескрипторы с методами __get__ и __set__) обычно имеют приоритет над значениями в obj.__dict__. В get_dict_attr, obj.__dict__ имеет приоритет.

get_dict_attr не пытается вызвать __getattr__.

Наконец, get_dict_attr будет работать только с объектами obj, которые являются экземплярами классов нового стиля.

Тем не менее, я надеюсь, что это поможет.


class Foo(object):
    @property
    def bar(self):
        return 0

f = Foo()

Это ссылается на свойство bar:

print(Foo.bar)
# <property object at 0xb76d1d9c>

Вы видите, что bar является ключом в Foo.__dict__:

print(Foo.__dict__['bar'])
# <property object at 0xb775dbbc>

Все свойства являются дескрипторами, что означает, что он имеет метод __get__:

print(Foo.bar.__get__)
# <method-wrapper '__get__' of property object at 0xb76d7d74>

Вы можете вызвать метод, передав объект f, а класс f в качестве аргументов:

print(Foo.bar.__get__(f,Foo))
# 0

Мне нравится следующая диаграмма. Вертикальные линии показывают взаимосвязь между объектом и классом объекта.

Если у вас есть такая ситуация:

   Foo                                B
   | Foo.__dict__={'bar':b}           | B.__dict__={'__get__':...}
   |                      \           |      
   f                       `--------> b

f.bar вызывает вызов b.__get__(f,Foo).

Это подробно объясняется здесь.

Ответ 2

В вашем случае

class foo(object):
    @property
    def bar(self):
        return 0
f = foo()

Вы можете получить доступ к свойству через словарь класса:

f.__class__.__dict__['bar']
=> <property at 0x4920c28>

Как вы можете видеть, это экземпляр property.

isinstance(f.__class__.__dict__['bar'], property)
=> True

Вы можете получить доступ к его методу getter (который делает return 0) через fget следующим образом:

f.__class__.__dict__['bar'].fget(f)
=> 0

Обратите внимание, что вам нужно предоставить экземпляр f в fget(), потому что это метод экземпляра (не статический), и вы получаете доступ к нему через __class__.

Ответ 3

Я столкнулся с подобной ситуацией, и ни один из других ответов не помог мне, поэтому здесь альтернативный подход.

Моя ситуация была немного иной, так как вместо того, чтобы просто иметь @property, я смешивал в пользовательском декораторе, который добавляет атрибуты к завернутому методу. Так, например, обычно у меня было бы что-то вроде этого:

class Foo:
    @my_decorator
    def bar(self):
        return do_stuff()  # Pretend this is non-trivial

Мой @my_decorator затем предоставит мне доступ к self.bar.my_attribute, у которого есть некоторые данные, которые мне нужно получить иногда. Это отлично работает для функций и методов, но я ударяю обломок при попытке смешать его с @property, потому что свойство скрывает ссылку на метод (self.bar.my_attribute сбой, потому что self.bar возвращает возвращаемое значение метода, которое doesn 't есть my_attribute, который я хочу).

Я бы использовал @property в качестве внешнего декоратора:

class Foo:
    @property
    @my_decorator
    def bar(self):
        return do_stuff()  # Pretend this is non-trivial

И тогда решение должно получить доступ к my_attribute как Foo.bar.fget.my_attribute (важная деталь здесь заключается в том, что доступ к свойству из класса возвращает объект свойства, тогда как доступ к свойству из экземпляра просто вызывает метод и возвращает значение, без доступа к ссылке на исходный метод).

TL; DR: Если вам нужна ссылка на метод, который предоставляет ваш @property, вам нужно использовать YourClassName.your_property.fget.

Ответ 4

Одна вещь, которую вы должны знать, это то, что дескрипторы данных (т.е. свойства) работают только тогда, когда они применяются к классам (новый стиль) (см. http://docs.python.org/reference/datamodel.html#implementing-descriptors). Копирование их в объект не создаст свойства этого объекта. Вам необходимо скопировать их в класс (новый стиль), чтобы вступить в силу.

Ответ 5

class A:
    prop = property(lambda self: 'get', lambda self, x: print('set', x))

setter = A.prop.fset
getter = A.prop.fget


my_a = A()

setter(my_a, 5)
print(getter(my_a))

Ответ 6

Я собираюсь сказать игнорировать другие ответы, потому что они плохие.

Вы можете получить ссылку на свойство просто foo.bar

Что касается того, что вы пытаетесь выполнить итерации по элементам f, см. ниже подробные сведения.

Длинный ответ: вы должны понимать, что методы в Python не принадлежат экземплярам, ​​но являются атрибутами объекта класса. Например,

class Foo:
    def bar(self):
        pass
foo = Foo()

если вы вызываете foo.bar(), Python сначала проверяет, имеет ли foo bar (это не так), тогда он проверяет, есть ли foo bar, что он делает. Затем Python "связывает" foo.bar с Foo.bar(foo) и вызывает это.

То же самое верно для свойств. Я не знаю точного процесса (и, я думаю, он отличается от реализаций), но по существу Python ищет foo.bar, а затем foo.bar, видит, что он является свойством, поэтому оценивает его getter и возвращает это.