模块上的 __getattr__

2024-01-29

如何实现相当于__getattr__在一个类上,在一个模块上?

Example

当调用模块的静态定义属性中不存在的函数时,我希望在该模块中创建类的实例,并使用与模块上的属性查找失败相同的名称调用其方法。

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) 意味着任何解决方案都必须跟踪正在检查的模块,否则every然后模块将具有实例替换行为; (2) 意味着 (1) 甚至不可能......至少不是直接可能。

幸运的是,sys.modules 对那里的内容并不挑剔,因此包装器可以工作,但仅适用于模块访问(即import somemodule; somemodule.salutation('world');对于相同模块的访问,您几乎必须从替换类中取出方法并将它们添加到globals()在类上使用自定义方法(我喜欢使用.export())或使用通用函数(例如已列为答案的函数)。需要记住的一件事是:如果包装器每次都创建一个新实例,而全局解决方案则不然,那么您最终会得到略有不同的行为。哦,你不能同时使用两者——只能是其中之一。


Update

From 吉多·范罗苏姆 http://mail.python.org/pipermail/python-ideas/2012-May/014969.html:

实际上有一个偶尔使用和推荐的 hack: 模块可以定义一个具有所需功能的类,然后在 最后,用该类的实例替换 sys.modules 中的自身 (或者如果你坚持的话,也可以在课堂上,但这通常不太有用)。 例如。:

# module foo.py

import sys

class Foo:
    def funct1(self, <args>): <code>
    def funct2(self, <args>): <code>

sys.modules[__name__] = Foo()

这是有效的,因为进口机械正在积极实现这一目标 hack,最后一步将实际模块从 sys.modules,加载后。 (这并非偶然。黑客攻击是 很久以前就提出了,我们决定我们足够喜欢支持它 进口机械。)

因此,完成您想要的任务的既定方法是在模块中创建一个类,并作为模块的最后一个动作替换sys.modules[__name__]与你的班级的一个实例 - 现在你可以玩__getattr__/__setattr__/__getattribute__如所须。


Note 1:如果您使用此功能,则模块中的其他任何内容(例如全局变量、其他函数等)都将在sys.modules分配已经完成——因此请确保所需的一切都在替换类中。

Note 2: 支持from module import *你必须有__all__在类中定义;例如:

class Foo:
    def funct1(self, <args>): <code>
    def funct2(self, <args>): <code>
    __all__ = list(set(vars().keys()) - {'__module__', '__qualname__'})

根据您的 Python 版本,可能还有其他名称需要省略__all__. The set()如果不需要 Python 2 兼容性,可以省略。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

模块上的 __getattr__ 的相关文章

随机推荐