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


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".


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



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

...


¿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

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
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.