¿Cuál es el significado del subrayado simple y doble antes del nombre de un objeto?

1502

¿Alguien puede explicar el significado exacto de tener guiones bajos iniciales simples y dobles antes del nombre de un objeto en Python, y la diferencia entre ambos?

Además, ¿ese significado permanece igual independientemente de si el objeto en cuestión es una variable, una función, un método, etc.?

2
1334

Guión bajo único

Los nombres, en una clase, con un guión bajo inicial son simplemente para indicar a otros programadores que el atributo o método está destinado a ser privado. Sin embargo, no se hace nada especial con el nombre en sí.

Para citar PEP-8 :

_single_leading_underscore: weak "internal use" indicator. E.g. from M import * does not import objects whose name starts with an underscore.

Doble subrayado (cambio de nombre)

De los documentos de Python :

Any identifier of the form __spam (at least two leading underscores, at most one trailing underscore) is textually replaced with _classname__spam, where classname is the current class name with leading underscore(s) stripped. This mangling is done without regard to the syntactic position of the identifier, so it can be used to define class-private instance and class variables, methods, variables stored in globals, and even variables stored in instances. private to this class on instances of other classes.

Y una advertencia de la misma página:

Name mangling is intended to give classes an easy way to define “private” instance variables and methods, without having to worry about instance variables defined by derived classes, or mucking with instance variables by code outside the class. Note that the mangling rules are designed mostly to avoid accidents; it still is possible for a determined soul to access or modify a variable that is considered private.

Ejemplo

>>> class MyClass():
...     def __init__(self):
...             self.__superprivate = "Hello"
...             self._semiprivate = ", world!"
...
>>> mc = MyClass()
>>> print mc.__superprivate
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: myClass instance has no attribute '__superprivate'
>>> print mc._semiprivate
, world!
>>> print mc.__dict__
{'_MyClass__superprivate': 'Hello', '_semiprivate': ', world!'}
11
  • 23
    ¿Qué pasa si hay un nombre de variable declarado con 2 guiones bajos que no está en la clase? Es solo una variable normal, ¿verdad? 27 de julio de 2015 a las 8:10
  • 138
    Esta respuesta es extremadamente engañosa, ya que lleva al lector a creer que dunderscore se usa para hacer que los atributos de instancia sean "superprivados". Este no es el caso, como lo explica aquí Raymond Hettinger, quien declara explícitamente que dunderscore se usa incorrectamente para marcar a los miembros como privados, mientras que fue diseñado para ser lo opuesto a privado. 17 de mayo de 2016 a las 10:01
  • 21
    @MarkusMeskanen No estoy de acuerdo, la respuesta establece explícitamente el uso de un dunderscore para crear instancias de variables y métodos privados de clase. Si bien el dunderscore fue diseñado para hacer que estos métodos y variables se sobrescriban fácilmente por subclases (haciéndolos públicos), el uso de dunderscore conserva una instancia privada para su uso dentro de esa clase.
    arewm
    5 de julio de 2016 a las 15:11
  • 13
    @MarkusMeskanen: La libertad es que las subclases usen los mismos nombres que la superclase sin golpear a la superclase; en otras palabras, los nombres más dunder de las superclases se vuelven privados para sí mismos. 15/07/2016 a las 17:48
  • 11
    Para un solo subrayado, la respuesta dice "no se hace nada especial con el nombre en sí", pero luego continúa diciendo que lo from M import *trata de manera diferente ... por lo que se hace algo especial ...
    flow2k
    11 de mayo de 2018 a las 17:27
361

__foo__: esto es solo una convención, una forma para que el sistema Python use nombres que no entren en conflicto con los nombres de usuario.

_foo: esto es solo una convención, una forma para que el programador indique que la variable es privada (lo que sea que eso signifique en Python).

__foo: esto tiene un significado real: el intérprete reemplaza este nombre con _classname__foocomo una forma de asegurarse de que el nombre no se superponga con un nombre similar en otra clase.

Ninguna otra forma de guiones bajos tiene significado en el mundo de Python.

No hay diferencia entre clase, variable, global, etc. en estas convenciones.

4
  • 7
    Simplemente encontré __fooy curioso. ¿Cómo puede superponerse con nombres de métodos similares con otras clases? Quiero decir que todavía tienes que acceder a él como instance.__foo()(si el intérprete no le cambió el nombre), ¿verdad? 9 de mayo de 2013 a las 8:03
  • 110
    Este tipo afirma que from module import *no importa objetos con prefijo de subrayado. Por tanto, _fooes más que una simple convención. 13/06/2013 a las 13:28
  • 5
    @Bibhas: si la clase Bsubclases class A, y ambas implementan foo(), B.foo()anula la .foo()heredada de A. Una instancia de Bsolo podrá acceder B.foo(), excepto a través de super(B).foo(). 26 de enero de 2015 a las 3:11
  • 5
    Para los __dunder__nombres, las invocaciones implícitas omiten el diccionario de instancias, por lo que quizás sea un poco más que una convención de nomenclatura en algunos casos (consulte la sección de búsqueda de métodos especiales en el modelo de datos).
    wim
    20/02/20 a las 20:43
347

Excelentes respuestas hasta ahora, pero faltan algunas cositas. Un guión que conduce sola no es exactamente simplemente una convención: si se utiliza from foobar import *, y el módulo foobarno define una __all__lista, los nombres importados desde el módulo no se incluyen los que tienen un subrayado inicial. Digamos que es principalmente una convención, ya que este caso es un rincón bastante oscuro ;-).

La convención de subrayado inicial se usa ampliamente no solo para nombres privados , sino también para lo que C ++ llamaría los protegidos , por ejemplo, nombres de métodos que están completamente destinados a ser reemplazados por subclases (incluso aquellos que deben ser reemplazados desde en la clase base they raise NotImplementedError! -) son a menudo nombres de subrayado inicial único para indicar al código utilizando instancias de esa clase (o subclases) que dichos métodos no deben llamarse directamente.

Por ejemplo, para crear una cola segura para subprocesos con una disciplina de cola diferente a FIFO, uno importa Cola, subclases Queue.Queue y anula métodos como _gety _put; El "código de cliente" nunca llama a esos métodos ("gancho"), sino a los métodos públicos ("organizativos") como puty get(esto se conoce como el patrón de diseño del Método de plantilla ; consulte, por ejemplo, aquí una presentación interesante basada en un video de una charla mía sobre el tema, con la adición de sinopsis de la transcripción).

Editar: Los enlaces de video en la descripción de las charlas ahora están rotos. Puedes encontrar los dos primeros videos aquí y aquí .

10
  • 2
    Entonces, ¿cómo decides si usar _var_nameo usar var_name+ excluirlo __all__? 30 de ene. De 2017 a las 15:39
  • 3
    @endolith Use un subrayado inicial para indicar al lector de su código que probablemente no debería usarlo (por ejemplo, porque podría cambiarlo en la versión 2.0, o incluso 1.1); use explícito __all__siempre que desee que el módulo sea from spam import *amigable (incluso en el intérprete interactivo). Entonces, la mayoría de las veces, la respuesta es ambas . 25/04/18 a las 17:15
  • @AlexMartelli ¿Esta regla relacionada con la importación se discute legalmente en algún lugar de los documentos o en otro lugar? 31/08/18 a las 13:12
  • 1
    Me gusta la analogía de C ++. En primer lugar, no me gusta cuando la gente llama al _ privado . Evidentemente, estoy hablando de analogías, ya que nada es realmente privado en Python. Cuando el buceo en la semántica yo diría que podemos atar el _que de Java protegida desde proctected "clases derivadas y / o dentro del mismo paquete" en los medios de Java. Reemplaza paquete con módulo ya que PEP8 ya nos dice que _no es solo una convención cuando se habla de *importaciones y ahí lo tienes. Y definitivamente __sería equivalente al privado de Java cuando se habla de identificadores dentro de una clase. 4 jun 19 a las 19:03
  • 3
    Si bien es una respuesta decente, también es muy autopromocional. 7 de ene de 2020 a las 7:42
222

._variable es semiprivado y está diseñado solo para convenciones

.__variablea menudo se considera incorrectamente superprivado, mientras que su significado real es simplemente nombrar para evitar el acceso accidental [1]

.__variable__ normalmente se reserva para métodos o variables integrados

Aún puede acceder a las .__mangledvariables si lo desea desesperadamente. Los guiones bajos dobles solo nombran, o renombran, la variable a algo comoinstance._className__mangled

Ejemplo:

class Test(object):
    def __init__(self):
        self.__a = 'a'
        self._b = 'b'

>>> t = Test()
>>> t._b
'b'

t._b es accesible porque solo está oculto por convención

>>> t.__a
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'Test' object has no attribute '__a'

t .__ a no se encuentra porque ya no existe debido a la alteración de nombres

>>> t._Test__a
'a'

Al acceder en instance._className__variablelugar de solo el nombre de doble subrayado, puede acceder al valor oculto

3
  • pero ¿qué tal si "__a" fuera una variable de clase, entonces no puede acceder a ella incluso con las instrucciones de los documentos de Python? 6 de julio de 2017 a las 7:07
  • ¿Puede actualizar su respuesta con un ejemplo de doble subrayado con respecto a la herencia? 9/09/19 a las 15:56
  • ._variable, de acuerdo con las publicaciones anteriores y PEP-8, no es solo una convención: " from M import *no importa objetos cuyos nombres comiencen con un guión bajo". Sin embargo, en el caso presentado que lo muestra como un atributo de clase, no cambia nada.
    pdaawr
    3 de mayo a las 9:07
138

Guión bajo único al principio:

Python no tiene métodos privados reales. En cambio, un guión bajo al comienzo de un método o nombre de atributo significa que no debe acceder a este método, porque no es parte de la API.

class BaseForm(StrAndUnicode):

    def _get_errors(self):
        "Returns an ErrorDict for the data provided for the form"
        if self._errors is None:
            self.full_clean()
        return self._errors

    errors = property(_get_errors)

(Este fragmento de código se tomó del código fuente de django: django / forms / forms.py). En este código, errorses una propiedad pública, pero el método que llama esta propiedad, _get_errors, es "privado", por lo que no debería acceder a él.

Dos guiones bajos al principio:

Esto causa mucha confusión. No debe usarse para crear un método privado. Debe usarse para evitar que su método sea anulado por una subclase o se acceda accidentalmente. Veamos un ejemplo:

class A(object):
    def __test(self):
        print "I'm a test method in class A"

    def test(self):
        self.__test()

a = A()
a.test()
# a.__test() # This fails with an AttributeError
a._A__test() # Works! We can access the mangled name directly!

Producción:

$ python test.py
I'm test method in class A
I'm test method in class A

Ahora cree una subclase B y personalice el método __test

class B(A):
    def __test(self):
        print "I'm test method in class B"

b = B()
b.test()

La salida será ....

$ python test.py
I'm test method in class A

Como hemos visto, A.test () no llamó a los métodos B .__ test (), como era de esperar. Pero, de hecho, este es el comportamiento correcto para __. Los dos métodos llamados __test () se renombran automáticamente (mutilados) a _A__test () y _B__test (), por lo que no se anulan accidentalmente. Cuando crea un método que comienza con __, significa que no desea que nadie pueda anularlo, y solo tiene la intención de acceder a él desde dentro de su propia clase.

Dos guiones bajos al principio y al final:

Cuando vemos un método como __this__, no lo llames. Este es un método al que Python debe llamar, no a ti. Vamos a ver:

>>> name = "test string"
>>> name.__len__()
11
>>> len(name)
11

>>> number = 10
>>> number.__add__(40)
50
>>> number + 50
60

Siempre hay un operador o función nativa que llama a estos métodos mágicos. A veces es solo un gancho de llamadas de Python en situaciones específicas. Por ejemplo, __init__()se llama cuando se crea el objeto después de que __new__()se llama para crear la instancia ...

Tomemos un ejemplo ...

class FalseCalculator(object):

    def __init__(self, number):
        self.number = number

    def __add__(self, number):
        return self.number - number

    def __sub__(self, number):
        return self.number + number

number = FalseCalculator(20)
print number + 10      # 10
print number - 20      # 40

Para obtener más detalles, consulte la guía PEP-8 . Para obtener más métodos mágicos, consulte este PDF .

2
  • 2
    Después de editar esta respuesta yo mismo, prefiero stackoverflow.com/a/8689983/1048186 21/08/18 a las 19:30
  • ¿Qué quiere decir con "Como hemos visto, A.test () no llamó a los métodos B .__ test ()"? ¿Dónde ha llamado A.test ()? 9/09/19 a las 15:27
32

Según el significado de los guiones bajos en Python

  • Subrayado inicial único ( _var) : Convención de nomenclatura que indica que un nombre está destinado a uso interno. Por lo general, el intérprete de Python no lo aplica (excepto en las importaciones de comodines) y solo es una sugerencia para el programador.
  • Subrayado final único ( var_) : utilizado por convención para evitar conflictos de nombres con palabras clave de Python.
  • Doble subrayado inicial ( __var) : desencadena la alteración de nombres cuando se usa en un contexto de clase. Aplicado por el intérprete de Python.
  • Doble subrayado inicial y final ( __var__) : indica métodos especiales definidos por el lenguaje Python. Evite este esquema de nombres para sus propios atributos.
  • Subrayado único ( _) : a veces se utiliza como nombre para variables temporales o insignificantes ("no me importa"). Además: el resultado de la última expresión en un REPL de Python .
19

A veces tiene lo que parece ser una tupla con un guión bajo inicial como en

def foo(bar):
    return _('my_' + bar)

En este caso, lo que está sucediendo es que _ () es un alias para una función de localización que opera en texto para ponerlo en el idioma apropiado, etc. según la configuración regional. Por ejemplo, Sphinx hace esto y encontrará entre las importaciones

from sphinx.locale import l_, _

y en sphinx.locale, _ () se asigna como un alias de alguna función de localización.

15

Dado que tanta gente se está refiriendo a la charla de Raymond , lo haré un poco más fácil escribiendo lo que dijo:

The intention of the double underscores was not about privacy. The intention was to use it exactly like this

class Circle(object):

    def __init__(self, radius):
        self.radius = radius

    def area(self):
        p = self.__perimeter()
        r = p / math.pi / 2.0
        return math.pi * r ** 2.0

    def perimeter(self):
        return 2.0 * math.pi * self.radius

    __perimeter = perimeter  # local reference


class Tire(Circle):

    def perimeter(self):
        return Circle.perimeter(self) * 1.25

It's actually the opposite of privacy, it's all about freedom. It makes your subclasses free to override any one method without breaking the others.

Digamos que no tiene una referencia local de perimeterin Circle. Ahora, una clase derivada Tireanula la implementación de perimeter, sin tocar area. Cuando llama Tire(5).area(), en teoría, debería seguir utilizándose Circle.perimeterpara la computación, pero en realidad está utilizando Tire.perimeter, que no es el comportamiento previsto. Por eso necesitamos una referencia local en Circle.

¿Pero por qué en __perimeterlugar de _perimeter? Porque _perimetertodavía le da a la clase derivada la oportunidad de anular:

class Tire(Circle):

    def perimeter(self):
        return Circle.perimeter(self) * 1.25

    _perimeter = perimeter

Los subrayados dobles tienen alteración de nombres, por lo que hay muy pocas posibilidades de que la referencia local en la clase principal se anule en la clase derivada. por lo tanto, " hace que sus subclases sean libres de anular cualquier método sin romper los demás ".

Si su clase no se heredará, o si la anulación del método no rompe nada, entonces simplemente no es necesario __double_leading_underscore.

2
  • 1
    Gracias, la diapositiva no se mostró correctamente, por lo que terminé sin desmentir por qué fallaba mi código.
    cgte
    28/10/19 a las 21:29
  • Hmm, obtuve la misma respuesta independientemente de que el perímetro tuviera dunder principal o no. 26 mar a las 21:50
8

Si uno realmente quiere hacer una variable de solo lectura, en mi humilde opinión, la mejor manera sería usar property () con solo pasarle un getter. Con property () podemos tener un control completo sobre los datos.

class PrivateVarC(object):

    def get_x(self):
        pass

    def set_x(self, val):
        pass

    rwvar = property(get_p, set_p)  

    ronly = property(get_p) 

Entiendo que OP hizo una pregunta un poco diferente, pero como encontré otra pregunta que preguntaba 'cómo establecer variables privadas' marcadas como duplicadas con esta, pensé en agregar esta información adicional aquí.

6

Los guiones bajos iniciales únicos son una convención. no hay diferencia desde el punto de vista del intérprete si los nombres comienzan con un guión bajo o no.

Doble ataque y de salida de subrayado se utilizan para una función de métodos, tales como __init__, __bool__, etc.

Los guiones bajos iniciales dobles sin contrapartes finales también son una convención, sin embargo, los métodos de clase serán alterados por el intérprete. Para variables o nombres de funciones básicas no existe diferencia.

6

Excelentes respuestas y todas son correctas. He proporcionado un ejemplo simple junto con una definición / significado simple.

Sentido:

alguna_variable --► es público, cualquiera puede ver esto.

_some_variable --► es público, cualquiera puede ver esto, pero es una convención para indicar privado ... advirtiendo que Python no realiza ninguna aplicación.

__some_varaible --► Python reemplaza el nombre de la variable con _classname__some_varaible (AKA name mangling) y reduce / oculta su visibilidad y se parece más a una variable privada.

Solo para ser honesto aquí De acuerdo con la documentación de Python

"“Private” instance variables that cannot be accessed except from inside an object don’t exist in Python"

El ejemplo:

class A():
    here="abc"
    _here="_abc"
    __here="__abc"


aObject=A()
print(aObject.here) 
print(aObject._here)
# now if we try to print __here then it will fail because it's not public variable 
#print(aObject.__here)
1
  • _ _some_varaible - .... y reduce / oculta su visibilidad y se parece más a una variable privada. No, la cuestión es alterar los nombres, no oculta el método.
    AMC
    11 feb.20 a las 17:04
5

Aquí hay un ejemplo ilustrativo simple sobre cómo las propiedades de doble subrayado pueden afectar una clase heredada. Entonces, con la siguiente configuración:

class parent(object):
    __default = "parent"
    def __init__(self, name=None):
        self.default = name or self.__default

    @property
    def default(self):
        return self.__default

    @default.setter
    def default(self, value):
        self.__default = value


class child(parent):
    __default = "child"

si luego crea una instancia secundaria en Python REPL, verá lo siguiente

child_a = child()
child_a.default            # 'parent'
child_a._child__default    # 'child'
child_a._parent__default   # 'parent'

child_b = child("orphan")
## this will show 
child_b.default            # 'orphan'
child_a._child__default    # 'child'
child_a._parent__default   # 'orphan'

Esto puede ser obvio para algunos, pero me tomó con la guardia baja en un entorno mucho más complejo.

5
  • _var: las variables con un guión bajo inicial en Python son variables clásicas, destinadas a informar a otros que usan su código que esta variable debe reservarse para uso interno. Se diferencian en un punto de las variables clásicas: no se importan al hacer una importación comodín de un objeto / módulo donde están definidas (excepciones al definir la __all__variable ). P.ej:

    # foo.py
    
    var = "var"
    _var = "_var"
    
    # bar.py
    
    from foo import *
    
    print(dir())  # list of defined objects, contains 'var' but not '_var'
    print(var)    # var
    print(_var)   # NameError: name '_var' is not defined
    
  • _: el guión bajo simple es un caso especial de las variables de guión bajo único iniciales. Se utiliza por convención como una variable de papelera, para almacenar un valor al que no se pretende acceder posteriormente. Tampoco se importa mediante importaciones comodín. Por ejemplo: este forbucle imprime "No debo hablar en clase" 10 veces y nunca necesita acceder a la _variable.

    for _ in range(10):
        print("I must not talk in class")
    
  • __var: variables de subrayado inicial doble (al menos dos subrayados iniciales, como máximo un subrayado final). Cuando se usan como atributos de clase (variables y métodos), estas variables están sujetas a cambios de nombre: fuera de la clase, Python cambiará el nombre del atributo a _<Class_name>__<attribute_name>. Ejemplo:

    class MyClass:
        __an_attribute = "attribute_value"
    
    my_class = MyClass()
    print(my_class._MyClass__an_attribute)  # "attribute_value"
    print(my_class.__an_attribute)  # AttributeError: 'MyClass' object has no attribute '__an_attribute'
    

    Cuando se utilizan como variables fuera de una clase, se comportan como variables de subrayado iniciales únicas.

  • __var__: variables de subrayado inicial y final doble (al menos dos subrayados iniciales y finales). También se llama dunders . Python utiliza esta convención de nomenclatura para definir variables internamente. Evite el uso de esta convención para evitar conflictos de nombres que podrían surgir con las actualizaciones de Python. Las variables Dunder se comportan como variables de subrayado principal único: no están sujetas a alteración de nombres cuando se usan dentro de clases, pero no se importan en importaciones con comodines.

3

Tu pregunta es buena, no se trata solo de métodos. Las funciones y los objetos de los módulos suelen ir precedidos de un guión bajo y pueden ir precedidos de dos.

Pero los nombres __double_underscore no están alterados en los módulos, por ejemplo. Lo que sucede es que los nombres que comienzan con uno (o más) guiones bajos no se importan si importa todo desde un módulo (desde la importación del módulo *), ni los nombres que se muestran en la ayuda (módulo).

1
  • 1
    Además, los nombres que comienzan con uno o más guiones bajos que tienen dos o más guiones bajos finales se comportan nuevamente como cualquier otro nombre. 7/04/12 a las 15:19
3

Las variables de instancia "privadas" a las que no se puede acceder excepto desde el interior de un objeto no existen en Python. Sin embargo, existe una convención que es seguida por la mayoría del código Python: un nombre prefijado con un guión bajo (por ejemplo, _spam) debe tratarse como una parte no pública de la API (ya sea una función, un método o un miembro de datos) . Debe considerarse un detalle de implementación y sujeto a cambios sin previo aviso.

referencia https://docs.python.org/2/tutorial/classes.html#private-variables-and-class-local-references

1
  • 1
    _ es mucho más similar, por ejemplo, a internal en c # que a private. El subrayado doble es mucho más similar a privado, entonces el subrayado es privado, diría yo.
    Ini
    4 de nov. De 2017 a las 0:38
1

Obtener los datos de _ y __ es bastante fácil; las otras respuestas las expresan bastante bien. El uso es mucho más difícil de determinar.

Así es como yo lo veo:

_

Debe usarse para indicar que una función no es para uso público como, por ejemplo, una API. Esto y la restricción de importación hacen que se comporte de forma muy similar internala c #.

__

Debe usarse para evitar la colisión de nombres en la jerarquía de herencia y para evitar la unión tardía. Al igual que privado en c #.

==>

Si desea indicar que algo no es para uso público, pero debe actuar como protecteduso _. Si desea indicar que algo no es para uso público, pero debe actuar como privateuso __.

Esta es también una cita que me gusta mucho:

The problem is that the author of a class may legitimately think "this attribute/method name should be private, only accessible from within this class definition" and use the __private convention. But later on, a user of that class may make a subclass that legitimately needs access to that name. So either the superclass has to be modified (which may be difficult or impossible), or the subclass code has to use manually mangled names (which is ugly and fragile at best).

Pero el problema con eso es, en mi opinión, que si no hay un IDE que le advierta cuando anula los métodos, encontrar el error puede llevarle un tiempo si ha anulado accidentalmente un método de una clase base.

0

En el caso de los métodos, puede usar el doble subrayado para ocultar los 'métodos' privados con el siguiente patrón:

# Private methods of MyClass
def _MyClass__do_something(obj:'MyClass'):
    print('_MyClass__do_something() called. type(obj) = {}'.format(type(obj)))

class MyClass():
    def __init__(self):
        __do_something(self)

mc = MyClass()

Producción:

_MyClass__do_something() called. type(obj) = <class '__main__.MyClass'>

Me encontré con esto hoy cuando intenté usar doble subrayado para los métodos de clase y obtuve el NameError: name '_<class><method>' is not definederror.