web-dev-qa-db-fra.com

En Python, pourquoi les propriétés ont-elles priorité sur les attributs d'instance?

Cet article décrit comment Python recherche un attribut sur un objet lorsqu'il exécute o.a. L'ordre de priorité est intéressant - il recherche:

  1. Un attribut de classe qui est un descripteur de données (le plus souvent une propriété)
  2. Un attribut d'instance
  3. Tout autre attribut de classe

Nous pouvons le confirmer en utilisant le code ci-dessous, qui crée un objet o avec un attribut d'instance a, dont la classe contient une propriété du même nom:

class C:
    def __init__(self):
        self.__dict__['a'] = 1

    @property
    def a(self):
        return 2

o = C()
print(o.a)  # Prints 2

Pourquoi Python utilise-t-il cet ordre de priorité plutôt que l'ordre "naïf" (les attributs d'instance ont la priorité sur tous les attributs de classe)? L’ordre de priorité de Python présente un inconvénient important: il ralentit la recherche d’attributs, car au lieu de simplement renvoyer un attribut de o s’il existe (cas courant), Python doit d’abord effectuer une recherche dans la classe de o et toutes ses super-classes descripteur.

Quel est l'avantage de l'ordre de priorité de Python? Ce n'est probablement pas juste pour la situation ci-dessus, car avoir une variable d'instance et une propriété du même nom est un cas d'école (notez la nécessité d'utiliser self.__dict__['a'] = 1 pour créer l'attribut d'instance, car le self.a = 1 habituel invoquerait la propriété).

Existe-t-il une situation différente dans laquelle l'ordre de recherche "naïf" poserait un problème?

8
user200783

C'est Guido van Rossum lui-même (l'ex-BDFL de Python) qui a conçu cette fonction lorsque de nouvelles classes de style ont été introduites avec Python 2.2 en 2001. Le raisonnement est présenté dans PEP 252 . L'impact sur la recherche d'attribut est explicitement mentionné:

Ce schéma présente un inconvénient: dans ce que je suppose être le cas le plus courant, référençant une variable d’instance stockée dans le dictionnaire d'instance, il effectue deux recherches dans le dictionnaire, alors que le schéma classique effectuait un test rapide des attributs commençant par deux traits de soulignement plus un seul. recherche dans le dictionnaire.

Et:

Un point de référence vérifie qu'en réalité, cela est aussi rapide que la recherche de variable d'instance classique, je ne suis donc plus inquiet.

4
fpbhb

Pour les classes de style ancien ( PEP 252 ):

L'instance dict remplace la classe dict, à l'exception des attributs spéciaux (tels que __dict__ et __class__), qui ont priorité sur l'instance dict.

Remplacer le __dict__ ou le __class__ dans l'instance dict aurait pour effet de rompre la recherche d'attribut et de provoquer un comportement extrêmement étrange de l'instance.

Dans les classes de style nouveau, Guido a choisi l'implémentation suivante de la recherche d'attribut pour maintenir la cohérence ( PEP 252 ):

  1. Regardez dans le type dict. Si vous trouvez un descripteur de données, utilisez sa méthode get() pour générer le résultat. Ceci prend en charge des attributs spéciaux tels que __dict__ et __class__.
  2. Regardez dans le dict d'instance. Si vous trouvez quelque chose, c'est tout. (Cela répond à l'exigence voulant que le dict d'instance l'emporte sur le dict de classe.)
  3. Recherchez à nouveau le type dict (en réalité, il utilise le résultat enregistré à l'étape 1, bien sûr). Si vous trouvez un descripteur, utilisez sa méthode get (); si vous trouvez autre chose, c'est tout; si ce n'est pas le cas, déclenchez AttributeError.

En résumé, les attributs __dict__ et __class__ sont implémentés en tant que propriétés (descripteurs de données). Pour conserver un état valide, l'instance dict ne peut pas remplacer __dict__ et __class__. Ainsi, les propriétés (descripteurs de données) ont priorité sur les attributs d'instance.

2
Joshua

Ce que je veux savoir, c'est pourquoi les descripteurs de données ont priorité sur attributs d'instance?

S'il n'existe aucune méthode prioritaire par rapport à la recherche d'instance normale, comment comptez-vous intercepter les accès aux attributs d'instance? Ces méthodes ne peuvent pas être des attributs d'instance eux-mêmes car cela irait à l'encontre de leur objectif (du moins sans conventions additionnelles à leur sujet, je pense).

Outre le commentaire concis de @timgeb, je ne peux rien expliquer des descripteurs mieux que le fonctionnaire Descriptor How To

1
progmatico