web-dev-qa-db-fra.com

Comment fonctionnent les propriétés Python?

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?

70
Fred Foo

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):

  • Si un descripteur est accessible sur une instance , cette instance est transmise comme argument approprié et owner est le classe de cette instance.
  • Lorsqu'il est accessible via la classe, 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.

48
Tim Yates

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
22
Timothy Vann

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) .

11
Sven Marnach

L'objet property implémente simplement le protocole de descripteur: http://docs.python.org/howto/descriptor.html

2
Achim