Можно ли получить ссылку на свойство Python?
Если у меня есть это:
class foo(object):
@property
def bar(self):
return 0
f = foo()
Как получить ссылку на f.bar без фактического вызова метода, если это возможно?
Отредактировано для добавления: то, что я хочу сделать, это написать функцию, которая выполняет итерацию над членами f и что-то делает с ними (что не важно). Свойства меня отключают, потому что простое их имя в getattr() вызывает их метод __get __().
Ответы
Ответ 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
и возвращает это.