J'ai utilisé avec succès les propriétés Python, mais je ne vois pas comment elles pourraient fonctionner. Si je déréférence une propriété en dehors d'une classe, j'obtiens simplement un objet de type property
:
@property
def hello(): return "Hello, world!"
hello # <property object at 0x9870a8>
Mais si je mets une propriété dans une classe, le comportement est très différent:
class Foo(object):
@property
def hello(self): return "Hello, world!"
Foo().hello # 'Hello, world!'
J'ai remarqué que non lié Foo.hello
est toujours l'objet property
, donc l'instanciation de classe doit faire la magie, mais quelle magie est-ce?
Comme d'autres l'ont noté, ils utilisent une fonction de langage appelée descripteurs.
La raison pour laquelle l'objet de propriété réel est renvoyé lorsque vous y accédez via une classe Foo.hello
Réside dans la façon dont la propriété implémente la méthode spéciale __get__(self, instance, owner)
:
owner
est le classe de cette instance.instance
est None et seul owner
est transmis. L'objet property
le reconnaît et renvoie self
.Outre le Descriptors howto , consultez également la documentation sur Implementing Descriptors et Invoking Descriptors dans le Language Guide.
Pour que @properties fonctionne correctement, la classe doit être une sous-classe de object. lorsque la classe n'est pas une sous-classe de objet alors la première fois que vous essayez d'accéder au setter, elle crée en fait un nouvel attribut avec le nom plus court au lieu d'accéder via le setter.
Ce qui suit ne pas fonctionne correctement.
class C(): # <-- Notice that object is missing
def __init__(self):
self._x = None
@property
def x(self):
print 'getting value of x'
return self._x
@x.setter
def x(self, x):
print 'setting value of x'
self._x = x
>>> c = C()
>>> c.x = 1
>>> print c.x, c._x
1 0
Les éléments suivants fonctionneront correctement
class C(object):
def __init__(self):
self._x = None
@property
def x(self):
print 'getting value of x'
return self._x
@x.setter
def x(self, x):
print 'setting value of x'
self._x = x
>>> c = C()
>>> c.x = 1
setting value of x
>>> print c.x, c._x
getting value of x
1 1
Les propriétés sont descripteurs , et les descripteurs se comportent spécialement lorsqu'ils sont membres d'une instance de classe. En bref, si a
est une instance de type A
et que A.foo
Est un descripteur, alors a.foo
Est équivalent à A.foo.__get__(a)
.
L'objet property
implémente simplement le protocole de descripteur: http://docs.python.org/howto/descriptor.html