Si j'ai ceci:
class foo(object):
@property
def bar(self):
return 0
f = foo()
Comment obtenir une référence à f.bar sans invoquer réellement la méthode, si cela est même possible?
Édité pour ajouter: Ce que je veux faire, c’est écrire une fonction qui itère sur les membres de f et fait quelque chose avec eux (ce qui n’a pas d’importance). Les propriétés me font trébucher parce que les nommer simplement dans getattr () appelle leur méthode __get __ ().
get_dict_attr
(ci-dessous) recherche attr
dans le __dict__
d'un objet donné et renvoie la valeur associée, le cas échéant. Si attr
n'est pas une clé dans ce __dict__
, la recherche de __dict__
s de l'objet MRO est effectuée. Si la clé n'est pas trouvée, une AttributeError
est générée.
def get_dict_attr(obj, attr):
for obj in [obj] + obj.__class__.mro():
if attr in obj.__dict__:
return obj.__dict__[attr]
raise AttributeError
Par exemple,
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
Notez que ceci est très différent des règles normales de recherche d'attribut. D'une part, les descripteurs de données dans obj.__class__.__dict__
(les descripteurs avec les deux méthodes __get__
et __set__
) ont normalement priorité sur les valeurs dans obj.__dict__
. Dans get_dict_attr
, obj.__dict__
a la priorité.
get_dict_attr
n'essaie pas d'appeler __getattr__
.
Enfin, get_dict_attr
fonctionnera uniquement avec les objets obj
qui sont des instances de classes de style nouveau.
Néanmoins, j'espère que cela vous aidera.
class Foo(object):
@property
def bar(self):
return 0
f = Foo()
Ceci fait référence à la propriété bar
:
print(Foo.bar)
# <property object at 0xb76d1d9c>
bar
est une clé dans Foo.__dict__
:
print(Foo.__dict__['bar'])
# <property object at 0xb775dbbc>
Toutes les propriétés sont des descripteurs, ce qui implique une méthode __get__
:
print(Foo.bar.__get__)
# <method-wrapper '__get__' of property object at 0xb76d7d74>
Vous pouvez appeler la méthode en passant l'objet f
et la classe f
en tant qu'arguments:
print(Foo.bar.__get__(f,Foo))
# 0
J'aime le schéma suivant. Les lignes verticales indiquent la relation entre un objet et la classe de l'objet.
Quand vous avez cette situation:
Foo B
| Foo.__dict__={'bar':b} | B.__dict__={'__get__':...}
| \ |
f `--------> b
f.bar
provoque l'appel de b.__get__(f,Foo)
.
Ceci est expliqué en détail ici .
J'ai rencontré une situation similaire à celle-ci et aucune des autres réponses ne m'a aidé. Voici donc une autre approche.
Ma situation était légèrement différente, car au lieu de simplement avoir un @property
, je mélangeais un décorateur personnalisé qui ajoute des attributs à la méthode encapsulée. Donc, par exemple, normalement, j'aurais quelque chose comme ceci:
class Foo:
@my_decorator
def bar(self):
return do_stuff() # Pretend this is non-trivial
Mon @my_decorator
me donnerait alors accès à self.bar.my_attribute
qui contenait certaines données auxquelles je devais parfois avoir accès. Cela fonctionne très bien pour les fonctions et les méthodes, mais j’ai eu du mal à le mélanger avec @property
car la propriété masque la référence à la méthode (self.bar.my_attribute
échoue car self.bar
renvoie la valeur de retour de la méthode, qui ne possède pas le my_attribute
qui Je voudrais).
J'utiliserais @property
comme décorateur extérieur comme ceci:
class Foo:
@property
@my_decorator
def bar(self):
return do_stuff() # Pretend this is non-trivial
Et ensuite, la solution consiste à accéder à my_attribute
en tant que Foo.bar.fget.my_attribute
(le détail important ici est que l'accès à la propriété depuis la classe renvoie l'objet property, alors qu'accéder à la propriété depuis une instance appelle simplement la méthode et renvoie la valeur, sans accès à la référence à la méthode originale).
TL; DR: Si vous avez besoin d'une référence à la méthode fournissant votre @property
, vous devez utiliser YourClassName.your_property.fget
.
Dans ton cas,
class foo(object):
@property
def bar(self):
return 0
f = foo()
Vous pouvez accéder à la propriété à travers le dictionnaire de la classe:
f.__class__.__dict__['bar']
=> <property at 0x4920c28>
Comme vous pouvez le constater, il s’agit d’une instance property
.
isinstance(f.__class__.__dict__['bar'], property)
=> True
Vous pouvez accéder à sa méthode getter (qui return 0
) à fget
comme ceci:
f.__class__.__dict__['bar'].fget(f)
=> 0
Notez que vous devez fournir l'instance f
à fget()
car il s'agit d'une méthode d'instance (non statique) et vous y accédez via le __class__
.
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))
Vous devez savoir que les descripteurs de données (propriétés) ne fonctionnent que s’ils sont appliqués à des classes (nouveau style) (voir http://docs.python.org/reference/datamodel.html#implementing-descriptors ). Les copier dans un objet ne créera pas la propriété sur cet objet. Vous devez les copier dans une classe (nouveau style) pour prendre effet.