Clase "privada" (implementación) en Python


106

Estoy codificando un pequeño módulo de Python compuesto por dos partes:

  • algunas funciones que definen una interfaz pública,
  • una clase de implementación utilizada por las funciones anteriores, pero que no es significativa fuera del módulo.

Al principio, decidí "ocultar" esta clase de implementación definiéndola dentro de la función que la usa, pero esto dificulta la legibilidad y no se puede usar si varias funciones reutilizan la misma clase.

Entonces, además de los comentarios y las cadenas de documentos, ¿existe algún mecanismo para marcar una clase como "privada" o "interna"? Soy consciente del mecanismo de subrayado, pero según tengo entendido, solo se aplica a las variables, la función y el nombre de los métodos.

Respuestas:


174

Utilice un prefijo de subrayado único:

class _Internal:
    ...

Esta es la convención oficial de Python para símbolos "internos"; "from module import *" no importa objetos con prefijo de subrayado.

Editar: referencia a la convención de subrayado único


3
No sabía que la regla de subrayado se extendía a las clases. No quiero saturar mi espacio de nombres al importar, por lo que este comportamiento es lo que estaba buscando. ¡Gracias!
oparisy

1
Dado que afirma que esta es la convención de Python "oficial", sería bueno con un enlace. Otra publicación aquí dice que todo es la forma oficial y tiene un enlace a la documentación.
Flodin

11
python.org/dev/peps/pep-0008 - _single_leading_underscore: indicador débil de "uso interno". Por ejemplo, "from M import *" no importa objetos cuyo nombre comience con un guión bajo. - Las clases para uso interno tienen un subrayado inicial
Miles

2
Un subrayado inicial es la convención para marcar cosas como internas, "no te metas con esto", mientras que todo es más para módulos diseñados para usarse con "from M import *", sin implicar necesariamente que los usuarios del módulo no deben tocar esa clase.
Miles

65

En breve:

  1. No puede hacer cumplir la privacidad . No hay clases / métodos / funciones privados en Python. Al menos, no la privacidad estricta como en otros lenguajes, como Java.

  2. Solo puede indicar / sugerir privacidad . Esto sigue una convención. La convención de Python para marcar una clase / función / método como privada es anteponerla con un _ (guión bajo). Por ejemplo, def _myfunc()o class _MyClass:. También puede crear pseudoprivacidad precediendo el método con dos guiones bajos (por ejemplo:) __foo. No puede acceder al método directamente, pero aún puede llamarlo a través de un prefijo especial usando el nombre de clase (por ejemplo:) _classname__foo. Por lo tanto, lo mejor que puede hacer es indicar / sugerir privacidad, no imponerla.

Python es como Perl a este respecto. Parafraseando una famosa línea sobre la privacidad del libro de Perl, la filosofía es que debes permanecer fuera de la sala de estar porque no fuiste invitado, no porque se defienda con una escopeta.

Para más información:


En respuesta al n. ° 1, puede hacer cumplir la privacidad de los métodos. Usar un guión bajo doble como __method (self) lo hará inaccesible fuera de la clase. Pero hay una forma de evitarlo llamándolo como, Foo () ._ Foo__method (). Supongo que simplemente lo renombra a algo más esotérico.
Evan Fosmark

1
Esa cita es precisa.
Paul Draper

37

Defina __all__, una lista de nombres que desea exportar ( ver documentación ).

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

10

Un patrón que a veces uso es este:

Definir una clase:

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

Cree una instancia de la clase, sobrescribiendo el nombre de la clase:

x = x()

Defina símbolos que expongan la funcionalidad:

doThis = x.doThis
doThat = x.doThat

Elimina la propia instancia:

del x

Ahora tienes un módulo que solo expone tus funciones públicas.


2
Me tomó un minuto entender el propósito / función de "sobrescribir el nombre de la clase", pero obtuve una gran sonrisa cuando lo hice. No estoy seguro de cuándo lo usaré. :)
Zach Young

¿Existe un nombre para esta técnica?
Bishwas Mishra


4

Para abordar el tema de las convenciones de diseño, y como dijo Christopher, realmente no existe algo como "privado" en Python. Esto puede sonar retorcido para alguien que proviene de C / C ++ (como yo hace un tiempo), pero eventualmente, probablemente se dará cuenta de que seguir las convenciones es suficiente.

Ver algo que tiene un guión bajo al frente debería ser una buena pista para no usarlo directamente. Si le preocupa el desorden de la help(MyClass)salida (que es lo que todos miran cuando buscan cómo usar una clase), los atributos / clases subrayados no se incluyen allí, por lo que terminará con su interfaz "pública" descrita.

Además, tener todo lo público tiene sus propias ventajas increíbles, como, por ejemplo, puede realizar pruebas unitarias prácticamente cualquier cosa desde el exterior (lo que realmente no puede hacer con las construcciones privadas de C / C ++).


4

Utilice dos guiones bajos para prefijar nombres de identificadores "privados". Para las clases de un módulo, utilice un solo subrayado inicial y no se importarán utilizando "from module import *".

class _MyInternalClass:
    def __my_private_method:
        pass

(No existe el verdadero "privado" en Python. Por ejemplo, Python simplemente modifica automáticamente los nombres de los miembros de la clase con guiones bajos dobles para que sean __clssname_mymember. Entonces, en realidad, si conoce el nombre mutilado, puede usar la entidad "privada" de todos modos . Vea aquí. Y, por supuesto, puede elegir importar manualmente clases "internas" si lo desea).


¿Por qué dos _? Uno es suficiente.
S.Lott

Un solo subrayado es suficiente para evitar que la "importación" importe clases y funciones. Necesita dos guiones bajos para inducir la función de alteración de nombres de Python. Debería haber sido más claro; Yo edité.
chroder

Ahora, el seguimiento. ¿Por qué nombrar destrozar? ¿Cuál es el posible beneficio de eso?
S.Lott

5
El posible beneficio es molestar a otros desarrolladores que necesitan acceso a esas variables :)
Richard Levasseur
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.