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?


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" ...! -) )


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
>>>

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
...

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

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.


9
Por que essa não é a melhor resposta?
Safay #


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.


-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.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.