¿Por qué algunas funciones tienen guiones bajos "__" antes y después del nombre de la función?

425

Este "subrayado" parece ocurrir mucho, y me preguntaba si esto era un requisito en el lenguaje Python, o simplemente una cuestión de convención.

Además, ¿podría alguien nombrar y explicar qué funciones tienden a tener guiones bajos y por qué ( __init__por ejemplo)?

Chuck Testa
fuente
8
@AustinHenley: No para guiones bajos dobles antes y después del nombre. Estás pensando en guiones bajos únicamente antes del nombre.
@MackM Tenga en cuenta que esta pregunta pregunta sobre guiones bajos antes y después del nombre, y el objetivo duplicado que propuso pregunta sobre guiones bajos solo antes del nombre. Sin embargo, admito que algunas de las respuestas allí también cubren este caso.
Georgy

Respuestas:

527

De Python PEP 8 - Guía de estilo para código Python :

Descriptivo: Estilos de nombres

Se reconocen los siguientes formularios especiales que utilizan guiones bajos iniciales o finales (generalmente se pueden combinar con cualquier convención de caso):

  • _single_leading_underscore: indicador débil de "uso interno". Por ejemplo from M import *, no importa objetos cuyo nombre comience con un guión bajo.

  • single_trailing_underscore_: utilizado por convención para evitar conflictos con la palabra clave Python, p. ej.

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

  • __double_leading_underscore: al nombrar un atributo de clase, invoca el cambio de nombre (dentro de la clase FooBar, se __booconvierte en _FooBar__boo; ver más abajo).

  • __double_leading_and_trailing_underscore__: objetos o atributos "mágicos" que viven en espacios de nombres controlados por el usuario. Por ejemplo __init__, __import__o__file__ . Nunca inventes tales nombres; solo utilízalos como se documenta.

Tenga en cuenta que los nombres con guiones bajos iniciales y finales dobles están esencialmente reservados para Python: "Nunca invente tales nombres; solo utilícelos como se documenta".

Michael Burr
fuente
66
Raymond también explica por qué querrías que el comportamiento de cambio de nombre comience alrededor de los 34 minutos en este video: youtube.com/watch?v=HTLu2DFOdTg
johncip
55
Entonces, ¿la elección entre el guión bajo y el guión bajo doble en un nombre es un poco como elegir entre protegido y privado en C ++ y Java? _single_leading_underscore puede ser cambiado por los niños, pero __double_leading_underscore no?
Alex W
2
__double_leading_underscoreaún es público , la variable simplemente se renombra para evitar un choque.
cz
59

Los otros encuestados tienen razón al describir los guiones bajos dobles iniciales y finales como una convención de nomenclatura para métodos "especiales" o "mágicos".

Si bien puede llamar a estos métodos directamente ( [10, 20].__len__()por ejemplo), la presencia de los guiones bajos es una pista de que estos métodos están destinados a ser invocados indirectamente ( len([10, 20])por ejemplo). La mayoría de los operadores de python tienen un método "mágico" asociado (por ejemplo, a[x]es la forma habitual de invocar a.__getitem__(x)).

Raymond Hettinger
fuente
5

En realidad, uso _ nombres de métodos cuando necesito diferenciar entre los nombres de clase principal y secundario. He leído algunos códigos que utilizan esta forma de crear clases padre-hijo. Como ejemplo, puedo proporcionar este código:

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))

...

y el niño que tiene un método _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)
    )

...

Omadbek Onorov
fuente
¿No es esto para lo que sirve el prefijo de subrayado doble?
AMC
1

Esta convención se utiliza para variables o métodos especiales (el llamado "método mágico") como __init__y__len__ . Estos métodos proporcionan características sintácticas especiales o hacen cosas especiales.

Por ejemplo, __file__indica la ubicación del archivo Python, __eq__se ejecuta cuando a == bse ejecuta la expresión.

Un usuario, por supuesto, puede hacer un método especial personalizado, que es un caso muy raro, pero a menudo puede modificar algunos de los métodos especiales incorporados (por ejemplo, debe inicializar la clase con __init__eso se ejecutará al principio cuando una instancia de una clase es creado).

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
Shagun Pruthi
fuente
0

Se agregó un ejemplo para comprender el uso de __ en python. Aquí está la lista de Todos __

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

Ciertas clases de identificadores (además de las palabras clave) tienen significados especiales. Cualquier uso de * nombres, en cualquier otro contexto, que no siga el uso explícitamente documentado, está sujeto a roturas sin previo aviso

Restricción de acceso usando __

"""
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
Hitesh Sahu
fuente