Qual é o significado de sublinhado único e duplo antes do nome de um objeto?

1292

Alguém pode explicar o significado exato de ter sublinhados à esquerda antes do nome de um objeto em Python e a diferença entre ambos?

Além disso, esse significado permanece o mesmo, independentemente de o objeto em questão ser uma variável, uma função, um método etc.?

ivanleoncz
fonte
2
Uma grande resposta curta de outro segmento: stackoverflow.com/a/8689983/911945
Anton Tarasenko

Respostas:

1156

Sublinhado único

Os nomes, em uma classe, com um sublinhado à esquerda são simplesmente para indicar a outros programadores que o atributo ou método se destina a ser privado. No entanto, nada de especial é feito com o próprio nome.

Para citar PEP-8 :

_single_leading_underscore: fraco indicador de "uso interno". Por exemplo from M import *, não importa objetos cujo nome comece com um sublinhado.

Sublinhado duplo (Mangling de nome)

Nos documentos do Python :

Qualquer identificador do formulário __spam(pelo menos dois sublinhados à esquerda, no máximo um sublinhado à direita) é textualmente substituído por _classname__spam, onde classnameé o nome da classe atual com os sublinhados à esquerda removidos. Essa manipulação é feita sem levar em consideração a posição sintática do identificador; portanto, pode ser usada para definir instâncias de classe privada e variáveis ​​de classe, métodos, variáveis ​​armazenadas em globais e até variáveis ​​armazenadas em instâncias. privado para esta classe em instâncias de outras classes.

E um aviso da mesma página:

A manipulação de nomes tem como objetivo fornecer às classes uma maneira fácil de definir variáveis ​​e métodos de instância "privados", sem ter que se preocupar com variáveis ​​de instância definidas por classes derivadas ou mexer com variáveis ​​de instância por código fora da classe. Observe que as regras de confundimento são projetadas principalmente para evitar acidentes; ainda é possível que uma alma determinada acesse ou modifique uma variável considerada privada.

Exemplo

>>> 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!'}
Andrew Keeton
fonte
17
E se houver um nome de variável declarado com 2 sublinhados que não esteja na classe? É apenas uma variável normal, certo?
Dhruv Ramani
5
qual é o significado de simplesmente __sublinhado duplo como um nome de variável? comoa, __ = foo()
AJ
104
Essa resposta é extremamente enganosa, pois leva o leitor a acreditar que a pontuação deundund é usada para tornar os atributos da instância "superprivados". Este não é o caso, como explicado aqui por Raymond Hettinger, que afirma explicitamente que a pontuação de dunderscore é incorretamente usada para marcar membros como privados, enquanto foi projetada para ser o oposto de privado.
Markus Meskanen 17/05/19
13
@MarkusMeskanen Não concordo, a resposta declara explicitamente o uso de uma pontuação de dunderscore para criar instâncias de variáveis ​​e métodos privados de classe. Embora o dunderscore tenha sido projetado para tornar esses métodos e variáveis ​​facilmente substituídos por subclasses (tornando-os públicos), o uso de um dunderscore preserva uma instância privada para uso nessa classe.
arewm
6
@MarkusMeskanen: A liberdade é que as subclasses usem os mesmos nomes que a superclasse sem prejudicar a superclasse - em outras palavras, os nomes obscuros das superclasses se tornam privados para si mesmos.
Ethan Furman
311

Excelentes respostas até agora, mas faltam alguns petiscos. Um único sublinhado principal não é exatamente apenas uma convenção: se você usar from foobar import *, e o módulo foobarnão definir uma __all__lista, os nomes importados do módulo não incluirão aqueles com um sublinhado principal. Digamos que é principalmente uma convenção, já que este caso é um canto bastante obscuro ;-).

A convenção de sublinhado principal é amplamente usada não apenas para nomes particulares , mas também para o que o C ++ chamaria de protegidos - por exemplo, nomes de métodos que se destinam totalmente a serem substituídos por subclasses (mesmo aqueles que precisam ser substituídos desde a classe base eles raise NotImplementedError! -) geralmente são nomes com sublinhado à esquerda para indicar ao código usando instâncias dessa classe (ou subclasses) que esses métodos não devem ser chamados diretamente.

Por exemplo, para criar uma fila de thread-safe com uma disciplina de enfileiramento diferente da FIFO, importa-se Fila, subclasses Queue.Queue e substitui métodos como _gete _put; O "código do cliente" nunca chama esses métodos ("ganchos"), mas os métodos públicos ("organizando") como pute get(isso é conhecido como padrão de design do Método do Modelo - veja, por exemplo, aqui uma apresentação interessante baseada em um vídeo) de uma palestra minha sobre o assunto, com a adição de sinopses da transcrição).

Edit: Os links de vídeo na descrição das conversas agora estão quebrados. Você pode encontrar os dois primeiros vídeos aqui e aqui .

Alex Martelli
fonte
1
Então, como você decide se deseja usar _var_nameou usar var_name+ excluindo-o __all__?
endolith 30/01
3
@ endolith Use sublinhado à esquerda para sinalizar ao leitor do seu código que ele provavelmente não deveria usá-lo (por exemplo, porque você pode alterá-lo na versão 2.0 ou até 1.1); use explícito __all__sempre que desejar tornar o módulo from spam import *amigável (inclusive no intérprete interativo). Então, na maioria das vezes, a resposta é ambas .
precisa saber é o seguinte
@AlexMartelli Esta regra relacionada à importação é discutida legalmente em algum lugar do Documentos ou em outro lugar?
Vicrobot
1
Eu gosto da analogia C ++. Em primeiro lugar, não gosto quando as pessoas chamam de _ privado . Evidentemente, estou falando de analogias, já que nada é verdadeiramente privado em Python. Ao mergulhar na semântica, eu diria que podemos vincular o protegido_ ao Java, já que o proctected em Java significa "classes derivadas e / ou dentro do mesmo pacote". Substitua pacote pelo módulo, já que o PEP8 já nos diz que não é apenas uma convenção quando se fala de importações, e aí está. E definitivamente seria equivalente ao privado de Java ao falar sobre identificadores dentro de uma classe. _*__
Marius Mucenicu
2
Embora seja uma resposta decente, também é altamente autopromocional.
Hybrid web dev
299

__foo__: esta é apenas uma convenção, uma maneira de o sistema Python usar nomes que não entrem em conflito com os nomes de usuário.

_foo: esta é apenas uma convenção, uma maneira do programador indicar que a variável é privada (o que quer que isso signifique em Python).

__foo: isso tem um significado real: o intérprete substitui esse nome _classname__foopor uma maneira de garantir que o nome não se sobreponha a um nome semelhante em outra classe.

Nenhuma outra forma de sublinhado tem significado no mundo Python.

Não há diferença entre classe, variável, global, etc. nessas convenções.

Ned Batchelder
fonte
6
Apenas veio através __fooe curioso. Como ele pode se sobrepor a nomes de métodos semelhantes a outras classes? Quero dizer, você ainda precisa acessá-lo como instance.__foo()(se não fosse renomeado pelo intérprete), certo?
Bibhas Debnath
81
Esse cara afirma que from module import *não importa objetos com prefixo sublinhado. Portanto, _fooé mais do que apenas uma convenção.
dotancohen
4
@Bibhas: if class Bsubclasses class A, e ambos implementam foo(), então B.foo()substitui o .foo()herdado de A. Uma instância de Bsomente poderá acessar B.foo(), exceto via super(B).foo().
precisa saber é o seguinte
3
Para __dunder__nomes, invocações implícitas ignoram o dicionário da instância, portanto, talvez seja um pouco mais do que apenas uma convenção de nomenclatura em alguns casos (consulte a seção de pesquisa de métodos especiais no modelo de dados).
wim 20/02
206

._variable é semi-privado e destinado apenas a convenções

.__variablegeralmente é considerado incorretamente superprivado, enquanto o significado real é apenas alterar o nome para impedir o acesso acidental [1]

.__variable__ é normalmente reservado para métodos ou variáveis ​​incorporadas

Você ainda pode acessar .__mangledvariáveis ​​se desejar desesperadamente. O sublinhado duplo apenas nomemangeia ou renomeia a variável para algo comoinstance._className__mangled

Exemplo:

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

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

t._b é acessível porque está oculto apenas por convenção

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

t .__ a não foi encontrado porque não existe mais devido à manipulação de nomes

>>> t._Test__a
'a'

Ao acessar, em instance._className__variablevez de apenas o nome de sublinhado duplo, você pode acessar o valor oculto

NickCSE
fonte
mas como sobre se "__A" foi uma variável de classe, então você não pode acessá-lo, mesmo com as instruções do docs python ..
Vitaliy Terziev
Você pode atualizar sua resposta com um exemplo de sublinhado duplo em relação à herança?
variável
116

Sublinhado único no início:

Python não possui métodos particulares reais. Em vez disso, um sublinhado no início de um método ou nome de atributo significa que você não deve acessar esse método, porque ele não faz parte da 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 trecho de código foi retirado do código-fonte do django: django / forms / forms.py). Nesse código, errorsé uma propriedade pública, mas o método que essa propriedade chama, _get_errors, é "privado", portanto você não deve acessá-lo.

Dois sublinhados no início:

Isso causa muita confusão. Não deve ser usado para criar um método privado. Deve ser usado para evitar que seu método seja substituído por uma subclasse ou acessado acidentalmente. Vamos ver um exemplo:

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!

Resultado:

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

Agora crie uma subclasse B e faça customização para o método __test

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

b = B()
b.test()

A saída será ....

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

Como vimos, A.test () não chamou os métodos B .__ test (), como poderíamos esperar. Mas, de fato, esse é o comportamento correto para __. Os dois métodos chamados __test () são renomeados automaticamente (desconfigurados) para _A__test () e _B__test (), para que não sejam substituídos acidentalmente. Quando você cria um método começando com __, significa que você não deseja que ninguém possa substituí-lo e só pretende acessá-lo de dentro de sua própria classe.

Dois sublinhados no início e no final:

Quando vemos um método como __this__, não o chame. Este é um método que o python deve chamar, não você. Vamos dar uma olhada:

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

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

Sempre existe um operador ou função nativa que chama esses métodos mágicos. Às vezes, é apenas um python gancho chama em situações específicas. Por exemplo, __init__()é chamado quando o objeto é criado após __new__()ser chamado para criar a instância ...

Vamos dar um exemplo ...

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 mais detalhes, consulte o guia PEP-8 . Para mais métodos mágicos, consulte este PDF .

PythonDev
fonte
1
Depois de editar esta resposta eu mesmo, eu prefiro stackoverflow.com/a/8689983/1048186
Josias Yoder
O que você quer dizer com "Como vimos, A.test () não chamou métodos B .__ test ()" - onde você chamou A.test ()?
variável
18

Às vezes, você tem o que parece ser uma tupla com um sublinhado à esquerda, como em

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

Nesse caso, o que está acontecendo é que _ () é um alias para uma função de localização que opera em texto para colocá-lo no idioma apropriado etc. com base no código do idioma. Por exemplo, o Sphinx faz isso, e você encontrará entre as importações

from sphinx.locale import l_, _

e em sphinx.locale, _ () é designado como um alias de alguma função de localização.

Tim D
fonte
11

Como tantas pessoas estão se referindo à palestra de Raymond , eu vou facilitar um pouco, escrevendo o que ele disse:

A intenção dos dois sublinhados não era sobre privacidade. A intenção era usá-lo exatamente assim

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

Na verdade, é o oposto da privacidade, é tudo sobre liberdade. Isso torna suas subclasses livres para substituir qualquer método sem quebrar os outros .

Digamos que você não mantenha uma referência local de perimeterin Circle. Agora, uma classe derivada Tiresubstitui a implementação de perimeter, sem tocar area. Quando você liga Tire(5).area(), em teoria, ele ainda deve estar sendo usado Circle.perimeterpara computação, mas, na realidade, está sendo usado Tire.perimeter, o que não é o comportamento pretendido. É por isso que precisamos de uma referência local no Circle.

Mas por que ao __perimeterinvés de _perimeter? Porque _perimeterainda dá à classe derivada a chance de substituir:

class Tire(Circle):

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

    _perimeter = perimeter

Os sublinhados duplos têm nomes diferentes, portanto, há muito pouca chance de a referência local na classe pai ser substituída na classe derivada. assim " torna suas subclasses livres para substituir qualquer método sem quebrar os outros ".

Se sua classe não será herdada ou a substituição de método não quebra nada, você simplesmente não precisa __double_leading_underscore.

laike9m
fonte
1
Obrigado, o slide não foi exibido corretamente, por isso acabei não entendendo por que meu código falharia.
Cgte #
8

Se alguém realmente deseja criar uma variável somente leitura, o IMHO é a melhor maneira de usar a propriedade () com apenas o getter passado para ela. Com property (), podemos ter controle total sobre os dados.

class PrivateVarC(object):

    def get_x(self):
        pass

    def set_x(self, val):
        pass

    rwvar = property(get_p, set_p)  

    ronly = property(get_p) 

Entendo que o OP fez uma pergunta um pouco diferente, mas como encontrei outra pergunta pedindo 'como definir variáveis ​​privadas' marcada como duplicada com essa, pensei em adicionar essas informações adicionais aqui.

Dev Maha
fonte
8

Codificação para https://dbader.org/blog/meaning-of-underscores-in-python

  • Sublinhado à esquerda (_var) : convenção de nomenclatura que indica um nome para uso interno. Geralmente não é imposto pelo intérprete Python (exceto nas importações de caracteres curinga) e serve apenas como sugestão ao programador.
  • Sublinhado à direita (var_) : usado por convenção para evitar conflitos de nome com palavras-chave Python.
  • Sublinhado à esquerda duplo (__ var) : aciona o nome desconectado quando usado em um contexto de classe. Imposto pelo intérprete Python.
  • Sublinhado duplo à esquerda e à direita (__ var__) : indica métodos especiais definidos pela linguagem Python. Evite esse esquema de nomeação para seus próprios atributos.
  • Sublinhado único (_) : às vezes usado como um nome para variáveis ​​temporárias ou insignificantes (“não me importo”). Além disso: o resultado da última expressão em um Python REPL.
Feuda
fonte
5

As respostas são ótimas e todas estão corretas. Forneci um exemplo simples, juntamente com uma definição / significado simples.

Significado:

some_variable --► é público, qualquer pessoa pode ver isso.

_some_variable --► é público, qualquer pessoa pode ver isso, mas é uma convenção indicar privado ... aviso que nenhuma aplicação é executada pelo Python.

__some_varaible --► Python substitui o nome da variável por _classname__some_varaible (nome do arquivo AKA) e reduz / oculta sua visibilidade e se parece mais com uma variável privada.

Apenas para ser honesto aqui De acordo com a documentação do Python

"Variáveis ​​de instância" privadas "que não podem ser acessadas, exceto de dentro de um objeto, não existem no Python"

O exemplo:

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)
grepit
fonte
_ _some_varaible - .... e reduz / oculta sua visibilidade e se parece mais com uma variável privada. Não, o nome desconcertante é o ponto, não esconde o método.
AMC
4

Os sublinhados principais líderes são uma convenção. não há diferença do ponto de vista do intérprete se os nomes começam com um único sublinhado ou não.

Duplos ataque e de fuga sublinhados são utilizados para embutido métodos, tais como __init__, __bool__, etc.

Os sublinhados iniciais duplos sem contrapartes à direita também são uma convenção; no entanto, os métodos de classe serão confundidos pelo intérprete. Para variáveis ​​ou nomes de funções básicas, não existe diferença.

SilentGhost
fonte
3

Sua pergunta é boa, não se trata apenas de métodos. Funções e objetos em módulos são geralmente prefixados com um sublinhado e também podem ser prefixados por dois.

Mas os nomes __double_underscore não são modificados por nome nos módulos, por exemplo. O que acontece é que os nomes que começam com um (ou mais) sublinhados não são importados se você importar tudo de um módulo (da importação de módulo *), nem os nomes mostrados na ajuda (módulo).

u0b34a0f6ae
fonte
1
Além disso, os nomes que começam com um ou mais sublinhados que possuem dois ou mais sublinhados finais se comportam como qualquer outro nome novamente.
Bentley4
3

Aqui está um exemplo ilustrativo simples de como as propriedades de sublinhado duplo podem afetar uma classe herdada. Portanto, com a seguinte configuração:

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"

se você criar uma instância filho no python REPL, verá o abaixo

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'

Isso pode ser óbvio para alguns, mas me pegou de surpresa em um ambiente muito mais complexo

Marc
fonte
3

Variáveis ​​de instância "privadas" que não podem ser acessadas, exceto de dentro de um objeto, não existem no Python. No entanto, existe uma convenção seguida pela maioria dos códigos Python: um nome prefixado com um sublinhado (por exemplo, _spam) deve ser tratado como uma parte não pública da API (seja uma função, um método ou um membro de dados) . Deve ser considerado um detalhe da implementação e sujeito a alterações sem aviso prévio.

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

aptro
fonte
1
_ é muito mais parecido com, por exemplo, interno em c # do que privado. Sublinhado duplo é muito mais parecido com privado do que sublinhado é privado, eu diria.
Ini
2

Obter os fatos de _ e __ é bastante fácil; as outras respostas as expressam muito bem. O uso é muito mais difícil de determinar.

É assim que eu vejo:

_

Deve ser usado para indicar que uma função não é para uso público, como por exemplo uma API. Isso e a restrição de importação fazem com que ele se comporte de maneira semelhante internalem c #.

__

Deve ser usado para evitar colisão de nomes na hierarquia de herança e para evitar a ligação tardia. Bem como privado em c #.

==>

Se você deseja indicar que algo não é para uso público, mas deve agir como um protecteduso _. Se você deseja indicar que algo não é para uso público, mas deve agir como um privateuso __.

Esta é também uma citação de que gosto muito:

O problema é que o autor de uma classe pode legitimamente pensar que "esse nome de atributo / método deve ser privado, acessível apenas a partir dessa definição de classe" e usar a convenção __private. Mais tarde, porém, um usuário dessa classe pode criar uma subclasse que legitimamente precisa de acesso a esse nome. Portanto, a superclasse precisa ser modificada (o que pode ser difícil ou impossível) ou o código da subclasse deve usar nomes desconfigurados manualmente (o que é feio e frágil, na melhor das hipóteses).

Mas o problema é que, na minha opinião, se não houver um IDE que avise quando você substituir métodos, encontrar o erro poderá demorar um pouco se você substituir acidentalmente um método de uma classe base.

Invader
fonte