59 votes

Quelle est la différence entre les variables de classe et les variables d'instance ?

Quelle est la différence entre les variables de classe et les variables d'instance en Python ?

class Complex:
    a = 1

y

class Complex:
    def __init__(self):
        self.a = 1

Utiliser l'appel : x = Complex().a dans les deux cas, attribue à x la valeur 1.

Une réponse plus approfondie sur __init__() y self seront appréciés.

111voto

Ben Points 22160

Lorsque vous écrivez un bloc de classe, vous créez attributs de la classe (ou variables de classe). Tous les noms que vous attribuez dans le bloc de classe, y compris les méthodes que vous définissez à l'aide de la commande def deviennent des attributs de classe.

Une fois qu'une instance de classe est créée, toute personne ayant une référence à l'instance peut créer des attributs d'instance sur celle-ci. Dans les méthodes, l'instance "courante" est presque toujours liée au nom self C'est pourquoi vous les considérez comme des "variables propres". Habituellement, dans la conception orientée objet, le code attaché à une classe est censé avoir le contrôle des attributs des instances de cette classe, de sorte que presque toutes les attributions d'attributs d'instance sont effectuées à l'intérieur des méthodes, en utilisant la référence à l'instance reçue dans l'attribut self de la méthode.

Les attributs des classes sont souvent comparés à statique variables (ou méthodes) comme dans des langages tels que Java, C# ou C++. Toutefois, si vous souhaitez approfondir votre compréhension, j'éviterais de considérer les attributs de classe comme "identiques" aux variables statiques. Bien qu'ils soient souvent utilisés aux mêmes fins, le concept sous-jacent est tout à fait différent. Vous trouverez plus d'informations à ce sujet dans la section "avancée" ci-dessous.

Un exemple !

class SomeClass:
    def __init__(self):
        self.foo = 'I am an instance attribute called foo'
        self.foo_list = []

    bar = 'I am a class attribute called bar'
    bar_list = []

Après l'exécution de ce bloc, il y a une classe SomeClass avec 3 attributs de classe : __init__ , bar y bar_list .

Nous allons ensuite créer une instance :

instance = SomeClass()

Lorsque cela se produit, SomeClass 's __init__ est exécutée, recevant la nouvelle instance dans son fichier self paramètre. Cette méthode crée deux attributs d'instance : foo y foo_list . Cette instance est ensuite affectée au instance de sorte qu'elle est liée à une chose avec ces deux attributs d'instance : foo y foo_list .

Mais.. :

print instance.bar

donne :

I am a class attribute called bar

Comment cela s'est-il produit ? Lorsque nous essayons de récupérer un attribut à l'aide de la syntaxe dot, et que l'attribut n'existe pas, Python passe par un certain nombre d'étapes pour essayer de répondre à votre demande malgré tout. La prochaine chose qu'il essaiera de faire, c'est de regarder le fichier attributs de la classe de la classe de votre instance. Dans ce cas, il a trouvé un attribut bar en SomeClass Il a donc renvoyé cette information.

C'est d'ailleurs ainsi que fonctionnent les appels de méthode. Lorsque vous appelez mylist.append(5) par exemple, mylist n'a pas d'attribut nommé append . Mais le classe de mylist et il est lié à un objet méthode. Cet objet méthode est renvoyé par la fonction mylist.append et ensuite le bit (5) bit appelle la méthode avec l'argument 5 .

L'utilité de ce système réside dans le fait que todos les instances de SomeClass auront accès aux mêmes bar attribut. Nous pourrions créer un million d'instances, mais nous n'avons besoin de stocker qu'une seule chaîne en mémoire, parce qu'elles peuvent toutes la trouver.

Mais il faut être un peu prudent. Jetez un coup d'œil aux opérations suivantes :

sc1 = SomeClass()
sc1.foo_list.append(1)
sc1.bar_list.append(2)

sc2 = SomeClass()
sc2.foo_list.append(10)
sc2.bar_list.append(20)

print sc1.foo_list
print sc1.bar_list

print sc2.foo_list
print sc2.bar_list

Que pensez-vous de ces empreintes ?

[1]
[2, 20]
[10]
[2, 20]

En effet, chaque instance possède sa propre copie de foo_list Ils ont donc été annexés séparément. Mais les todos partagent l'accès au même bar_list . Ainsi, lorsque nous avons sc1.bar_list.append(2) il a affecté sc2 même si sc2 n'existait pas encore ! Et de même sc2.bar_list.append(20) a affecté le bar_list récupéré par l'intermédiaire de sc1 . Ce n'est souvent pas ce que vous souhaitez.


Des études approfondies suivront :)

Pour vraiment comprendre Python, si l'on vient de langages OO traditionnels à typage statique comme Java et C#, il faut apprendre à repenser un peu les classes.

En Java, une classe n'est pas vraiment une chose à part entière. Lorsque vous écrivez une classe, vous déclarez un ensemble de choses que toutes les instances de cette classe ont en commun. Au moment de l'exécution, il n'y a que des instances (et des méthodes/variables statiques, mais il ne s'agit en fait que de variables et de fonctions globales dans un espace de noms associé à une classe, ce qui n'a rien à voir avec l'OO). Les classes sont le moyen d'écrire dans votre code source ce que seront les instances au moment de l'exécution ; elles n'"existent" que dans votre code source, pas dans le programme en cours d'exécution.

En Python, une classe n'a rien de particulier. C'est un objet comme les autres. Ainsi, les "attributs de classe" sont en fait exactement la même chose que les "attributs d'instance" ; en réalité, il n'y a que des "attributs". La seule raison de faire une distinction est que nous avons tendance à utiliser les objets qui sont des classes diffèrent des objets qui ne sont pas des classes. Le mécanisme sous-jacent est le même. C'est pourquoi je dis que ce serait une erreur de considérer les attributs de classe comme des variables statiques d'autres langages.

Mais ce qui différencie vraiment les classes Python des classes de type Java, c'est que, comme tout autre objet, les classes Python sont des objets. chaque classe est une instance d'une autre classe !

En Python, la plupart des classes sont des instances d'une classe intégrée appelée type . C'est cette classe qui contrôle le comportement commun des classes et qui fait que tous les éléments de l'OO sont tels qu'ils sont. La façon OO par défaut d'avoir des instances de classes qui ont leurs propres attributs et dont les méthodes/attributs communs sont définis par leur classe, n'est qu'un protocole en Python. Vous pouvez en modifier la plupart des aspects si vous le souhaitez. Si vous avez déjà entendu parler de l'utilisation d'un métaclasse il s'agit simplement de définir une classe qui est une instance d'une classe différente de la classe type .

La seule chose vraiment "spéciale" à propos des classes (en dehors de toute la machinerie intégrée pour les faire fonctionner comme elles le font par défaut), est la syntaxe du bloc de classe, qui permet de créer plus facilement des instances de type . Ceci :

class Foo(BaseFoo):
    def __init__(self, foo):
        self.foo = foo

    z = 28

est à peu près équivalent à ce qui suit :

def __init__(self, foo):
    self.foo = foo

classdict = {'__init__': __init__, 'z': 28 }

Foo = type('Foo', (BaseFoo,) classdict)

Et il s'arrangera pour que tout le contenu de classdict pour devenir des attributs de l'objet créé.

Il devient alors presque trivial de voir que l'on peut accéder à un attribut de classe par Class.attribute aussi facilement que i = Class(); i.attribute . Les deux i y Class sont des objets, et les objets ont des attributs. Cela permet également de comprendre comment il est possible de modifier une classe après sa création ; il suffit d'attribuer ses attributs de la même manière que pour n'importe quel autre objet !

En effet, les instances n'ont pas de relation particulière avec la classe utilisée pour les créer. La façon dont Python sait dans quelle classe chercher les attributs qui ne se trouvent pas dans l'instance est la fonction cachée __class__ attribut. Vous pouvez lire cet attribut pour savoir de quelle classe il s'agit, comme pour n'importe quel autre attribut : c = some_instance.__class__ . Vous avez maintenant une variable c lié à une classe, même s'il n'a probablement pas le même nom que la classe. Vous pouvez l'utiliser pour accéder aux attributs de la classe, ou même l'appeler pour créer d'autres instances de la classe (même si vous ne savez pas de quelle classe il s'agit !).

Et vous pouvez même assigner à i.__class__ pour changer la classe dont il est une instance ! Si vous faites cela, rien de particulier ne se produit immédiatement. Ce n'est pas bouleversant. Tout ce que cela signifie, c'est que lorsque vous recherchez des attributs qui n'existent pas dans l'instance, Python va aller voir le nouveau contenu de __class__ . Comme cela inclut la plupart des méthodes, et que les méthodes s'attendent généralement à ce que l'instance sur laquelle elles opèrent soit dans certains états, cela entraîne généralement des erreurs si vous le faites au hasard, et c'est très déroutant, mais c'est possible. Si vous êtes très prudent, la chose que vous stockez dans __class__ n'a même pas besoin d'être un objet de classe ; tout ce que Python va faire avec lui est de chercher des attributs dans certaines circonstances, donc tout ce dont vous avez besoin est un objet qui a le bon type d'attributs (quelques mises en garde mises à part où Python est pointilleux sur les choses qui sont des classes ou des instances d'une classe particulière).

C'est probablement suffisant pour l'instant. J'espère (si vous avez lu jusqu'ici) que je ne vous ai pas trop embrouillé. Python est intéressant quand on apprend comment il fonctionne :)

10voto

voithos Points 15066

Ce que vous appelez une variable "d'instance" n'est pas en fait une variable d'instance ; c'est un variable de classe . Voir le référence linguistique sur les classes .

Dans votre exemple, le a semble être une variable d'instance car elle est immuable. Sa nature en tant que classe peut être vu dans le cas où vous assignez un objet mutable :

>>> class Complex:
>>>    a = []
>>>
>>> b = Complex()
>>> c = Complex()
>>> 
>>> # What do they look like?
>>> b.a
[]
>>> c.a
[]
>>> 
>>> # Change b...
>>> b.a.append('Hello')
>>> b.a
['Hello']
>>> # What does c look like?
>>> c.a
['Hello']

Si vous avez utilisé self il s'agirait alors d'une véritable variable d'instance, et chaque instance aurait donc sa propre variable d'instance. a . Les caractéristiques d'un objet __init__ est appelé lorsqu'une nouvelle instance est créée y self est une référence à cette instance.

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