D'accord, tout d'abord.
Il n'y a pas de "déclaration de variable" ou "d'initialisation de variable" en Python.
Il y a simplement ce que nous appelons «affectation», mais nous devrions probablement simplement appeler «nommer».
L'affectation signifie "ce nom sur le côté gauche se réfère maintenant au résultat de l'évaluation du côté droit, indépendamment de ce à quoi il faisait référence auparavant (le cas échéant)".
foo = 'bar'
foo = 2 * 3
En tant que tels, les noms de Python (un meilleur terme que «variables», sans doute) n'ont pas de types associés; les valeurs font. Vous pouvez réappliquer le même nom à n'importe quoi quel que soit son type, mais l'objet a toujours un comportement qui dépend de son type. Le nom est simplement un moyen de faire référence à la valeur (objet). Cela répond à votre deuxième question: vous ne créez pas de variables pour contenir un type personnalisé. Vous ne créez pas de variables pour contenir un type particulier. Vous ne "créez" pas du tout de variables. Vous donnez des noms aux objets.
Deuxième point: Python suit une règle très simple en ce qui concerne les classes, qui est en fait beaucoup plus cohérente que ce que font des langages comme Java, C ++ et C #: tout ce qui est déclaré à l'intérieur du class
bloc fait partie de la classe . Ainsi, les fonctions ( def
) écrites ici sont des méthodes, c'est-à-dire une partie de l'objet de classe (non stocké par instance), tout comme en Java, C ++ et C #; mais d'autres noms ici font également partie de la classe. Encore une fois, les noms ne sont que des noms, et ils n'ont pas de types associés, et les fonctions sont également des objets en Python. Donc:
class Example:
data = 42
def method(self): pass
Les classes sont aussi des objets , en Python.
Alors maintenant, nous avons créé un objet nommé Example
, qui représente la classe de toutes les choses qui sont Example
s. Cet objet a deux attributs fournis par l'utilisateur (en C ++, «membres»; en C #, «champs ou propriétés ou méthodes»; en Java, «champs ou méthodes»). L'un d'eux est nommé data
et stocke la valeur entière 42
. L'autre est nommé method
et stocke un objet fonction. (Il y a plusieurs autres attributs que Python ajoute automatiquement.)
Cependant, ces attributs ne font toujours pas vraiment partie de l'objet. Fondamentalement, un objet est juste un ensemble de noms supplémentaires (les noms d'attributs), jusqu'à ce que vous arriviez à des choses qui ne peuvent plus être divisées. Ainsi, les valeurs peuvent être partagées entre différentes instances d'une classe, ou même entre des objets de différentes classes, si vous le configurez délibérément.
Créons une instance:
x = Example()
Nous avons maintenant un objet distinct nommé x
, qui est une instance de Example
. Les data
et method
ne font pas réellement partie de l'objet, mais nous pouvons toujours les rechercher via à x
cause de la magie que Python fait dans les coulisses. Lorsque nous recherchons method
, en particulier, nous obtiendrons à la place une "méthode liée" (lorsque nous l'appelons, elle x
est passée automatiquement en self
paramètre, ce qui ne peut pas arriver si nous recherchons Example.method
directement).
Que se passe-t-il lorsque nous essayons d'utiliser x.data
?
Lorsque nous l'examinons, il est d'abord recherché dans l'objet. S'il ne se trouve pas dans l'objet, Python regarde dans la classe.
Cependant, lorsque nous attribuons à x.data
, Python créera un attribut sur l'objet. Il ne remplacera pas l'attribut de la classe.
Cela nous permet de faire l' initialisation des objets . Python appellera automatiquement la __init__
méthode de la classe sur les nouvelles instances lors de leur création, le cas échéant. Dans cette méthode, nous pouvons simplement assigner des attributs pour définir les valeurs initiales de cet attribut sur chaque objet:
class Example:
name = "Ignored"
def __init__(self, name):
self.name = name
Maintenant, nous devons spécifier a name
lorsque nous créons un Example
, et chaque instance a la sienne name
. Python ignorera l'attribut de classe Example.name
chaque fois que nous recherchons .name
une instance, car l'attribut de l'instance sera trouvé en premier.
Une dernière mise en garde: la modification (mutation) et l'assignation sont des choses différentes!
En Python, les chaînes sont immuables. Ils ne peuvent pas être modifiés. Quand vous faites:
a = 'hi '
b = a
a += 'mom'
Vous ne modifiez pas la chaîne «hi» d'origine. C'est impossible en Python. Au lieu de cela, vous créez une nouvelle chaîne 'hi mom'
et faites a
cesser d'être un nom pour 'hi '
, et commencez à être un nom à la 'hi mom'
place. Nous avons fait b
un nom pour 'hi '
ainsi, et après avoir réappliqué le a
nom, il b
est toujours un nom pour 'hi '
, car 'hi '
il existe toujours et n'a pas été changé.
Mais les listes peuvent être modifiées:
a = [1, 2, 3]
b = a
a += [4]
Maintenant, b
c'est [1, 2, 3, 4] aussi, parce que nous avons fait b
un nom pour la même chose qui a
nommait, puis nous avons changé cette chose. Nous n'avons pas créé de nouvelle liste pour a
to name, car Python traite simplement +=
différemment les listes.
Ceci est important pour les objets car si vous aviez une liste comme attribut de classe et que vous utilisiez une instance pour modifier la liste, alors le changement serait "vu" dans toutes les autres instances. Cela est dû au fait que (a) les données font en fait partie de l'objet de classe, et non d'un objet d'instance; (b) parce que vous modifiiez la liste et ne faisiez pas une simple affectation, vous n'avez pas créé un nouvel attribut d'instance masquant l'attribut de classe.