__getattr__在__enter__中不工作 -> AttributeError
我想在一个使用了 __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 个回答
你用的是哪个版本的Python?看起来这是个老问题。你可以看看这个链接。你的代码在Python 2.5.1 (r251:54863, 2009年2月6日, 19:02:12)
上能正常工作,前提是修正了def __exit__(self, *args):
这一部分。
根据上游的说法,这个问题在2.6版本中是个bug,在2.7版本中“修复”了。简单来说,像__enter__
这样的函数是从类中查找的,而不是从对象中查找。
关于这种奇怪行为的文档可以在这里找到:http://docs.python.org/reference/datamodel#specialnames:x[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通常是非常一致的,而这点则显得相当不愉快。