261 votes

Différence entre les variables à l'intérieur et à l'extérieur de __init__() (attributs de classe et d'instance)

Y a-t-il une différence entre ces classes autre que le nom?

class WithClass ():
    def __init__(self):
        self.value = "Bob"
    def my_func(self):
        print(self.value)

class WithoutClass ():
    value = "Bob"

    def my_func(self):
        print(self.value)

Est-ce que cela fait une différence si j'utilise ou non la méthode __init__ pour déclarer la variable value?

Mon principal souci est que je l'utiliserai d'une certaine manière, ce qui pourrait me causer des problèmes par la suite.

1 votes

Une réponse détaillée avec des exemples dans la question dupliquée : stackoverflow.com/a/9056994/911945

315voto

S.Lott Points 207588

Les variables définies en dehors de __init__ appartiennent à la classe. Elles sont partagées par toutes les instances.

Les variables créées à l'intérieur de __init__ (ainsi que toutes les autres fonctions de méthode) et précédées de self. appartiennent à l'instance de l'objet.

3 votes

Ce n'est pas ce que fait Python pour moi. Les listes/dicts/etc sont partagés entre toutes les instances si vous ne les créez pas dans __init__().

5 votes

@trop de php: Toutes les variables au niveau de la méthode de la classe (indépendamment de leur mutabilité -- les listes et les dictionnaires sont mutables) sont partagées. Avec des objets immuables, le partage n'est pas intéressant. Avec des objets mutables (listes et dictionnaires), le partage est significatif.

0 votes

J'ai suspecté que cela pourrait être le cas mais j'ai pensé que si je mentionnais mes hypothèses, cela pourrait détourner l'attention de la question elle-même, merci de l'avoir clarifié :)

136voto

northben Points 1133

Sans Self

Créez quelques objets:

class foo(object):
    x = 'classe d'origine'

c1, c2 = foo(), foo()

Je peux changer l'instance c1, et cela n'affectera pas l'instance c2:

c1.x = 'instance modifiée'
c2.x
>>> 'classe d'origine'

Mais si je change la classe foo, toutes les instances de cette classe seront également modifiées:

foo.x = 'classe modifiée'
c2.x
>>> 'classe modifiée'

Veuillez noter comment fonctionne la portée en Python ici:

c1.x
>>> 'instance modifiée'

Avec Self

Changer la classe n'affecte pas les instances:

class foo(object):
    def __init__(self):
        self.x = 'self d'origine'

c1 = foo()
foo.x = 'classe modifiée'
c1.x
>>> 'self d'origine'

53voto

alejandro Points 456

Je voudrais ajouter quelque chose aux réponses que j'ai lues dans ce fil de discussion et ce fil de discussion (qui fait référence à celui-ci).

Avertissement : ces remarques proviennent des expériences que j'ai menées

Variables à l'extérieur de __init__:

Il s'agit en fait de variables de classe statiques et sont, par conséquent, accessibles à toutes les instances de la classe.

Variables à l'intérieur de __init__:

La valeur de ces variables d'instance est uniquement accessible à l'instance en cours (via la référence self)

Ma contribution:

Une chose que les programmeurs doivent prendre en compte lors de l'utilisation de variables de classe statiques est qu'elles peuvent être masquées par des variables d'instance (si vous accédez aux variables de classe statiques via la référence self).

Explication:

Auparavant, je pensais que les deux façons de déclarer les variables étaient exactement les mêmes (sotte que je suis), en partie parce que je pouvais accéder aux deux types de variables via la référence self. C'est maintenant, lorsque j'ai rencontré des problèmes, que j'ai fait des recherches sur le sujet et l'ai clarifié.

Le problème d'accéder aux variables de classe statiques via la référence self est que cela ne fait référence à la variable de classe statique que s'il n'y a pas de variable d'instance avec le même nom, et pour aggraver les choses, essayer de redéfinir une variable de classe statique via la référence self ne fonctionne pas car une variable d'instance est créée qui masque ensuite la variable de classe statique précédemment accessible.

Pour contourner ce problème, vous devriez toujours faire référence aux variables de classe statiques via le nom de la classe.

Exemple:

#!/usr/bin/env python

class Foo:
    static_var = 'chaque instance a accès'

    def __init__(self,name):
        self.instance_var = 'Je suis %s' % name

    def printAll(self):
        print 'self.instance_var = %s' % self.instance_var
        print 'self.static_var = %s' % self.static_var
        print 'Foo.static_var = %s' % Foo.static_var

f1 = Foo('f1')

f1.printAll()

f1.static_var = 'Masquer static_var'

f1.printAll()

f2 = Foo('f2')

f2.printAll()

Foo.static_var = 'classe modifiée'

f1.printAll()
f2.printAll()

Sortie:

self.instance_var = Je suis f1
self.static_var = chaque instance a accès
Foo.static_var = chaque instance a accès
self.instance_var = Je suis f1
self.static_var = Masquer static_var
Foo.static_var = chaque instance a accès
self.instance_var = Je suis f2
self.static_var = chaque instance a accès
Foo.static_var = chaque instance a accès
self.instance_var = Je suis f1
self.static_var = Masquer static_var
Foo.static_var = classe modifiée
self.instance_var = Je suis f2
self.static_var = classe modifiée
Foo.static_var = classe modifiée

J'espère que cela sera utile à quelqu'un

6voto

Neil Points 124

En réponse à la réponse de S.Lott, les variables de classe sont transmises à la méthode new de la métaclasse et peuvent être accédées via le dictionnaire lorsqu'une métaclasse est définie. Ainsi, les variables de classe peuvent être accédées même avant la création et l'instanciation des classes.

par exemple:

class meta(type):
    def __new__(cls,name,bases,dicto):
          # deux caractères manquants dans l'original de la ligne suivante ...
          if dicto['class_var'] == 'A':
             print 'Il y a'
class proxyclass(object):
      class_var = 'A'
      __metaclass__ = meta
      ...
      ...

3voto

class Utilisateur(object):
    email = 'aucun'
    prenom = 'aucun'
    nom = 'aucun'

    def __init__(self, email=None, prenom=None, nom=None):
        self.email = email
        self.prenom = prenom
        self.nom = nom

    @classmethod
    def afficher_variables(cls, obj):
        print ("obj.email obj.prenom obj.nom")
        print(obj.email, obj.prenom, obj.nom)
        print("cls.email cls.prenom cls.nom")
        print(cls.email, cls.prenom, cls.nom)

u1 = Utilisateur(email='abc@xyz', prenom='first', nom='last')
Utilisateur.afficher_variables(u1)

Dans le code ci-dessus, la classe Utilisateur a 3 variables globales, chacune avec la valeur 'aucun'. u1 est l'objet créé en instanciant cette classe. La méthode afficher_variables affiche la valeur des variables de classe de la classe Utilisateur et des variables d'objet de l'objet u1. Dans la sortie ci-dessous, chacune des variables de classe Utilisateur.email, Utilisateur.prenom et Utilisateur.nom a la valeur 'aucun', tandis que les variables d'objet u1.email, u1.prenom et u1.nom ont les valeurs 'abc@xyz', 'first' et 'last'.

obj.email obj.prenom obj.nom
('abc@xyz', 'first', 'last')
cls.email cls.prenom cls.nom
('aucun', 'aucun', 'aucun')

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X