Definición de funciones de módulos privados en python


238

De acuerdo con http://www.faqs.org/docs/diveintopython/fileinfo_private.html :

Como la mayoría de los lenguajes, Python tiene el concepto de elementos privados:

  • Funciones privadas, que no se pueden llamar desde fuera de su módulo

Sin embargo, si defino dos archivos:

#a.py
__num=1

y:

#b.py
import a
print a.__num

cuando lo ejecuto b.pyse imprime 1sin dar ninguna excepción. ¿Está mal Diveintopython o no he entendido bien algo? ¿Y hay alguna manera de no definir la función de un módulo como privada?


No es que diveintopython esté mal, pero en su ejemplo: >>> 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 () es una instancia de clase. Lo que da esta excepción cuando usa doble guión bajo. Mientras que en su caso, no creó una clase, simplemente creó un módulo. Ver también: stackoverflow.com/questions/70528/...
Homero Esmeraldo

Respuestas:


323

En Python, la "privacidad" depende de los niveles de acuerdo de los "adultos que consienten": no puede forzarla (más de lo que puede en la vida real ;-). Un solo guión bajo significa que no se debe acceder a él "desde afuera": dos guiones bajos (sin guiones bajos) llevan el mensaje aún más enérgicamente ... pero, al final, aún depende de las redes sociales convención y consenso: la introspección de Python es lo suficientemente fuerte como para que no puedas esposar a cualquier otro programador del mundo para respetar tus deseos.

((Por cierto, aunque es un secreto muy guardado, lo mismo ocurre con C ++: con la mayoría de los compiladores, una simple #define private publiclínea antes de #includeingresar su .harchivo es todo lo que necesitan los codificadores astutos para hacer hash de su "privacidad" ... -) )


82
Su nota sobre C ++ es incorrecta. Al usar #define private public, está cambiando el código que se envía al compilador, que es donde tiene lugar el cambio de nombre.
Rhinoinrepose 05 de

14
Además, la destrucción de C ++ es oscura, pero apenas secreta. También puede "introspectar" un binario producido por C ++. OT, lo siento.
Prof. Falken

47
Como actualización de @rhinoinrepose, no solo es incorrecto, es un comportamiento indefinido según el estándar para redefinir una palabra clave con una macro de preprocesador.
Cory Kramer

3
@AlexMartelli no es static void foo()tan privado como parece. Al menos está oculto para el vinculador, y la función se puede eliminar por completo mediante la incorporación.
user877329

3
En la vida real, las personas son procesadas si violan las leyes
Lay González

289

Puede haber confusión entre las clases privadas y las módulos privadas .

Un módulo privado comienza con un guión bajo.
Este elemento no se copia cuando se utiliza el from <module_name> import *formulario del comando de importación; sin embargo, se importa si se usa la import <moudule_name>sintaxis ( vea la respuesta de Ben Wilhelm )
Simplemente elimine un guión bajo del número a .__ del ejemplo de la pregunta y no se mostrará en los módulos que importan a.py usando la from a import *sintaxis.

Una clase privada comienza con dos guiones bajos (también conocido como dunder, es decir, d-ouble under-score).
Dicha variable tiene su nombre "mutilado" para incluir el nombre de la clase, etc.
Todavía se puede acceder fuera de la lógica de la clase, a través del nombre mutilado.
Aunque el cambio de nombre puede servir como un dispositivo de prevención leve contra el acceso no autorizado, su objetivo principal es evitar posibles colisiones de nombres con miembros de la clase de antepasados. Vea la referencia divertida pero precisa de Alex Martelli a los adultos que consienten mientras describe la convención utilizada con respecto a estas variables.

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

Bueno, TIL. Sin embargo, ¿hay alguna razón por la que no imponen el nivel de módulo __private_function? Me encontré con esto y me equivoqué por eso.
Santa

Gracias por las amables palabras @Terrabits, pero con mucho gusto estoy detrás de Alex (¡muy por detrás!) En todas las cosas de 'Python'. Además, sus respuestas suelen ser más concisas y humorísticas, a la vez que conservan un alto nivel de autoridad dada las amplias contribuciones de Alex al idioma y la comunidad.
mjv

1
@mjv ¡Esta fue una explicación muy útil! ¡Gracias! He estado bastante desconcertado sobre este comportamiento por un tiempo. Desearía que la opción hubiera sido arrojar algún tipo de error que no sea AttributeError si intentaste acceder directamente a la clase privada; quizás un "PrivateAccessError" o algo hubiera sido más explícito / útil. (Dado que obtener el error de que no tiene un atributo no es realmente cierto).
HFBrowning

82

Esta pregunta no se respondió completamente, ya que la privacidad del módulo no es puramente convencional, y dado que el uso de importación puede o no reconocer la privacidad del módulo, dependiendo de cómo se use.

Si define nombres privados en un módulo, esos nombres se importarán a cualquier script que use la sintaxis, 'import module_name'. Por lo tanto, suponiendo que haya definido correctamente en su ejemplo el módulo privado, _num, en a.py, así ...

#a.py
_num=1

... podría acceder a él en b.py con el símbolo del nombre del módulo:

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

Para importar solo no privados de a.py, debe usar la sintaxis from :

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

Sin embargo, en aras de la claridad, es mejor ser explícito al importar nombres de módulos, en lugar de importarlos todos con un '*':

#b.py
from a import name1 
from a import name2
...

1
¿Dónde especifica qué funciones / bibliotecas se importan? en el init .py?
FistOfFury

No hay riesgo de colisiones de nombres cuando _namesse invoca con import aellos; son accesos como a._namescuando se usa este estilo.
Josiah Yoder

@FistOfFury Sí, usted especifica las funciones importadas en el __init__.pyarchivo. Vea aquí para obtener ayuda sobre eso.
Mike Williamson el

29

Python permite miembros de clases privadas con el doble prefijo de subrayado. Esta técnica no funciona a nivel de módulo, así que creo que es un error en Dive Into Python.

Aquí hay un ejemplo de funciones de clase privada:

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

f = foo()
f.bar()   # this call succeeds
f.__bar() # this call fails

2
Creo que la intención del OP es escribir funciones que no sean accesibles fuera de, por ejemplo, un paquete comercial. En ese sentido, esta respuesta no está completa. La función __bar () todavía es accesible desde afuera a través de f._foo__bar (). Por lo tanto, los guiones bajos de doble punta no lo hacen privado.
SevakPrime

24

Puedes agregar una función interna:

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

Algo así si realmente necesitas ese nivel de privacidad.


99
¿Por qué esta no es la mejor respuesta?
safay


1

incrustado con cierres o funciones es unidireccional. Esto es común en JS, aunque no es necesario para las plataformas que no son del navegador o para los trabajadores del navegador.

En Python parece un poco extraño, pero si algo realmente necesita estar oculto, esa podría ser la forma. Más concretamente, usar la API de Python y mantener las cosas que requieren estar ocultas en la C (u otro lenguaje) es probablemente la mejor manera. De lo contrario, iría por poner el código dentro de una función, llamarlo y hacer que devuelva los elementos que desea exportar.


-11

Python tiene tres modos: privado, público y protegido. Al importar un módulo, solo se puede acceder al modo público. Por lo tanto, los módulos privados y protegidos no se pueden llamar desde fuera del módulo, es decir, cuando se importa.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.