Le trait de soulignement unique en Python "_"
est utilisé pour rendre une variable différente d'un mot-clé Python comme dans float_=8
, ou pour indiquer qu'il doit être utilisé dans un contexte privé comme dans _var=8
.
>>> _str = 'hello' >>> _str 'hello'
Le double trait de soulignement en Python "__"
(appelé “"__init__"
, cela indique qu'il s'agit d'une méthode spéciale en Python appelée "méthode magique" .
>>> class Wizard: def __init__(self): self.mana = 100 >>> harry = Wizard() >>> harry.mana 100
Dans cet article et la vidéo bonus, vous apprendrez les tenants et les aboutissants du trait de soulignement en Python.
Les définitions de classe peuvent être intimidantes. Parfois, si vous lisez les exemples de code simplifiés des didacticiels Python, vous pouvez croire que vous obtenez le didacticiel. Mais écrire et lire du code dans le monde réel peut devenir moche très rapidement. Par exemple, regardez l'extrait de code suivant d'un didacticiel Python en ligne :
class Length: __metric = {"mm" : 0.001, "cm" : 0.01, "m" : 1, "km" : 1000, "in" : 0.0254, "ft" : 0.3048, "yd" : 0.9144, "mi" : 1609.344 } def __init__(self, value, unit = "m" ): self.value = value self.unit = unit def Converse2Metres(self): return self.value * Length.__metric[self.unit] def __add__(self, other): l = self.Converse2Metres() + other.Converse2Metres() return Length(l / Length.__metric[self.unit], self.unit ) def __str__(self): return str(self.Converse2Metres()) def __repr__(self): return "Length(" + str(self.value) + ", '" + self.unit + "')" if __name__ == "__main__": x = Length(4) print(x) y = eval(repr(x)) z = Length(4.5, "yd") + Length(1) print(repr(z)) print(z)
Si vous n'avez pas déjà investi du temps, des efforts et de l'argent dans une formation Python performante, vous pouvez vous demander :Quel est le problème avec ce code ?
Après vous être calmé, vous vous rendrez peut-être compte que la principale chose que vous n'obtenez pas est l'utilisation du "trait de soulignement" en Python. Dans l'extrait de code, les traits de soulignement semblent être partout !
Ce qui est triste, c'est que si vous ne comprenez pas la signification du trait de soulignement, vous ne pourrez jamais suivre des discussions Python approfondies, vous ne comprendrez pas beaucoup de discussions Python, sans parler des bases de code des autres.
Il est vital pour votre carrière - et pour votre capacité à communiquer en Python - que vous étudiiez à fond ces connaissances apparemment infimes.
Quelle est la signification du trait de soulignement dans Python Object Orientation ?
Le trait de soulignement unique n'a, en fait, aucune signification particulière.
- Vous pouvez utiliser le trait de soulignement pour séparer des mots comme dans :
this_is_a_long_variable = 42
. - Si vous commencez un nom d'un attribut d'instance avec le trait de soulignement comme dans
_var = 8
, vous indiquez que cet attribut d'instance est censé être "privé" et qu'il ne doit pas être accessible depuis l'extérieur de la classe. Cependant, cela reste possible, comme vous pouvez le voir dans l'extrait de code suivant :
class Wizard: # underscore = visual separator studied_at = "Hogwarts" # underscore = "please keep me private" _wizards = [] # discouraged but possible: print(Wizard._wizards) # [] print(Wizard.studied_at) # Hogwarts
Le double trait de soulignement (également appelé "dunder" par les pros de Python) a en revanche une signification particulière.
Il y a deux cas :
- Dunders en tête :
__var
- englobant les dunders :
__init__
Leader Dunders
Lors du démarrage d'un nom avec
Voici un exemple d'Harry Potter qui montre la nature privée d'un chef de file :
class Wizard: # underscore = visual separator studied_at = "Hogwarts" # underscore = "please keep me private" _wizards = [] # enclosing dunder = magic methods def __init__(self, mana): self.mana = mana Wizard._wizards.append(self) # trailing underscore = overwrite keyword self.key_ = True # leading dunder = "enforce to keep me private" self.__supersecretphrase = "wingardium leviosa" def secret_trick(self): return self.__supersecretphrase tom = Wizard(100) print(tom.__supersecretphrase) # AttributeError: 'Wizard' object has no attribute '__supersecretphrase' print(tom.secret_trick()) # wingardium leviosa
Vous ne pouvez pas accéder à l'attribut d'instance __supersecretphrase
de l'extérieur de la classe ! L'interpréteur Python générera une erreur si vous essayez de le faire de manière aussi directe. Mais vous pouvez le faire en appelant la méthode non privée secret_trick()
qui accède à l'attribut d'instance privé __supersecretphrase
depuis la définition de classe.
Maintenant, vous pouvez demander :
Pour quelle raison protège-t-on les noms de cette manière ?
Le paradigme de la programmation orientée objet découle de l'idée d'« encapsulation ». Chaque objet encapsule des données (les attributs d'instance) et des méthodes pour accéder aux données (les méthodes d'instance). Le point de vue le plus extrême consiste à interdire complètement la modification des attributs de l'instance depuis l'extérieur. Cela conduit à une sémantique très claire (quel est l'effet de vos objets) et cache la complexité à l'utilisateur de votre classe.
Enfermer les Dunders
Lorsque vous entourez un nom de méthode avec __init__
, vous indiquez qu'il s'agit d'une méthode spéciale. Les pros l'appellent en fait "méthode magique" en Python - un terme qui convient très bien à notre exemple ;).
Vous avez probablement déjà utilisé le spécial __init_
_ méthode (le constructeur) assez lourdement pour créer de nouvelles instances à partir d'une description de classe.
Mais il existe aussi de nombreuses autres méthodes spéciales. Un exemple est le __str__
méthode qui vous permet de créer une nouvelle représentation textuelle de votre objet.
Voici un exemple qui montre comment la classe Wizard2
remplace la représentation sous forme de chaîne par défaut à l'aide de la méthode dunder englobante __str__
:
class Wizard1: def __init__(self, mana): self.mana = mana class Wizard2(Wizard1): def __str__(self): return "Wizard's Mana Level: " + str(self.mana) tom = Wizard1(99) print(tom) # <__main__.Wizard1 object at 0x000001FEFF2ACA90> harry = Wizard2(101) print(harry) # Wizard's Mana Level: 101 The class Wizard1 is the top-level class here. It defines the constructor using the magic method __init__.
La classe Wizard1
est la classe de niveau supérieur ici. Il définit le constructeur en utilisant la méthode magique __init__
.
La classe Wizard2
est une classe qui hérite de la classe de niveau supérieur (vous pouvez en savoir plus sur l'héritage ici). En d'autres termes, Wizard2
"hérite" de toutes les méthodes et attributs de la classe parente Wizard1
.
Mais en plus de cela, Wizard2
définit également le __str__
méthode qui renvoie une représentation textuelle de l'instance courante sur laquelle elle est appelée.
Lors de l'impression du Wizard1
instance (par exemple tom
), la représentation textuelle par défaut est vraiment moche. Il ne vous donne que le code hexadécimal de l'objet — pas vraiment utile pour comprendre l'instance. Mais lors de l'impression du Wizard2
instance (par exemple harry
), Python appellera implicitement votre __str__
défini méthode et renvoie la représentation textuelle telle que définie par vous.
Il existe de nombreuses autres méthodes magiques. Par exemple, vous pouvez remplacer le comportement par défaut pour l'addition, la soustraction, la multiplication et la division :
class Wizard: def __init__(self, mana): self.mana = mana def __add__(self, other): return Wizard(self.mana + other.mana) tom = Wizard(99) harry = Wizard(101) print((tom+harry).mana) # 200
Dans l'exemple, l'addition de deux assistants crée un nouvel assistant qui a l'additif mana
des deux assistants.