O Python possui variáveis ​​"privadas" nas classes?

578

Estou vindo do mundo Java e lendo Python 3 Patterns, Recipes and Idioms, de Bruce Eckels .

Ao ler sobre classes, continua dizendo que no Python não há necessidade de declarar variáveis ​​de instância. Você apenas os usa no construtor, e boom, eles estão lá.

Então, por exemplo:

class Simple:
    def __init__(self, s):
        print("inside the simple constructor")
        self.s = s

    def show(self):
        print(self.s)

    def showMsg(self, msg):
        print(msg + ':', self.show())

Se isso for verdade, qualquer objeto da classe Simplepode apenas alterar o valor da variável sfora da classe.

Por exemplo:

if __name__ == "__main__":
    x = Simple("constructor argument")
    x.s = "test15" # this changes the value
    x.show()
    x.showMsg("A message")

Em Java, aprendemos sobre variáveis ​​públicas / privadas / protegidas. Essas palavras-chave fazem sentido porque às vezes você deseja variáveis ​​em uma classe às quais ninguém fora da classe tem acesso.

Por que isso não é necessário no Python?

Onipresente
fonte
17
Você quis dizer variáveis ​​de instância , não variáveis ​​de classe , certo?
PaulMcG
13
Você deve verificar as propriedades: docs.python.org/library/functions.html#property . Basta usar o getter e sua variável será protegida.
rubik 17/07
Uma resposta curta e nítida está aqui . Espero que isso ajude.
Premkumar chalmeti

Respostas:

962

É cultural. No Python, você não grava na instância ou nas variáveis ​​de outras classes. Em Java, nada impede que você faça o mesmo se realmente quiser - afinal, você sempre pode editar a fonte da própria classe para obter o mesmo efeito. O Python descarta essa pretensão de segurança e incentiva os programadores a serem responsáveis. Na prática, isso funciona muito bem.

Se você deseja emular variáveis ​​privadas por algum motivo, sempre pode usar o __prefixo do PEP 8 . O Python manipula os nomes das variáveis ​​como __foopara que elas não fiquem facilmente visíveis para o código fora da classe que as contém (embora você possa contornar isso se você estiver determinado o suficiente, assim como você pode contornar as proteções do Java se você trabalhar com isso )

Pela mesma convenção, o _prefixo significa ficar longe, mesmo que você não seja tecnicamente impedido de fazê-lo . Você não brinca com as variáveis ​​de outra classe que se parecem __fooou _bar.

Kirk Strauser
fonte
14
Isso faz sentido. No entanto, eu não acho que exista alguma maneira no java de acessar variáveis ​​privadas fora da classe (exceto, na verdade, alterar a fonte da classe de curso). Existe?
Onipresente
168
Costumo preferir o modo python, mas não acho que o java seja tão inútil quanto você imagina. Declarar algo privado rapidamente diz a alguém que lê o código algo muito útil: esse campo é modificado apenas dentro desta classe.
Ned
67
@ Onipresente, você pode usar a reflexão.
Raphael
77
Deixe-me esclarecer isso, para que o Python não implemente atributos públicos nem privados porque "é uma pretensão de segurança e incentiva os programadores a serem responsáveis"; no entanto, a comunidade incentiva o uso de "_" para indicar variáveis ​​e métodos privados? Talvez python definitivamente deva ter público e privado não? Seu principal objetivo é informar qual API você deve usar para interagir com uma classe. Eles servem como uma documentação dizendo para você usar esses métodos e não para usá-los. Eles não são uma "pretensão de segurança", são uma documentação da API, que pode até ser usada pelo IDE para guiá-lo!
PedroD
19
Essa é uma boa resposta e seu raciocínio é certamente válido, mas eu discordo de um aspecto. O objetivo dos modificadores de acesso nunca foi a segurança . Em vez disso, são um meio de demarcar explicitamente (e em grande parte, impor) quais partes de uma classe são consideradas internas e quais são expostas a usuários externos dessa classe. As convenções (cultura) são certamente uma alternativa válida aos modificadores de acesso, e ambos os métodos têm seus prós e contras, mas é enganoso propor que os modificadores de acesso no nível da linguagem tenham a intenção de ser "seguros" no sentido usual da linguagem. palavra.
devios1
159

Variáveis ​​privadas em python são mais ou menos um hack: o intérprete renomeia intencionalmente a variável.

class A:
    def __init__(self):
        self.__var = 123
    def printVar(self):
        print self.__var

Agora, se você tentar acessar __varfora da definição de classe, ela falhará:

 >>>x = A()
 >>>x.__var # this will return error: "A has no attribute __var"

 >>>x.printVar() # this gives back 123

Mas você pode facilmente se safar com isso:

 >>>x.__dict__ # this will show everything that is contained in object x
               # which in this case is something like {'_A__var' : 123}

 >>>x._A__var = 456 # you now know the masked name of private variables
 >>>x.printVar() # this gives back 456

Você provavelmente sabe que os métodos no OOP são invocados assim: x.printVar() => A.printVar(x)se A.printVar()puder acessar algum campo em x, esse campo também poderá ser acessado fora A.printVar() ... afinal, as funções são criadas para reutilização, não há poder especial dado às instruções internas.

O jogo é diferente quando há um compilador envolvido ( privacidade é um conceito no nível do compilador ). Ele conhece a definição de classe com modificadores de controle de acesso, para que possa errar se as regras não estiverem sendo seguidas no momento da compilação

watashiSHUN
fonte
3
Em suma, este não é o encapsulamento
watashiSHUN
2
Gostaria de saber se o PHP tem algo semelhante com suas variáveis ​​privadas patetas - já que variáveis ​​privadas realmente não fazem sentido na linguagem interpretada - quero dizer que otimização ele pode fazer sabendo que x variável é privada, se não for compilada?
NoBugs
1
Como podemos aleatorizar o padrão de variáveis ​​privadas?
crisron
@crisron mesma pergunta
IanS 17/02
5
@watashiSHUN "em suma, isso não é encapsulamento" => sim, é. O encapsulamento é apenas sobre o uso da API pública, para que o código do cliente seja protegido contra alterações na implementação. As convenções de nomenclatura são uma maneira perfeitamente válida de dizer o que é API e o que é implementação, e o ponto é que simplesmente funciona.
Bruno desthuilliers
30

Como mencionado corretamente por muitos dos comentários acima, não vamos esquecer o objetivo principal dos Modificadores de Acesso: Ajudar os usuários de código a entender o que deve mudar e o que não deve. Quando você vê um campo particular, não brinca com ele. Portanto, é principalmente o açúcar sintático que é facilmente alcançado no Python pelos _ e __.

Ardavan
fonte
4
Eu acho que esse é um ponto tão importante quanto qualquer outro. Ao depurar código (eu sei, sou um fraco para introduzir bugs), saber quais classes podem alterar uma variável de membro simplifica o processo de depuração. Pelo menos, se a variável estiver protegida por algum escopo. Um conceito semelhante é const funções em C ++. Eu sei que as variáveis ​​de membro não foram alteradas lá e, portanto, nem considero esse método a causa potencial de uma configuração incorreta de variável. Embora possa facilitar o desenvolvimento subseqüente de extensões de classe / adicionar recursos, limitar a visibilidade do código facilita a depuração.
MrMas
18

Há uma variação de variáveis ​​privadas na convenção de sublinhado.

In [5]: class Test(object):
   ...:     def __private_method(self):
   ...:         return "Boo"
   ...:     def public_method(self):
   ...:         return self.__private_method()
   ...:     

In [6]: x = Test()

In [7]: x.public_method()
Out[7]: 'Boo'

In [8]: x.__private_method()
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-8-fa17ce05d8bc> in <module>()
----> 1 x.__private_method()

AttributeError: 'Test' object has no attribute '__private_method'

Existem algumas diferenças sutis, mas por uma questão de pureza ideológica de padrão de programação, é bom o suficiente.

Existem exemplos de decoradores privados que implementam mais de perto o conceito, mas o YMMV. Indiscutivelmente, também se poderia escrever uma definição de classe que use meta

Shayne
fonte
14
Sei que isso é muito tarde para a festa, mas esse link aparece no google ao pesquisar o problema no Google. Isso não conta a história toda. __xcomo uma variável dentro da classe Aé realmente reescrita pelo compilador _A__x, ainda não é totalmente privada e ainda pode ser acessada.
Zorf
1
Obviamente, se eu _A__xvir uma variável chamada , não vou tocá-la. Pode ser contagioso. Eu vou fugir disso.
Mateen Ulhaq
Certamente não é um verdadeiro privado. Mas o principal raciocínio para o privado forçado em C ++ e Java (etc), otimização do compilador, não existe realmente no Python, portanto o privado por convenção é bom o suficiente. A convenção do Python geralmente é que confia que você se comportará sem supervisão. (E sua armadilha um novato, mas você sabe, basta ser pensativo sobre design de classe e consumo)
Shayne
14

"Em java, aprendemos sobre variáveis ​​públicas / privadas / protegidas"

"Por que isso não é necessário em python?"

Pelo mesmo motivo, não é necessário em Java.

Você é livre para usar - ou não usar privatee protected.

Como programador de Python e Java, descobri isso privatee protectedsão conceitos de design muito, muito importantes. Mas, como uma questão prática, em dezenas de milhares de linhas de Java e Python, eu nunca realmente usado privateou protected.

Por que não?

Aqui está minha pergunta "protegida de quem?"

Outros programadores da minha equipe? Eles têm a fonte. O que significa protegido quando eles podem mudar isso?

Outros programadores em outras equipes? Eles trabalham para a mesma empresa. Eles podem - com um telefonema - obter a fonte.

Clientes? É uma programação de trabalho contratado (geralmente). Os clientes (geralmente) possuem o código.

Então, de quem - exatamente - eu estou protegendo?

S.Lott
fonte
119
-1: Eu concordo com Porculus. Não se trata de proibir o acesso ou ocultar algo, mas de documentação implícita da API. Os desenvolvedores, bem como os compiladores / intérpretes / verificador de código, vêem facilmente quais membros são recomendados para uso e quais não devem ser tocados (ou pelo menos com cuidado). Na maioria dos casos, seria uma bagunça horrível se todos os membros de uma classe ou módulo fossem públicos. Considere a distinção de membros privados / protegidos / públicos como um serviço, dizendo: "Ei, esses membros são importantes enquanto são usados ​​internamente e provavelmente não são úteis para você".
Oben Sonne
7
@ S.Lott: Concordo que os documentos da API têm prioridade mais alta e geralmente são a única maneira de comunicar os usos pretendidos de uma API. Às vezes, porém, os nomes e a visibilidade dos membros (em termos de privado / público) falam por si só. Além disso, entendo que a ideia de documentação implícita não funciona bem em editores sem inspeção de API, mas é realmente útil em IDEs com conclusão de código. Supondo que você já leu os documentos da API há algum tempo, ajuda a lembrar como usar uma classe. As coisas não funcionariam tão bem se não houvesse distinção entre membros privados e públicos.
Oben Sonne
21
Tarde para a discussão, mas tudo Porculus e Oben está solicitando aqui é tratado perfeitamente adequadamente pela "prefixo com um sublinhado" convenção (e sem os danos que a execução do compilador da referida convenção pode causar)
ncoghlan
38
@ S.Lott Eu não sou um cara de python, então não vou comentar dessa perspectiva. No entanto, como desenvolvedor de Java, este é um conselho realmente horripilante. -1
dbyrne 6/06/2014
11
Uau. Você perde completamente o objetivo, dá um péssimo conselho, insulta qualquer um que discorde de você nesse ponto, mas ainda recebe distintivos e mais de 1000 pontos de reputação por essa "resposta".
Eric Duminil
12

Como mencionado anteriormente, você pode indicar que uma variável ou método é privado, prefixando-o com um sublinhado. Se você não acha que isso é suficiente, sempre pode usar o propertydecorador. Aqui está um exemplo:

class Foo:

    def __init__(self, bar):
        self._bar = bar

    @property
    def bar(self):
        """Getter for '_bar'."""
        return self._bar

Dessa forma, alguém ou algo que faz referência barestá realmente fazendo referência ao valor de retorno da barfunção em vez da própria variável e, portanto, pode ser acessada, mas não alterada. No entanto, se alguém realmente quisesse, poderia simplesmente usar _bare atribuir um novo valor a ele. Não existe uma maneira segura de impedir que alguém acesse variáveis ​​e métodos que você deseja ocultar, como já foi dito repetidamente. No entanto, usar propertyé a mensagem mais clara que você pode enviar informando que uma variável não deve ser editada. propertytambém pode ser usado para caminhos de acesso getter / setter / deleter mais complexos, conforme explicado aqui: https://docs.python.org/3/library/functions.html#property

Isaac Saffold
fonte
O Django também aprecia isso.
babygame0ver
10

O Python possui suporte limitado para identificadores particulares, por meio de um recurso que precede automaticamente o nome da classe a qualquer identificador começando com dois sublinhados. Isso é transparente para o programador, na maioria das vezes, mas o efeito líquido é que quaisquer variáveis ​​nomeadas dessa maneira podem ser usadas como variáveis ​​privadas.

Veja aqui para mais informações.

Em geral, a implementação de orientação a objetos do Python é um pouco primitiva em comparação com outras linguagens. Mas eu gosto disso, na verdade. É uma implementação muito conceitualmente simples e se encaixa bem no estilo dinâmico da linguagem.

Dan Olson
fonte
Sim. A beleza é que as habilidades de metaprogramação do python significam que você pode realmente implementar coisas sofisticadas, se quiser (e existem bibliotecas que implementam decoradores e outras coisas com @ private / @ protected / etc. Inferno, eu até vi uma biblioteca que implementava classes de protótipos no estilo JS por nenhuma maldita razão sã), mas, na prática, isso não é tão necessário. Eu meio que odeio o meme "python / js / o que é um cisco" porque quase nunca é verdade, mas o python compartilha 'costeletas de metaprogramação combinadas com sintaxe simples "com essa língua
Shayne
8

O único momento em que uso variáveis ​​privadas é quando preciso fazer outras coisas ao escrever ou ler a partir da variável e, como tal, preciso forçar o uso de um setter e / ou getter.

Novamente, isso vai para a cultura, como já foi dito. Eu tenho trabalhado em projetos em que a leitura e a escrita de outras variáveis ​​de classe eram gratuitas para todos. Quando uma implementação ficou obsoleta, demorou muito mais tempo para identificar todos os caminhos de código que usavam essa função. Quando o uso de setters e getters foi forçado, uma instrução de depuração poderia ser facilmente escrita para identificar que o método descontinuado havia sido chamado e o caminho do código que o chama.

Quando você está em um projeto em que qualquer pessoa pode escrever uma extensão, notificar os usuários sobre métodos descontinuados que devem desaparecer em algumas versões é vital para manter a quebra do módulo no mínimo durante as atualizações.

Então minha resposta é; se você e seus colegas mantêm um conjunto de códigos simples, nem sempre é necessário proteger as variáveis ​​de classe. Se você estiver escrevendo um sistema extensível, torna-se imperativo quando são feitas alterações no núcleo que precisam ser capturadas por todas as extensões usando o código.

BlueEagle
fonte
8

Desculpe pessoal por "ressuscitar" o tópico, mas espero que isso ajude alguém:

No Python3, se você quiser apenas "encapsular" os atributos da classe, como em Java, você pode fazer o mesmo:

class Simple:
    def __init__(self, str):
        print("inside the simple constructor")
        self.__s = str

    def show(self):
        print(self.__s)

    def showMsg(self, msg):
        print(msg + ':', self.show())

Para instanciar isso, faça:

ss = Simple("lol")
ss.show()

Observe que: print(ss.__s)lançará um erro.

Na prática, o Python3 ofuscará o nome do atributo global. Transformando isso como um atributo "privado", como em Java. O nome do atributo ainda é global, mas de maneira inacessível, como um atributo privado em outros idiomas.

Mas não tenha medo disso. Não importa. Também faz o trabalho. ;)

Ferrarezi
fonte
2
isso existe desde o Python 1.5.2 IIRC e ainda não impede o acesso ao atributo através de seu nome desconfigurado.
Bruno desthuilliers
7

conceitos privados e protegidos são muito importantes. Mas python - apenas uma ferramenta para prototipagem e desenvolvimento rápido com recursos restritos disponíveis para desenvolvimento, é por isso que alguns dos níveis de proteção não são tão rigorosos seguidos no python. Você pode usar "__" no membro da classe, ele funciona corretamente, mas não parece bom o suficiente - cada acesso a esse campo contém esses caracteres.

Além disso, você pode notar que o conceito OOP do python não é perfeito, smaltalk ou ruby ​​muito mais próximo do conceito OOP puro. Até C # ou Java estão mais próximos.

Python é uma ferramenta muito boa. Mas é uma linguagem OOP simplificada. Sintaticamente e conceitualmente simplificado. O principal objetivo da existência do python é trazer aos desenvolvedores a possibilidade de escrever código fácil de ler com alto nível de abstração de uma maneira muito rápida.

user711294
fonte
O rearson Private e Protected são importantes: em linguagens compiladas estaticamente, o compilador pode criar chamadas diretas para o método privado, mas deve contar com uma tabela de pesquisa para métodos públicos. Isso simplesmente não é um problema com linguagens dinâmicas. Finalmente, linguagens como C ++, existem implicações para herança e resolução de métodos. Python e Ruby têm implementações muito similares de OO, portanto, a comparação não faz sentido. O Smalltalk, na verdade, não tem noção de mensagens públicas / privadas. Você é livre para adicionar privado como categoria, mas é puramente consultivo.
Shayne
Para aprofundar minha afirmação. De um ponto de vista de higiene de codificação, sim, eles são importantes para o encapsulamento, mas não é necessário para isso; portanto, os decoradores @private (etc) são mais consultivos do que qualquer coisa, mas como privado / público não adiciona nada útil à otimização em um ambiente. linguagem não estático, não é implementado em um nível profundo como seria em uma linguagem compilada como Java ou C
Shayne
4

Python não possui nenhuma variável privada como C ++ ou Java. Você também pode acessar qualquer variável de membro a qualquer momento, se desejar. No entanto, você não precisa de variáveis ​​privadas no Python, porque no Python não é ruim expor as variáveis ​​de membro de suas classes. Se você precisar encapsular uma variável de membro, poderá fazer isso usando "@property" posteriormente, sem quebrar o código do cliente existente.

Em python, o sublinhado único "_" é usado para indicar que um método ou variável não é considerado parte da API pública de uma classe e que essa parte da API pode mudar entre versões diferentes. Você pode usar esses métodos / variáveis, mas seu código pode ser quebrado se você usar uma versão mais recente dessa classe.

O sublinhado duplo "__" não significa uma "variável privada". Você o utiliza para definir variáveis ​​que são "classe local" e que não podem ser facilmente substituídas por subclasses. Ele gerencia o nome das variáveis.

Por exemplo:

class A(object):
    def __init__(self):
        self.__foobar = None # will be automatically mangled to self._A__foobar

class B(A):
    def __init__(self):
        self.__foobar = 1 # will be automatically mangled to self._B__foobar

self .__ foobar nome é automaticamente mutilado para self._A__foobar na classe A. Na classe B é mutilado para self._B__foobar. Portanto, cada subclasse pode definir sua própria variável __foobar sem substituir as variáveis-mãe. Mas nada impede que você acesse variáveis ​​que começam com sublinhados duplos. No entanto, a manipulação de nomes impede que você chame essas variáveis ​​/ métodos por acaso.

Eu recomendo assistir Raymond Hettingers falar sobre "Pythons class development toolkit" do Pycon 2013 (deve estar disponível no Youtube), o que dá um bom exemplo de por que e como você deve usar as variáveis ​​@property e "__" - instance.

Hatatister
fonte
Vou dar uma olhada nessa conversa. Isso faz @propertyparte do Python padrão ou é específico para um IDE?
precisa saber é o seguinte
Faz parte do padrão desde python 2.6. Se você deve usar uma versão mais antiga, ainda existe a possibilidade de usar a propertyfunção embutida, que está disponível desde o python 2.2.
Hatatister
0

Na verdade, você pode simular um C#getter e um setter usando este truque simples:

class Screen(object):

    def getter_setter_y(self, y, get=True):
        if get is True:
            Screen.getter_setter_y.value = y
        else:
            return Screen.getter_setter_y.value

     def getter_setter_x(self, x, get=True):
         if get is True:
             Screen.getter_setter_x.value = x
         else:
             return Screen.getter_setter_x.value

Em seguida, use-o semelhante como em C#:

scr = Screen()
scr.getter_setter_x(100)
value =  scr.getter_setter_x(0, get=False)
print (value)

É apenas declarar uma variável local estática em uma função que desempenhará uma função get / set, pois essa é a única maneira de compartilhar uma variável por meio de métodos get e set, sem torná-la global para uma classe ou arquivo.

Ilian Zapryanov
fonte