__getattr__在__enter__中不工作 -> AttributeError

6 投票
2 回答
1686 浏览
提问于 2025-04-16 06:51

我想在一个使用了 __getattr__ 来重定向调用的对象上使用 with
不过,这似乎在 __enter__ 方法上不起作用。

请看下面这个简化的代码,它可以重现这个错误:

class EnterTest(object):
    def myenter(self):
        pass
    def __exit__(self, type, value, traceback):
        pass

    def __getattr__(self, name):
        if name == '__enter__':
            return self.myenter

enter_obj = EnterTest()
print getattr(enter_obj, '__enter__')

with enter_obj:
    pass

输出结果:

<bound method EnterTest.myenter of <__main__.EnterTest object at 0x00000000021432E8>>
Traceback (most recent call last):
  File "test.py", line 14, in <module>
    with enter_obj:
AttributeError: __enter__

为什么当对象上没有 __enter__ 方法时,它不回退到 __getattr__ 呢?

当然,如果我创建一个 __enter__ 方法并从那里进行重定向,它是可以工作的,但我想知道为什么在其他情况下不行。

我的 Python 版本如下:

C:\Python27\python27.exe 2.7 (r27:82525, Jul  4 2010, 07:43:08) [MSC v.1500 64 bit (AMD64)]

2 个回答

0

你用的是哪个版本的Python?看起来这是个老问题。你可以看看这个链接。你的代码在Python 2.5.1 (r251:54863, 2009年2月6日, 19:02:12)上能正常工作,前提是修正了def __exit__(self, *args):这一部分。

5

根据上游的说法,这个问题在2.6版本中是个bug,在2.7版本中“修复”了。简单来说,像__enter__这样的函数是从类中查找的,而不是从对象中查找。

关于这种奇怪行为的文档可以在这里找到:http://docs.python.org/reference/datamodel#specialnamesx[i] 大致等同于 ... type(x).__getitem__(x, i) 对于新式类

你可以在其他特殊方法中看到这种行为:

class foo(object):
    def __iadd__(self, i):
        print i
a = foo()
a += 1

class foo2(object):
    def __getattr__(self, key):
        print key
        raise AttributeError
b = foo2()
b += 1

class foo3(object):
    pass
def func(self, i):
    print i
c = foo3()
c.__iadd__ = func
c += 1

第一个能正常工作;后两个则不行。Python 2.6在__enter____exit__的行为上没有遵循这个规则,但2.7版本遵循了。http://bugs.python.org/issue9259

不过,这种方法不能像其他属性那样动态处理,实在是让人感到不一致。同样,你也不能像处理其他方法那样,用__getattribute__来监控这些方法的访问。我找不到任何内在的设计逻辑来解释这一点。Python通常是非常一致的,而这点则显得相当不愉快。

撰写回答