__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 

您遇到两个基本问题:

  1. __xxx__方法只能在类中查找
  2. 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']()