Definindo funções de módulo privado em python

238

De acordo com http://www.faqs.org/docs/diveintopython/fileinfo_private.html :

Como a maioria das linguagens, o Python tem o conceito de elementos privados:

  • Funções privadas, que não podem ser chamadas de fora do módulo

No entanto, se eu definir dois arquivos:

#a.py
__num=1

e:

#b.py
import a
print a.__num

quando eu executo b.pyele imprime 1sem dar nenhuma exceção. O diveintopython está errado ou entendi algo errado? E há alguma maneira de se definir a função de um módulo como privado?

olamundo
fonte
Não é que o diveintopython esteja errado, mas no exemplo deles: >>> import fileinfo >>> m = fileinfo.MP3FileInfo() >>> m.__parse("/music/_singles/kairo.mp3") 1 Traceback (innermost last): File "<interactive input>", line 1, in ? AttributeError: 'MP3FileInfo' instance has no attribute '__parse' fileinfo.MP3FileInfo () é uma instância da classe. O que gera essa exceção quando você usa sublinhado duplo. Enquanto no seu caso, você não criou uma classe, apenas criou um módulo. Veja também: stackoverflow.com/questions/70528/…
Homero Esmeraldo

Respostas:

323

No Python, "privacidade" depende dos níveis de "consentimento dos adultos" - você não pode forçá- lo (assim como na vida real ;-). Um único sublinhado à esquerda significa que você não deve acessá-lo "de fora" - dois sublinhados à esquerda (sem sublinhados à direita) transmitem a mensagem com mais força ainda ... mas, no final, ainda depende do social convenção e consenso: a introspecção do Python é forte o suficiente para que você não possa algemar todos os outros programadores do mundo para respeitar seus desejos.

((Btw, embora seja um segredo bem guardado, o mesmo vale para C ++: na maioria dos compiladores, uma #define private publiclinha simples antes de #includeinserir seu .harquivo é tudo o que é necessário para codificadores espertos fazerem hash de sua "privacidade" ...! -) )

Alex Martelli
fonte
82
Sua observação no C ++ está incorreta. Usando #define private public, você está alterando o código que é enviado ao compilador, que é onde o nome é alterado.
rhinoinrepose
14
Além disso, a distorção em C ++ é obscura, mas dificilmente secreta. Você também pode "introspectar" um binário produzido pelo C ++. OT, desculpe.
Prof. Falken
47
Como uma atualização para @rhinoinrepose, não é apenas incorreto, é um comportamento indefinido de acordo com o padrão para redefinir uma palavra-chave com uma macro de pré-processador.
Cory Kramer
3
@AlexMartelli Não é static void foo()tão privado quanto possível. Pelo menos, está oculto para o vinculador, e a função pode ser removida totalmente por inlining.
user877329
3
Na vida real, as pessoas são processados se quebrar as leis
Lay González
288

Pode haver confusão entre o privado da classe e o privado do módulo .

Um módulo privado começa com um sublinhado.
Esse elemento não é copiado ao usar o from <module_name> import *formulário do comando import; no entanto, é importado se você estiver usando a import <moudule_name>sintaxe ( consulte a resposta de Ben Wilhelm )
Simplesmente remova um sublinhado do a .__ num do exemplo da pergunta e não será exibido nos módulos que importam a.py usando a from a import *sintaxe.

Uma classe privada começa com dois sublinhados (também conhecido como dunder, ou seja, sub-pontuação d-ouble).
Essa variável tem o nome "desconfigurado" para incluir o nome da classe, etc.
Ela ainda pode ser acessada fora da lógica da classe, através do nome desconfigurado.
Embora o nome errado possa servir como um dispositivo de prevenção moderado contra acesso não autorizado, seu principal objetivo é evitar possíveis colisões de nomes com membros da classe das classes ancestrais. Veja a referência engraçada, mas precisa, de Alex Martelli a adultos que consentem, enquanto ele descreve a convenção usada em relação a essas variáveis.

>>> class Foo(object):
...    __bar = 99
...    def PrintBar(self):
...        print(self.__bar)
...
>>> myFoo = Foo()
>>> myFoo.__bar  #direct attempt no go
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'Foo' object has no attribute '__bar'
>>> myFoo.PrintBar()  # the class itself of course can access it
99
>>> dir(Foo)    # yet can see it
['PrintBar', '_Foo__bar', '__class__', '__delattr__', '__dict__', '__doc__', '__
format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__',
'__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__
', '__subclasshook__', '__weakref__']
>>> myFoo._Foo__bar  #and get to it by its mangled name !  (but I shouldn't!!!)
99
>>>
mjv
fonte
Bem, TIL. Existe alguma razão para eles não aplicarem o nível de módulo __private_function? Eu me deparei com isso e cometi erros por causa disso.
Santa
Obrigado pelas amáveis ​​palavras @Terrabits, mas fico feliz em ficar em segundo com Alex (bem atrás!) Em todas as coisas 'Python'. Além disso, suas respostas são tipicamente mais concisas e bem-humoradas, mantendo um alto nível de autoridade, dadas as extensas contribuições de Alex ao idioma e à comunidade.
Mjv 5/05
1
@mjv Esta foi uma explicação muito útil! Obrigado! Fiquei bastante intrigado com esse comportamento por um tempo. Eu gostaria que a escolha tivesse sido lançar algum tipo de erro diferente de um AttributeError se você tentasse acessar diretamente a classe private; talvez um "PrivateAccessError" ou algo teria sido mais explícito / útil. (Como obter o erro de que ele não possui um atributo não é realmente verdadeiro).
HFBrowning
82

Esta pergunta não foi totalmente respondida, pois a privacidade do módulo não é puramente convencional e o uso da importação pode ou não reconhecer a privacidade do módulo, dependendo de como é usado.

Se você definir nomes particulares em um módulo, esses nomes serão importados para qualquer script que use a sintaxe 'import module_name'. Assim, supondo que você tenha definido corretamente em seu exemplo o módulo private, _num, em a.py, assim ..

#a.py
_num=1

..você poderia acessá-lo em b.py com o símbolo do nome do módulo:

#b.py
import a
...
foo = a._num # 1

Para importar apenas não privados do a.py, você deve usar a sintaxe from :

#b.py
from a import *
...
foo = _num # throws NameError: name '_num' is not defined

Por uma questão de clareza, no entanto, é melhor ser explícito ao importar nomes de módulos, em vez de importá-los todos com um '*':

#b.py
from a import name1 
from a import name2
...
Ben Wilhelm
fonte
1
onde você especifica quais funções / bibliotecas são importadas? no init .py?
FistOfFury 24/08
Não há risco de colisões de nomes quando _namessão invocadas import a- elas são acessos como a._namesquando se usa esse estilo.
Josiah Yoder
@FistOfFury Sim, você especifica as funções importadas no __init__.pyarquivo. Veja aqui para alguma ajuda nisso.
Mike Williamson
29

Python permite classe privada membros da com o prefixo de sublinhado duplo. Essa técnica não funciona no nível do módulo, então estou pensando que isso é um erro no Dive Into Python.

Aqui está um exemplo de funções de classe privada:

class foo():
    def bar(self): pass
    def __bar(self): pass

f = foo()
f.bar()   # this call succeeds
f.__bar() # this call fails
Andrew Hare
fonte
2
Acho que a intenção do OP é escrever funções que não são acessíveis fora de, por exemplo, um pacote comercial. A esse respeito, esta resposta não está completa. A função __bar () ainda pode ser acessada de fora através de f._foo__bar (). Portanto, os sublinhados duplos à esquerda não o tornam privado.
SevakPrime
24

Você pode adicionar uma função interna:

def public(self, args):
   def private(self.root, data):
       if (self.root != None):
          pass #do something with data

Algo assim se você realmente precisar desse nível de privacidade.

Ilian Zapryanov
fonte
9
Por que essa não é a melhor resposta?
Safay #
2

Essa é uma pergunta antiga, mas agora as variáveis ​​manipuladas do módulo privado (um sublinhado) e da classe privada (dois sublinhados) agora são abordadas na documentação padrão:

O tutorial do Python » Classes » Variáveis ​​privadas

user1338062
fonte
1

incorporado com fechamentos ou funções é uma maneira. Isso é comum em JS, embora não seja necessário para plataformas que não sejam de navegador ou trabalhadores de navegador.

No Python, parece um pouco estranho, mas se algo realmente precisa ser oculto, pode ser o caminho. Mais ao ponto de usar a API python e manter as coisas que precisam estar ocultas no C (ou outra linguagem) é provavelmente a melhor maneira. Na falta disso, eu colocaria o código dentro de uma função, chamando isso e retornando os itens que você deseja exportar.

Fama marciana
fonte
-11

O Python possui três modos via., Privado, público e protegido. Ao importar um módulo, apenas o modo público é acessível. Portanto, os módulos privados e protegidos não podem ser chamados de fora do módulo, ou seja, quando são importados.

sravan
fonte