Con las propiedades de Python, puedo hacer que
obj.y
llama a una función en lugar de simplemente devolver un valor.
¿Hay alguna forma de hacer esto con módulos? Tengo un caso donde quiero
module.y
para llamar a una función, en lugar de simplemente devolver el valor almacenado allí.
python
properties
python-module
Josh Gibson
fuente
fuente
__getattr__
un módulo para obtener una solución más moderna.Respuestas:
Solo las instancias de clases de estilo nuevo pueden tener propiedades. Puede hacer que Python crea que una instancia de este tipo es un módulo guardándolo
sys.modules[thename] = theinstance
. Entonces, por ejemplo, su archivo de módulo m.py podría ser:fuente
types.ModuleType
como se muestra en la respuesta muy similar de @ Unknown?builtins.module
, que en sí es una instancia detype
(que es la definición de clase de estilo nuevo). El problema es que las propiedades deben estar en la clase, no en la instancia: si lo hacesf = Foo()
,f.some_property = property(...)
fallará de la misma manera que si lo pusieras ingenuamente en un módulo. La solución es ponerlo en la clase, pero como no quiere que todos los módulos tengan la propiedad, su subclase (vea la respuesta de Desconocido).globals()
(mantener las claves intactas pero restablecer los valores aNone
) al volver a enlazar el nombresys.modules
es un problema de Python 2: Python 3.4 funciona según lo previsto. Si necesita acceder al objeto de clase en Py2, agregue, por ejemplo,_M._cls = _M
justo después de laclass
declaración (o guárdelo de manera equivalente en algún otro espacio de nombres) y acceda a él comoself._cls
en los métodos que lo requieran (type(self)
podría estar bien, pero no si también realiza una subclase de_M
) .Haría esto para heredar correctamente todos los atributos de un módulo y ser identificado correctamente por isinstance ()
Y luego puede insertar esto en sys.modules:
fuente
__file__
que deben definirse manualmente, (2) las importaciones realizadas en el módulo que contiene la clase no serán "visibles" durante el tiempo de ejecución, etc ...types.ModuleType
, cualquier clase (estilo nuevo) servirá. ¿Exactamente qué atributos de módulo especiales esperas heredar?__init__
una instancia, y obtendrá el comportamiento correcto al usarisinstance
.Como PEP 562 se ha implementado en Python> = 3.7, ahora podemos hacer esto
archivo: module.py
uso:
Tenga en cuenta que si omite
raise AttributeError
en la__getattr__
función, significa que la función termina conreturn None
, entoncesmodule.nosuch
obtendrá un valor deNone
.fuente
Basado en la respuesta de John Lin :
Uso (observe el subrayado inicial), en
the_module.py
:Luego:
El guión bajo inicial es necesario para diferenciar la función con propiedad de la función original. No pude pensar en una forma de reasignar el identificador, ya que durante el tiempo de ejecución del decorador, aún no se ha asignado.
Tenga en cuenta que los IDE no sabrán que la propiedad existe y mostrarán ondas rojas.
fuente
@property def x(self): return self._x
creo quedef thing()
sin subrayado es más convencional. ¿Y puede crear un decorador de "establecedor de propiedades de módulo" en su respuesta también?def thing()
sugerencia. El problema es que__getattr__
solo se llama por atributos faltantes . Pero después de@module_property def thing(): …
ejecutar,the_module.thing
se define, por lo que nunca se llamará a getattr . Necesitamos registrarnos de alguna manerathing
en el decorador y luego eliminarlo del espacio de nombres del módulo. Intenté regresarNone
del decorador, pero luegothing
se define comoNone
. Se podría hacer,@module_property def thing(): … del thing
pero me parece peor que usarlothing()
como una función__getattribute__
". Gracias.Un caso de uso típico es: enriquecer un módulo (enorme) existente con algunos (pocos) atributos dinámicos, sin convertir todo el material del módulo en un diseño de clase. Desafortunadamente, un parche de clase de módulo más simple como
sys.modules[__name__].__class__ = MyPropertyModule
fallaTypeError: __class__ assignment: only for heap types
. Por tanto, es necesario volver a cablear la creación de módulos.Este enfoque lo hace sin los ganchos de importación de Python, solo con tener un prólogo encima del código del módulo:
Uso:
Nota: Algo como
from propertymodule import dynval
producirá una copia congelada, por supuesto, correspondiente adynval = someobject.dynval
fuente
Una respuesta corta: usa
proxy_tools
El
proxy_tools
paquete intenta proporcionar@module_property
funcionalidad.Se instala con
Utilizando una ligera modificación del ejemplo de @ Marein,
the_module.py
ponemosAhora de otro guión, puedo hacer
Comportamiento inesperado
Esta solución no está exenta de salvedades. Es decir, ¡ no
the_module.thing
es una cadena ! Es unproxy_tools.Proxy
objeto cuyos métodos especiales se han anulado para que imite una cadena. Aquí hay algunas pruebas básicas que ilustran el punto:Internamente, la función original se almacena en
the_module.thing._Proxy__local
:Pensamientos adicionales
Honestamente, estoy desconcertado acerca de por qué los módulos no tienen esta funcionalidad incorporada. Creo que el quid del asunto es que
the_module
es una instancia de latypes.ModuleType
clase. Establecer una "propiedad de módulo" equivale a establecer una propiedad en una instancia de esta clase, en lugar de en latypes.ModuleType
clase misma. Para obtener más detalles, consulte esta respuesta .De hecho, podemos implementar propiedades de la
types.ModuleType
siguiente manera, aunque los resultados no son excelentes. No podemos modificar directamente los tipos integrados, pero podemos maldecirlos :Esto nos da una propiedad que existe en todos los módulos. Es un poco difícil de manejar, ya que rompemos el comportamiento de configuración en todos los módulos:
fuente
@module_property
decorador. En términos generales, el@property
decorador incorporado se usa cuando se define una clase, no después de que se haya creado una instancia de ella, por lo que supongo que lo mismo sería cierto para una propiedad de módulo y es con la respuesta de Alex: recuerde que esta pregunta pide "¿Pueden los módulos tener propiedades de la misma manera que los objetos?". Sin embargo, es posible agregarlos después y modifiqué mi fragmento anterior para ilustrar una forma en que se puede hacer.cached_module_property
, el hecho de que__getattr__()
ya no se llamará si el atributo se define es útil. (similar a lo quefunctools.cached_property
logra).