2009-05-19 12 views
69

Con propiedades pitón, que puede hacer que sea tal que¿Los módulos pueden tener propiedades de la misma manera que los objetos?

obj.y 

llama a una función en lugar de devolver un valor.

¿Hay alguna manera de hacer esto con los módulos? Tengo un caso en el que deseo

module.y 

para llamar a una función, en lugar de simplemente devolver el valor almacenado allí.

+2

¿Qué hay de malo con solo llamar a una función? –

+8

quiero hacer menos tipeo para hacer ciertas cosas que se hacen mucho. –

Respuesta

50

Solo las instancias de las clases de nuevo estilo pueden tener propiedades. Puede hacer que Python crea que una instancia de este tipo es un módulo al esconderlo en sys.modules[thename] = theinstance. Así, por ejemplo, su archivo de módulo m.py podría ser:

import sys 
class _M(object): 
    def __init__(self): 
    self.c = 0 
    def afunction(self): 
    self.c += 1 
    return self.c 
    y = property(afunction) 
sys.modules[__name__] = _M() 

Editado: eliminado una dependencia implícita en variables globales (nada que ver con el punto del ejemplo, pero lo hizo confundir las cosas, haciendo que el el código original falla!).

+0

Nunca hubiera pensado en hacer eso. Admito que es poco probable que lo use, pero nunca se sabe. Siempre feliz de aprender sobre avenidas inexploradas en Python ... gracias. –

+1

¿Alguien más intentó esto? Cuando coloco este código en un archivo x.py e lo importo de otro, entonces llamar a xy resulta en AttributeError: el objeto 'NoneType' no tiene atributo 'c', ya que _M de alguna manera tiene el valor None ... – Stephan202

+0

Debe estar escribiendo mal algo . Compruebe una sesión interactiva: $ pitón ... sys >>> >>> importación _M clase (objeto): ... c = 0 ... ACONFIG def (auto): ... _M .c + = 1 ... return _M.c ... y = propiedad (función) ... >>> sys.módulos [ 'guau'] = _M() >>> import bowwow >>> bowwow.y >>> bowwow.y >>> ... exactamente como se esperaba, por supuesto. Entonces, ¿qué es diferente en lo que exactamente estás haciendo? –

44

Me gustaría hacer esto con el fin de heredar correctamente todos los atributos de un módulo, e identificarse correctamente por isinstance()

import types 

class MyModule(types.ModuleType): 
    @property 
    def y(self): 
     return 5 


>>> a=MyModule("test") 
>>> a 
<module 'test' (built-in)> 
>>> a.y 
5 

y luego se puede insertar esto en sys.modules:

sys.modules[__name__] = MyModule(__name__) # remember to instantiate the class 
+0

Esto parece funcionar solo para el caso más simple. Posibles problemas son: (1) algunos importadores también pueden esperar otros atributos como '__file__' que deben ser definidos manualmente, (2) las importaciones hechas en el módulo que contiene la clase no serán" visibles "durante el tiempo de ejecución, etc. – tutuDajuju

+1

No es necesario derivar una subclase de 'types.ModuleType', _any_ (new-style) class. ¿Exactamente qué atributos especiales del módulo espera heredar? – martineau

+0

¿Qué pasa si el módulo original es un paquete y quiero acceder a los módulos debajo del módulo original? –

1

Un caso de uso típico es: enriquecer un (enorme) módulo 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 falla con TypeError: __class__ assignment: only for heap types. Entonces la creación del módulo necesita ser reconectada.

Este enfoque hace sin Python ganchos de importación, sólo por tener algún prólogo en la parte superior del código del módulo:

# propertymodule.py 
""" Module property example """ 

if '__orgmod__' not in globals(): 

    # constant prolog for having module properties/supports reload() 

    print "PropertyModule stub execution", __name__ 
    import sys, types 
    class PropertyModule(types.ModuleType): 
     def __str__(self): 
      return "<PropertyModule %r from %r>" % (self.__name__, self.__file__) 
    modnew = PropertyModule(__name__, __doc__) 
    modnew.__modclass__ = PropertyModule   
    modnew.__file__ = __file__ 
    modnew.__orgmod__ = sys.modules[__name__] 
    sys.modules[__name__] = modnew 
    exec sys._getframe().f_code in modnew.__dict__ 

else: 

    # normal module code (usually vast) .. 

    print "regular module execution" 
    a = 7 

    def get_dynval(module): 
     return "property function returns %s in module %r" % (a * 4, module.__name__)  
    __modclass__.dynval = property(get_dynval) 

Uso:

>>> import propertymodule 
PropertyModule stub execution propertymodule 
regular module execution 
>>> propertymodule.dynval 
"property function returns 28 in module 'propertymodule'" 
>>> reload(propertymodule) # AFTER EDITS 
regular module execution 
<module 'propertymodule' from 'propertymodule.pyc'> 
>>> propertymodule.dynval 
"property function returns 36 in module 'propertymodule'" 

Nota: Algo así como from propertymodule import dynval producirá una congelada copia por supuesto - correspondiente a dynval = someobject.dynval

Cuestiones relacionadas