How can implement the equivalent of a __getattr__
on a class, on a module?
Example
When calling a function that does not exist in a module's statically defined attributes, I wish to create an instance of a class in that module, and invoke the method on it with the same name as failed in the attribute lookup on the module.
class A(object):
def salutation(self, accusative):
print "hello", accusative
# note this function is intentionally on the module, and not the class above
def __getattr__(mod, name):
return getattr(A(), name)
if __name__ == "__main__":
# i hope here to have my __getattr__ function above invoked, since
# salutation does not exist in the current namespace
salutation("world")
Which gives:
matt@stanley:~/Desktop$ python getattrmod.py
Traceback (most recent call last):
File "getattrmod.py", line 9, in <module>
salutation("world")
NameError: name 'salutation' is not defined
Create your module file that has your classes. Import the module. Run
getattr
on the module you just imported. You can do a dynamic import using__import__
and pull the module from sys.modules.Here's your module
some_module.py
:And in another module:
Doing this dynamically:
This is a hack, but you can wrap the module with a class:
There are two basic problems you are running into here:
__xxx__
methods are only looked up on the classTypeError: can't set attributes of built-in/extension type 'module'
(1) means any solution would have to also keep track of which module was being examined, otherwise every module would then have the instance-substitution behavior; and (2) means that (1) isn't even possible... at least not directly.
Fortunately, sys.modules is not picky about what goes there so a wrapper will work, but only for module access (i.e.
import somemodule; somemodule.salutation('world')
; for same-module access you pretty much have to yank the methods from the substitution class and add them toglobals()
eiher with a custom method on the class (I like using.export()
) or with a generic function (such as those already listed as answers). One thing to keep in mind: if the wrapper is creating a new instance each time, and the globals solution is not, you end up with subtly different behavior. Oh, and you don't get to use both at the same time -- it's one or the other.Update
From Guido van Rossum:
So the established way to accomplish what you want is to create a single class in your module, and as the last act of the module replace
sys.modules[__name__]
with an instance of your class -- and now you can play with__getattr__
/__setattr__
/__getattribute__
as needed.Note that if you use this functionality anything else in the module, such as globals, other functions, etc., will be lost when the
sys.modules
assignment is made -- so make sure everything needed is inside the replacement class.We don't usually do it that way.
What we do is this.
Why? So that the implicit global instance is visible.
For examples, look at the
random
module, which creates an implicit global instance to slightly simplify the use cases where you want a "simple" random number generator.This is hackish, but...
This works by iterating over the all the objects in the global namespace. If the item is a class, it iterates over the class attributes. If the attribute is callable it adds it to the global namespace as a function.
It ignore all attributes which contain "__".
I wouldn't use this in production code, but it should get you started.
A while ago, Guido declared that all special method lookups on new-style classes bypass
__getattr__
and__getattribute__
. Dunder methods had previously worked on modules - you could, for example, use a module as a context manager simply by defining__enter__
and__exit__
, before those tricks broke.Recently some historical features have made a comeback, the module
__getattr__
among them, and so the existing hack (a module replacing itself with a class insys.modules
at import time) should be no longer necessary.In Python 3.7+, you just use the one obvious way. To customize attribute access on a module, define a
__getattr__
function at the module level which should accept one argument (name of attribute), and return the computed value or raise anAttributeError
:This will also allow hooks into "from" imports, i.e. you can return dynamically generated objects for statements such as
from my_module import whatever
.On a related note, along with the module getattr you may also define a
__dir__
function at module level to respond todir(my_module)
. See PEP 562 for details.