__getattr__ on a module
如何在类和模块上实现等效的
当调用模块静态定义的属性中不存在的函数时,我希望在该模块中创建类的实例,并使用与模块的属性查找失败时相同的名称对其调用方法。
1 2 3 4 5 6 7 8 9 10 11 12 | 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") |
它给出:
1 2 3 4 5 | 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 |
号
这里有两个基本问题:
(1)意味着任何解决方案都必须跟踪正在检查的模块,否则每个模块都将具有实例替换行为;以及(2)意味着(1)甚至不可能……至少不是直接的。
幸运的是,sys.modules并不是很挑剔,所以包装器可以工作,但只适用于模块访问(即
更新
来自Guido van Rossum:
There is actually a hack that is occasionally used and recommended: a
module can define a class with the desired functionality, and then at
the end, replace itself in sys.modules with an instance of that class
(or with the class, if you insist, but that's generally less useful).
E.g.:
号
1 2 3 4 5 6 7 8 9 | # module foo.py import sys class Foo: def funct1(self, ): <wyn> def funct2(self, ): <wyn> sys.modules[__name__] = Foo() |
This works because the import machinery is actively enabling this
hack, and as its final step pulls the actual module out of
sys.modules, after loading it. (This is no accident. The hack was
proposed long ago and we decided we liked enough to support it in the
import machinery.)
号
因此,实现您想要的目标的既定方法是在模块中创建一个类,作为模块的最后一个操作,用您的类的实例替换
请注意,如果使用此功能,则在执行
这是一个黑客,但您可以用类包装模块:
1 2 3 4 5 6 7 8 9 10 11 | class Wrapper(object): def __init__(self, wrapped): self.wrapped = wrapped def __getattr__(self, name): # Perform custom logic here try: return getattr(self.wrapped, name) except AttributeError: return 'default' # Some sensible default sys.modules[__name__] = Wrapper(sys.modules[__name__]) |
不久前,guido声明所有特殊的方法查找新型类绕过了
最近,一些历史特性又卷土重来,其中包括模块
在Python3.7+中,您只需使用一种明显的方法。要自定义模块上的属性访问,请在模块级别定义一个
1 2 3 4 | # my_module.py def __getattr__(name: str) -> Any: ... |
这还允许钩住"from"导入,即,您可以为诸如
在相关注释中,除了模块getattr,您还可以在模块级别定义一个
我们通常不会那样做。
我们要做的就是这个。
1 2 3 4 5 6 7 8 | class A(object): .... # The implicit global instance a= A() def salutation( *arg, **kw ): a.salutation( *arg, **kw ) |
号
为什么?使隐式全局实例可见。
例如,查看
类似于什么@h?vard建议,在我需要在一个模块上实现一些魔力的情况下(比如
请参阅Werkzeug的主要
这有点陈词滥调,但是…
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 | import types class A(object): def salutation(self, accusative): print"hello", accusative def farewell(self, greeting, accusative): print greeting, accusative def AddGlobalAttribute(classname, methodname): print"Adding" + classname +"." + methodname +"()" def genericFunction(*args): return globals()[classname]().__getattribute__(methodname)(*args) globals()[methodname] = genericFunction # set up the global namespace x = 0 # X and Y are here to add them implicitly to globals, so y = 0 # globals does not change as we iterate over it. toAdd = [] def isCallableMethod(classname, methodname): someclass = globals()[classname]() something = someclass.__getattribute__(methodname) return callable(something) for x in globals(): print"Looking at", x if isinstance(globals()[x], (types.ClassType, type)): print"Found Class:", x for y in dir(globals()[x]): if y.find("__") == -1: # hack to ignore default methods if isCallableMethod(x,y): if y not in globals(): # don't override existing global names toAdd.append((x,y)) for x in toAdd: AddGlobalAttribute(*x) if __name__ =="__main__": salutation("world") farewell("goodbye","world") |
。
这是通过迭代全局命名空间中的所有对象来实现的。如果该项是一个类,它将迭代类属性。如果属性是可调用的,它会将其作为函数添加到全局命名空间中。
它忽略所有包含"uuuu"的属性。
我不会在生产代码中使用它,但它应该可以让您开始使用。
这是我自己谦虚的贡献,@h的一点点缀?vard的答案评价很高,但更明确一点(因此@s.lott可能可以接受,尽管可能不足以满足OP要求):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | import sys class A(object): def salutation(self, accusative): print"hello", accusative class Wrapper(object): def __init__(self, wrapped): self.wrapped = wrapped def __getattr__(self, name): try: return getattr(self.wrapped, name) except AttributeError: return getattr(A(), name) _globals = sys.modules[__name__] = Wrapper(sys.modules[__name__]) if __name__ =="__main__": _globals.salutation("world") |
。
在某些情况下,
1 2 3 | from somemodule import * # imports SomeClass someclass_instance = globals()['SomeClass']() |
。
创建包含类的模块文件。导入模块。在刚导入的模块上运行
这是您的模块
1 2 3 4 5 | class Foo(object): pass class Bar(object): pass |
在另一个模块中:
1 2 3 | import some_module Foo = getattr(some_module, 'Foo') |
。
动态执行此操作:
1 2 3 4 5 | import sys __import__('some_module') mod = sys.modules['some_module'] Foo = getattr(mod, 'Foo') |