Y a-t-il une distinction significative entre:
class A(object):
foo = 5 # some default value
contre.
class B(object):
def __init__(self, foo=5):
self.foo = foo
Si vous créez de nombreuses instances, y a-t-il une différence dans les performances ou les exigences d'espace pour les deux styles? Lorsque vous lisez le code, considérez-vous que la signification des deux styles est significativement différente?
python
attributes
member-variables
Dan Homerick
la source
la source
Réponses:
Au-delà des considérations de performances, il existe une différence sémantique significative . Dans le cas de l'attribut de classe, il n'y a qu'un seul objet auquel il est fait référence. Dans l'instance-attribut-ensemble-à-instanciation, il peut y avoir plusieurs objets référencés. Par exemple
la source
int
etstr
ils sont toujours attachés à chaque instance plutôt qu'à la classe.int
etstr
sont également partagés, exactement de la même manière. Vous pouvez vérifier cela facilement avecis
ouid
. Ou regardez simplement dans chaque instance__dict__
et la classe__dict__
. Le fait que les types immuables soient partagés ou non n'a généralement pas beaucoup d'importance.a.foo = 5
, dans les deux cas, vous verrez leb.foo
retour[]
. En effet, dans le premier cas, vous écrasez l'attribut de classea.foo
par un nouvel attribut d'instance du même nom.La différence est que l'attribut de la classe est partagé par toutes les instances. L'attribut sur une instance est unique à cette instance.
S'ils proviennent de C ++, les attributs de la classe ressemblent plus à des variables membres statiques.
la source
>>> class A(object): foo = 5
>>> a, b = A(), A()
>>> a.foo = 10
>>> b.foo
5
a.foo.append(5)
fait muter la valeur à laquelle sea.foo
réfère,a.foo = 5
est en train de créera.foo
un nouveau nom pour la valeur5
. Ainsi, vous vous retrouvez avec un attribut d'instance qui cache l'attribut de classe. Essayez la même chosea.foo = 5
dans la version d'Alex et vous verrez que celab.foo
n'a pas changé.Voici un très bon article et résumez-le comme ci-dessous.
Et sous forme visuelle
Attribution d'attributs de classe
Si un attribut de classe est défini en accédant à la classe, il remplacera la valeur de toutes les instances
Si une variable de classe est définie en accédant à une instance, elle remplacera la valeur uniquement pour cette instance . Cela remplace essentiellement la variable de classe et la transforme en une variable d'instance disponible, intuitivement, uniquement pour cette instance .
Quand utiliseriez-vous l'attribut de classe?
Stockage des constantes . Comme les attributs de classe sont accessibles en tant qu'attributs de la classe elle-même, il est souvent agréable de les utiliser pour stocker des constantes spécifiques à la classe à l'échelle de la classe
Définition des valeurs par défaut . À titre d'exemple trivial, nous pourrions créer une liste bornée (c'est-à-dire une liste qui ne peut contenir qu'un certain nombre d'éléments ou moins) et choisir d'avoir une limite par défaut de 10 éléments
la source
Puisque les personnes dans les commentaires ici et dans deux autres questions marquées comme dups semblent toutes confuses à ce sujet de la même manière, je pense qu'il vaut la peine d'ajouter une réponse supplémentaire en plus de celle d' Alex Coventry .
Le fait qu'Alex attribue une valeur de type mutable, comme une liste, n'a rien à voir avec le fait que les choses soient partagées ou non. Nous pouvons le voir avec la
id
fonction ou l'is
opérateur:(Si vous vous demandez pourquoi j'ai utilisé
object()
au lieu de, disons,5
c'est pour éviter de rencontrer deux autres problèmes que je ne veux pas aborder ici; pour deux raisons différentes, des s entièrement créés séparément5
peuvent finir par être le même instance du nombre5
. Mais lesobject()
s créés entièrement séparément ne le peuvent pas.)Alors, pourquoi est-ce que
a.foo.append(5)
dans l'exemple d'Alex affecteb.foo
, mais pasa.foo = 5
dans mon exemple? Eh bien, essayeza.foo = 5
l'exemple d'Alex et notez que cela n'affecte pasb.foo
là non plus .a.foo = 5
fait justea.foo
un nom pour5
. Cela n'affecte pasb.foo
, ou tout autre nom pour l'ancienne valeur quia.foo
faisait référence. * C'est un peu délicat que nous créons un attribut d'instance qui cache un attribut de classe, ** mais une fois que vous obtenez cela, rien de compliqué n'est se passe ici.Espérons que la raison pour laquelle Alex a utilisé une liste est maintenant évidente: le fait que vous puissiez muter une liste signifie qu'il est plus facile de montrer que deux variables nomment la même liste, et signifie également qu'il est plus important dans le code réel de savoir si vous avez deux listes ou deux noms pour la même liste.
* La confusion pour les personnes venant d'un langage comme C ++ est qu'en Python, les valeurs ne sont pas stockées dans des variables. Les valeurs vivent seules dans le domaine des valeurs, les variables ne sont que des noms de valeurs et l'affectation crée simplement un nouveau nom pour une valeur. Si cela vous aide, considérez chaque variable Python comme un
shared_ptr<T>
fichier au lieu d'unT
.** Certaines personnes en profitent en utilisant un attribut de classe comme "valeur par défaut" pour un attribut d'instance que les instances peuvent ou non définir. Cela peut être utile dans certains cas, mais cela peut aussi prêter à confusion, alors faites attention.
la source
Il y a une autre situation.
Les attributs de classe et d'instance sont Descriptor .
Ci-dessus affichera:
Le même type d'accès à l'instance via une classe ou une instance renvoie un résultat différent!
Et j'ai trouvé dans la définition de c.PyObject_GenericGetAttr, et un excellent post .
Explique
la source