With python properties, I can make it such that
obj.y
calls a function rather than just returning a value.
Is there a way to do this with modules? I have a case where I want
module.y
to call a function, rather than just returning the value stored there.
Only instances of new-style classes can have properties. You can make Python believe such an instance is a module by stashing it in sys.modules[thename] = theinstance
. So, for example, your m.py module file could be:
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()
I would do this in order to properly inherit all the attributes of a module, and be correctly identified by isinstance()
import types
class MyModule(types.ModuleType):
@property
def y(self):
return 5
>>> a=MyModule("test")
>>> a
<module 'test' (built-in)>
>>> a.y
5
And then you can insert this into sys.modules:
sys.modules[__name__] = MyModule(__name__) # remember to instantiate the class
A typical use case is: enriching a (huge) existing module with some (few) dynamic attributes - without turning all module stuff into a class layout.
Unfortunately a most simple module class patch like sys.modules[__name__].__class__ = MyPropertyModule
fails with TypeError: __class__ assignment: only for heap types
. So module creation needs to be rewired.
This approach does it without Python import hooks, just by having some prolog on top of the module code:
# 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)
Usage:
>>> 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'"
Note: Something like from propertymodule import dynval
will produce a frozen copy of course - corresponding to dynval = someobject.dynval
As PEP 562 has been implemented in Python >= 3.7, now we can do this
file: module.py
def __getattr__(name):
if name == 'y':
return 3
raise AttributeError(f"module '{__name__}' has no attribute '{name}'")
other = 4
usage:
>>> import module
>>> module.y
3
>>> module.other
4
>>> module.nosuch
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "module.py", line 4, in __getattr__
raise AttributeError(f"module '{__name__}' has no attribute '{name}'")
AttributeError: module 'module' has no attribute 'nosuch'
Note that if you omit the raise AttributeError
in the __getattr__
function, it means the function ends with return None
, then the module.nosuch
will get a value of None
.
来源:https://stackoverflow.com/questions/880530/can-modules-have-properties-the-same-way-that-objects-can