__getattr__模块上__getattr__模块上(__getattr__ on a modu

2019-05-09 06:22发布

如何才能实现相当于__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:

有你正在运行到这里两个基本问题:

  1. __xxx__方法仅抬头的类
  2. 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