Pourquoi certaines fonctions ont-elles un trait de soulignement «__» avant et après le nom de la fonction?


425

Ce "soulignement" semble se produire beaucoup, et je me demandais si c'était une exigence dans le langage Python, ou simplement une question de convention?

De plus, quelqu'un pourrait-il nommer et expliquer quelles fonctions ont tendance à être soulignées et pourquoi ( __init__par exemple)?


8
@AustinHenley: Pas pour les doubles soulignés avant et après le nom. Vous pensez aux traits de soulignement uniquement avant le nom.



@MackM Notez que cette question porte sur les traits de soulignement avant et après le nom, et que la cible en double que vous avez proposée ne demande des traits de soulignement qu'avant le nom. Cependant, j'admets que certaines des réponses y couvrent également ce cas.
Georgy

Réponses:


527

À partir de Python PEP 8 - Guide de style pour le code Python :

Descriptif: dénomination des styles

Les formes spéciales suivantes utilisant des soulignements de début ou de fin sont reconnues (elles peuvent généralement être combinées avec n'importe quelle convention de cas):

  • _single_leading_underscore: indicateur "usage interne" faible. Par exemple, from M import *n'importe pas les objets dont le nom commence par un trait de soulignement.

  • single_trailing_underscore_: utilisé par convention pour éviter les conflits avec le mot clé Python, par exemple

    Tkinter.Toplevel(master, class_='ClassName')

  • __double_leading_underscore: lorsque vous nommez un attribut de classe, invoque la modification de nom (à l'intérieur de la classe FooBar, __boodevient _FooBar__boo; voir ci-dessous).

  • __double_leading_and_trailing_underscore__: objets ou attributs "magiques" qui vivent dans des espaces de noms contrôlés par l'utilisateur. Par exemple __init__, __import__ou __file__. N'inventez jamais de tels noms; utilisez-les uniquement comme indiqué.

Notez que les noms avec des soulignements doubles en tête et en fin sont essentiellement réservés à Python lui-même: "N'inventez jamais de tels noms; utilisez-les uniquement comme indiqué".


6
Raymond explique également pourquoi vous voudriez que le comportement de changement de nom commence à environ 34 minutes dans cette vidéo: youtube.com/watch?v=HTLu2DFOdTg
johncip

5
Donc, le choix entre le trait de soulignement simple et le trait de soulignement double dans un nom est un peu comme choisir entre protégé et privé en C ++ et Java? _single_leading_underscore peut être modifié par les enfants, mais __double_leading_underscore ne peut pas?
Alex W

2
__double_leading_underscoreest toujours public , la variable est simplement renommée pour éviter un conflit.
cz

59

Les autres répondants ont raison de décrire les doubles soulignements de début et de fin comme une convention de dénomination pour les méthodes "spéciales" ou "magiques".

Bien que vous puissiez appeler ces méthodes directement ( [10, 20].__len__()par exemple), la présence des traits de soulignement indique que ces méthodes sont destinées à être invoquées indirectement ( len([10, 20])par exemple). La plupart des opérateurs python ont une méthode "magique" associée (par exemple, a[x]est la manière habituelle d'invoquer a.__getitem__(x)).



5

En fait, j'utilise des noms de méthode _ lorsque j'ai besoin de faire la différence entre les noms de classe parent et enfant. J'ai lu quelques codes qui utilisaient cette façon de créer des classes parent-enfant. À titre d'exemple, je peux fournir ce code:

class ThreadableMixin:
   def start_worker(self):
       threading.Thread(target=self.worker).start()

   def worker(self):
      try:
        self._worker()
    except tornado.web.HTTPError, e:
        self.set_status(e.status_code)
    except:
        logging.error("_worker problem", exc_info=True)
        self.set_status(500)
    tornado.ioloop.IOLoop.instance().add_callback(self.async_callback(self.results))

...

et l'enfant qui a une méthode _worker

class Handler(tornado.web.RequestHandler, ThreadableMixin):
   def _worker(self):
      self.res = self.render_string("template.html",
        title = _("Title"),
        data = self.application.db.query("select ... where object_id=%s", self.object_id)
    )

...


N'est-ce pas à cela que sert le préfixe de double soulignement?
AMC

1

Cette convention est utilisée pour les variables ou méthodes spéciales (dites «méthode magique») telles que __init__et __len__. Ces méthodes fournissent des fonctionnalités syntaxiques spéciales ou font des choses spéciales.

Par exemple, __file__indique l'emplacement du fichier Python, __eq__est exécuté lorsque l' a == bexpression est exécutée.

Un utilisateur peut bien sûr créer une méthode spéciale personnalisée, ce qui est un cas très rare, mais peut souvent modifier certaines des méthodes spéciales intégrées (par exemple, vous devez initialiser la classe avec __init__celle qui sera exécutée au début lorsqu'une instance d'une classe est créé).

class A:
    def __init__(self, a):  # use special method '__init__' for initializing
        self.a = a
    def __custom__(self):  # custom special method. you might almost do not use it
        pass

0

Ajout d'un exemple pour comprendre l'utilisation de __ en python. Voici la liste de tous __

https://docs.python.org/3/genindex-all.html#_

Certaines classes d'identifiants (outre les mots-clés) ont des significations spéciales. Toute utilisation de * noms, dans tout autre contexte, qui ne suit pas une utilisation explicitement documentée, est sujette à rupture sans avertissement

Restriction d'accès à l'aide de __

"""
Identifiers:
-  Contain only (A-z, 0-9, and _ )
-  Start with a lowercase letter or _.
-  Single leading _ :  private
-  Double leading __ :  strong private
-  Start & End  __ : Language defined Special Name of Object/ Method
-  Class names start with an uppercase letter.
-

"""


class BankAccount(object):
    def __init__(self, name, money, password):
        self.name = name            # Public
        self._money = money         # Private : Package Level
        self.__password = password  # Super Private

    def earn_money(self, amount):
        self._money += amount
        print("Salary Received: ", amount, " Updated Balance is: ", self._money)

    def withdraw_money(self, amount):
        self._money -= amount
        print("Money Withdraw: ", amount, " Updated Balance is: ", self._money)

    def show_balance(self):
        print(" Current Balance is: ", self._money)


account = BankAccount("Hitesh", 1000, "PWD")  # Object Initalization

# Method Call
account.earn_money(100)

# Show Balance
print(account.show_balance())

print("PUBLIC ACCESS:", account.name)  # Public Access

# account._money is accessible because it is only hidden by convention
print("PROTECTED ACCESS:", account._money)  # Protected Access

# account.__password will throw error but account._BankAccount__password will not
# because __password is super private
print("PRIVATE ACCESS:", account._BankAccount__password)

# Method Call
account.withdraw_money(200)

# Show Balance
print(account.show_balance())

# account._money is accessible because it is only hidden by convention
print(account._money)  # Protected Access
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.