Classe “privada” (implementação) em Python

107

Estou codificando um pequeno módulo Python composto de duas partes:

  • algumas funções que definem uma interface pública,
  • uma classe de implementação usada pelas funções acima, mas que não é significativa fora do módulo.

No início, decidi "ocultar" essa classe de implementação definindo-a dentro da função que a usa, mas isso dificulta a legibilidade e não pode ser usado se várias funções reutilizarem a mesma classe.

Portanto, além de comentários e docstrings, existe um mecanismo para marcar uma classe como "privada" ou "interna"? Estou ciente do mecanismo de sublinhado, mas pelo que entendi, ele só se aplica a variáveis, funções e nomes de métodos.

oparisia
fonte

Respostas:

175

Use um único prefixo de sublinhado:

class _Internal:
    ...

Esta é a convenção oficial do Python para símbolos 'internos'; "from module import *" não importa objetos com prefixo sublinhado.

Editar: Referência à convenção de sublinhado único

Ferdinand Beyer
fonte
3
Eu não conhecia a regra de sublinhado estendida às classes. Não quero sobrecarregar meu namespace ao importar, então esse comportamento é o que eu estava procurando. Obrigado!
oparisy de
1
Já que você afirma que esta é a convenção python "oficial", seria bom ter um link. Outro post aqui diz que tudo é da forma oficial e faz link para a documentação.
flodin de
11
python.org/dev/peps/pep-0008 - _single_leading_underscore: indicador de "uso interno" fraco. Por exemplo, "de M import *" não importa objetos cujo nome comece com um sublinhado. - Classes para uso interno têm um sublinhado principal
Milhas de
2
Um sublinhado principal é a convenção para marcar as coisas como internas, "não mexa com isso", enquanto tudo é mais para módulos projetados para serem usados ​​com "de M import *", sem necessariamente implicar que os usuários do módulo não devem tocar essa classe.
Milhas de
65

Em resumo:

  1. Você não pode impor privacidade . Não há classes / métodos / funções privadas em Python. Pelo menos, não a privacidade estrita como em outras linguagens, como Java.

  2. Você só pode indicar / sugerir privacidade . Isso segue uma convenção. A convenção python para marcar uma classe / função / método como privada é precedê-la com um _ (sublinhado). Por exemplo, def _myfunc()ou class _MyClass:. Você também pode criar pseudo-privacidade precedendo o método com dois sublinhados (por exemplo:) __foo. Você não pode acessar o método diretamente, mas ainda pode chamá-lo por meio de um prefixo especial usando o nome da classe (por exemplo:) _classname__foo. Portanto, o melhor que você pode fazer é indicar / sugerir privacidade, não aplicá-la.

Python é como perl nesse aspecto. Parafraseando uma frase famosa sobre privacidade do livro Perl, a filosofia é que você deve ficar fora da sala porque não foi convidado, não porque ela é defendida com uma espingarda.

Para maiores informações:

Karl Fast
fonte
Em resposta ao nº 1, você meio que pode impor a privacidade dos métodos. Usar um sublinhado duplo como __method (self) tornará inacessível fora da classe. Mas há uma maneira de contornar isso chamando-o de Foo () ._ Foo__method (). Eu acho que apenas muda o nome para algo mais esotérico.
Evan Fosmark
1
Essa citação é exata.
Paul Draper,
37

Defina __all__uma lista de nomes que deseja exportar ( consulte a documentação ).

__all__ = ['public_class'] # don't add here the 'implementation_class'
UncleZeiv
fonte
10

Um padrão que às vezes uso é este:

Defina uma classe:

class x(object):
    def doThis(self):
        ...
    def doThat(self):
        ...

Crie uma instância da classe, substituindo o nome da classe:

x = x()

Defina os símbolos que expõem a funcionalidade:

doThis = x.doThis
doThat = x.doThat

Exclua a própria instância:

del x

Agora você tem um módulo que apenas expõe suas funções públicas.

Theller
fonte
2
Levei um minuto para entender o propósito / função de "sobrescrever o nome da classe", mas recebi um grande sorriso quando o fiz. Não tenho certeza de quando vou usá-lo. :)
Zach Young
Existe um nome para esta técnica?
Bishwas Mishra
8

A convenção é anexar "_" a classes, funções e variáveis ​​internas.

Benjamin Peterson
fonte
4

Para abordar a questão das convenções de design, e como disse Christopher, não existe realmente "privado" em Python. Isso pode soar distorcido para alguém vindo da formação C / C ++ (como eu há um tempo atrás), mas eventualmente, você provavelmente perceberá que seguir as convenções é o bastante.

Ver algo com um sublinhado na frente deve ser uma dica boa o suficiente para não usá-lo diretamente. Se você está preocupado com a help(MyClass)produção desordenada (que é o que todos olham ao pesquisar sobre como usar uma classe), os atributos / classes sublinhados não estão incluídos lá, então você acabará tendo apenas sua interface "pública" descrita.

Além disso, ter tudo público tem suas próprias vantagens incríveis, como, por exemplo, você pode testar a unidade de praticamente qualquer coisa de fora (o que você realmente não pode fazer com construções privadas C / C ++).

Dimitri Tcaciuc
fonte
4

Use dois sublinhados para prefixar nomes de identificadores "privados". Para classes em um módulo, use um único sublinhado inicial e eles não serão importados usando "do módulo import *".

class _MyInternalClass:
    def __my_private_method:
        pass

(Não existe algo como "privado" verdadeiro em Python. Por exemplo, Python apenas altera automaticamente os nomes dos membros da classe com sublinhados duplos __clssname_mymember. Então, realmente, se você souber o nome mutilado, poderá usar a entidade "privada" de qualquer maneira . Veja aqui. E, claro, você pode escolher importar manualmente as classes "internas", se desejar.

chroder
fonte
Por que dois _'s? Um é suficiente.
S.Lott de
Um único sublinhado é suficiente para evitar que "import" importe classes e funções. Você precisa de dois sublinhados para induzir o recurso de mutilação de nome do Python. Deveria ter sido mais claro; Eu editei.
chroder de
Agora, o acompanhamento. Por que nome mutilado? Qual é o possível benefício disso?
S.Lott
5
O possível benefício é irritar outros desenvolvedores que precisam acessar essas variáveis ​​:)
Richard Levasseur