如何才能实现相当于__getattr__
一个类,一个模块?
例
当我们调用了不以模块的静态定义的属性存在的功能,我想创建一个模块中的类的实例,并用相同的名字调用它的方法未能在模块上的属性查找。
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
Answer 1:
前一段时间,圭多宣布新样式类的所有特殊方法查找绕过__getattr__
和__getattribute__
。 Dunder方法曾经工作的模块-你可以,例如,使用一个模块作为一个上下文管理器简单地定义__enter__
和__exit__
,这些技巧之前爆发 。
最近一些历史功能已卷土重来,模块__getattr__
当中,所以现有的黑客(有一类更换本身就是一个模块sys.modules
导入时)应该是没有存在的必要。
在Python 3.7+,您只需使用一个明显的方式。 定制一个模块上的属性的访问中,定义一个__getattr__
在模块级函数应该接受一个参数(属性名),以及返回的计算值或引发AttributeError
:
# my_module.py
def __getattr__(name: str) -> Any:
...
这也将让钩进“从”进口,即可以返回报表动态生成的对象,比如from my_module import whatever
。
与此相关,随着模块GETATTR你也可以定义一个__dir__
在模块级函数来响应dir(my_module)
见PEP 562了解详情。
Answer 2:
有你正在运行到这里两个基本问题:
-
__xxx__
方法仅抬头的类 -
TypeError: can't set attributes of built-in/extension type 'module'
(1)是指任何解决方案必须还跟踪哪些正在审查模块,否则每个模块将不得不实例取代行为; 和(2)表示(1)甚至不可能......至少不直接。
幸运的是,sys.modules中是不挑剔(发生的事情有这样的包装都可以工作,但仅限于模块接入即import somemodule; somemodule.salutation('world')
;对于同一个模块访问,你几乎必须猛拉方法从替代类,并将它们添加到globals()
eiher与上类的定制方法(我喜欢用.export()
或通用的功能(如已列为答案的)有一点要记住:如果包装是每次创建一个新的实例和全局的解决方案是不是,你结束了微妙的不同的行为哦,你不要同时使用这两种 - 它是一个或另一个。
更新
从吉多·范罗苏姆 :
其实,有是偶尔使用,并建议一个黑客:一个模块可以定义所需功能的类,然后在年底,与该类的实例代替本身sys.modules中(或用类,如果你坚持,但是这通常不太有用)。 例如:
# module foo.py
import sys
class Foo:
def funct1(self, <args>): <code>
def funct2(self, <args>): <code>
sys.modules[__name__] = Foo()
这工作,因为进口机器正在积极实现这个技巧,并作为其最后一步拉实际模块从sys.modules中的,加载它之后。 (这不是偶然的。这个破解早就提出了,我们决定我们喜欢不足以支持它在进口机器。)
因此,建立的方式来完成你想要的是你的模块中创建一个类,并作为该模块的最后一幕更换sys.modules[__name__]
与你的类的实例-现在你可以玩__getattr__
/ __setattr__
/ __getattribute__
根据需要。
请注意,如果您使用此功能的任何模块中的其他人,如全局变量,其他功能等时,会丢失sys.modules
的分配是由-所以务必需要确保一切是更换类中。
Answer 3:
这是一个黑客,但可以用类包装模块:
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__])
Answer 4:
我们通常不这样做的。
我们所做的就是这一点。
class A(object):
....
# The implicit global instance
a= A()
def salutation( *arg, **kw ):
a.salutation( *arg, **kw )
为什么? 这样隐含全局实例可见。
对于例子,看看random
模块,创建一个隐含全局实例略有简化,你希望有一个“简单”的随机数生成器的使用情况。
Answer 5:
什么@哈佛小号提出的,在这里我需要实现一个模块(如一些魔法的情况下,类似__getattr__
),我会定义一个继承自一个新类types.ModuleType
也放到了sys.modules
(可能更换模块在我的自定义ModuleType
定义)。
参见主__init__.py
的文件WERKZEUG一个相当可靠地实现了这一点。
Answer 6:
这是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")
这是通过遍历全局命名空间中的所有对象。 如果该项目是一个类,它遍历类属性。 如果属性是可调用它,它添加到全局命名空间的功能。
它忽略包含“__”的所有属性。
我不会在生产代码中使用此,但它应该让你开始。
Answer 7:
下面是我自己的绵薄之力 - 轻微的点缀@哈佛小号的高度评价答案,但更明确一点(所以它或许可以被接受@美国洛特,尽管可能不是为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")
Answer 8:
创建具有您的类的模块文件。 导入模块。 运行getattr
刚才导入的模块上。 你可以用做一个动态的进口__import__
从sys.modules中拉出模块。
这是你的模块some_module.py
:
class Foo(object):
pass
class Bar(object):
pass
而在另一个模块:
import some_module
Foo = getattr(some_module, 'Foo')
动态地这样做:
import sys
__import__('some_module')
mod = sys.modules['some_module']
Foo = getattr(mod, 'Foo')
Answer 9:
在某些情况下, globals()
字典可以足够了,比如你可以通过名称从全球范围内实例化一个类:
from somemodule import * # imports SomeClass
someclass_instance = globals()['SomeClass']()
文章来源: __getattr__ on a module