__getattr__在模块上
如何在一个模块上实现相当于__getattr__
的类?
例
当调用模块的静态定义的属性中不存在的函数时,我希望在该模块中创build一个类的实例,并在模块上的属性查找中调用其名称与失败相同的方法。
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")
这使:
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
您遇到两个基本问题:
-
__xxx__
方法只能在类中查找 -
TypeError: can't set attributes of built-in/extension type 'module'
(1)意味着任何解决scheme都必须跟踪哪个模块正在被检查,否则每个模块将具有实例replace行为; (2)意味着(1)甚至不可能…至less不是直接的。
幸运的是,sys.modules并不挑剔,因此包装器将工作,但仅用于模块访问(即import somemodule; somemodule.salutation('world')
;对于相同的模块访问你几乎必须将方法从replace类中将它们添加到globals()
,使用类的自定义方法(我喜欢使用.export()
)或generics函数(例如已经列出的答案)。有一点要记住:如果包装器每次都创build一个新的实例,而全局解决scheme则不是这样,那么最终的行为就会有微妙的差别,而且你不能同时使用两者 – 它是一个或另一个。
更新
从Guido van Rossum开始 :
实际上有一个偶尔使用和推荐的黑客攻击:一个模块可以定义一个具有所需function的类,然后在sys.modules中将其自身replace为该类的一个实例(或者如果您坚持,但通常不太有用)。 例如:
# module foo.py import sys class Foo: def funct1(self, <args>): <code> def funct2(self, <args>): <code> sys.modules[__name__] = Foo()
这是有效的,因为import机器正在积极地进行这种攻击,最后一步是在加载之后将实际模块从sys.modules中取出。 (这不是偶然的,黑客很早就提出了,我们决定我们很喜欢在import机器上支持它。)
所以完成你想要的东西的方法是在你的模块中创build一个类,并且作为模块的最后一个动作用你的类的实例replacesys.modules[__name__]
– 现在你可以使用__getattr__
/ __setattr__
/ __getattribute__
根据需要。
这是一个黑客,但你可以用一个类包装模块:
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__])
我们通常不这样做。
我们做的是这个。
class A(object): .... # The implicit global instance a= A() def salutation( *arg, **kw ): a.salutation( *arg, **kw )
为什么? 所以隐式的全局实例是可见的。
例如,查看random
模块,该模块创build一个隐式全局实例,稍微简化您希望“简单”随机数生成器的用例。
类似于@Havard S提出的,在需要在模块上实现一些魔法的情况下(比如__getattr__
),我会定义一个inheritance自types.ModuleType
的新类,并把它放在sys.modules
(可能replace模块我的自定义ModuleType
被定义)。
请参阅Werkzeug的主__init__.py
文件,以获得相当健壮的实现。
这是hackish,但…
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")
这通过遍历全局名称空间中的所有对象来工作。 如果该项目是一个类,它遍历类属性。 如果该属性是可调用的,则将其作为函数添加到全局名称空间。
它忽略所有包含“__”的属性。
我不会在生产代码中使用它,但它应该让你开始。
这是我自己卑微的贡献 – @HåvardS的评价很高的答案略有点缀,但更明确一点(所以@ S.Lott可能是可以接受的,尽pipe对于OP来说可能不够好):
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")
创build你的类的模块文件。 导入模块。 在刚刚导入的模块上运行getattr
。 您可以使用__import__
进行dynamic导入,并从sys.modules中__import__
模块。
这里是你的模块some_module.py
:
class Foo(object): pass class Bar(object): pass
而在另一个模块中:
import some_module Foo = getattr(some_module, 'Foo')
这样做dynamic:
import sys __import__('some_module') mod = sys.modules['some_module'] Foo = getattr(mod, 'Foo')
在某些情况下, globals()
字典就足够了,例如你可以在全局范围内按名称实例化一个类:
from somemodule import * # imports SomeClass someclass_instance = globals()['SomeClass']()