Python doesn't allow modules to override or add any magic method, because keeping module objects simple, regular and lightweight is just too advantageous considering how rarely strong use cases appear where you could use magic methods there.
When such use cases do appear, the solution is to make a class instance masquerade as a module. Specifically, code your mod_call.py
as follows:
import sys
class mod_call:
def __call__(self):
return 42
sys.modules[__name__] = mod_call()
Now your code importing and calling mod_call
works fine.
Special methods are only guaranteed to be called implicitly when they are defined on the type, not on the instance. (__call__
is an attribute of the module instance mod_call
, not of <type 'module'>
.) You can't add methods to built-in types.
https://docs.python.org/reference/datamodel.html#special-lookup
As Miles says, you need to define the call on class level.
So an alternative to Alex post is to change the class of sys.modules[__name__]
to a subclass of the type of sys.modules[__name__]
(It should be types.ModuleType
).
This has the advantage that the module is callable while keeping all other properties of the module (like accessing functions, variables, ...).
import sys
class MyModule(sys.modules[__name__].__class__):
def __call__(self): # module callable
return 42
sys.modules[__name__].__class__ = MyModule
Note: Tested with python3.6.
Christoph Böddeker's answer seems to be the best way to create a callable module, but as a comment says, it only works in Python 3.5 and up.
The benefit is that you can write your module like normal, and just add the class reassignment at the very end, i.e.
# coolmodule.py
import stuff
var = 33
class MyClass:
...
def function(x, y):
...
class CoolModule(types.ModuleType):
def __call__(self):
return 42
sys.modules[__name__].__class__ = CoolModule
and everything works, including all expected module attributes like __file__
being defined. (This is because you're actually not changing the module object resulting from the import at all, just "casting" it to a subclass with a __call__
method, which is exactly what we want.)
To get this to work similarly in Python versions below 3.5, you can adapt Alex Martelli's answer to make your new class a subclass of ModuleType, and copy all the module's attributes into your new module instance:
#(all your module stuff here)
class CoolModule(types.ModuleType):
def __init__(self):
types.ModuleType.__init__(self, __name__)
# or super().__init__(__name__) for Python 3
self.__dict__.update(sys.modules[__name__].__dict__)
def __call__(self):
return 42
sys.modules[__name__] = CoolModule()
Now __file__
, __name__
and other module attributes are defined (which aren't present if just following Alex's answer), and your imported module object still "is a" module.
All answers work only for import mod_call
. To get it working simultaneously for from mod_call import *
, the solution of @Alex Martelli can be enhanced as follow
import sys
class mod_call:
def __call__(self):
return 42
mod_call = __call__
__all__ = list(set(vars().keys()) - {'__qualname__'}) # for python 2 and 3
sys.modules[__name__] = mod_call()
This solution was derived with the discussion of an answer of a similar problem.
To turn the solution into a convenient reusable function:
def set_module(cls, __name__):
import sys
class cls2(sys.modules[__name__].__class__, cls):
pass
sys.modules[__name__].__class__ = cls2
save it to, say, util.py
. Then in your module,
import util
class MyModule:
def __call__(self): # module callable
return 42
util.set_module(MyModule, __name__)
Hurray!
I wrote this because I need to enhance a lot of modules with this trick.
P.S. Few days after I wrote this answer, I removed this trick from my code, since it is so tricky for tools like Pylint to understand.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With