为什么__getattr__不能与__exit__一起使用?

GP8*_*P89 4 python with-statement attr

在尝试解决另一个问题时,我偶然发现了这一点.

这对我来说似乎非常奇怪,我认为值得提问.为什么似乎没有__getattr__合作with

如果我做这个对象:

class FileHolder(object):
    def __init__(self,*args,**kwargs):
        self.f= file(*args,**kwargs)

    def __getattr__(self,item):
        return getattr(self.f,item)
Run Code Online (Sandbox Code Playgroud)

并使用它with,

>>> a= FileHolder("a","w")
>>> a.write
<built-in method write of file object at 0x018D75F8>
>>> with a as f:
...   print f
...
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: __exit__
>>> a.__exit__
<built-in method __exit__ of file object at 0x018D75F8>
Run Code Online (Sandbox Code Playgroud)

为什么会这样?

编辑

>>> object.__exit__
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: type object 'object' has no attribute '__exit__'
Run Code Online (Sandbox Code Playgroud)

它肯定不是继承 __exit__

nne*_*neo 5

with声明码SETUP_WITH查找__exit__一个"特殊方法查找",而忽略__getattr____getattribute__新样式类(而不是在老式类).有关更多信息,请参阅此邮件列表线程,他们讨论了如何添加特殊方法查找语义with(他们最终会这样做).另请参阅新样式类的特殊方法查找,以详细讨论为何以这种方式查找这些特殊方法.

特别是,特殊方法查找也会绕过__getattr__类型对象.因此,即使文档说该方法被查找为type(mgr).__exit__,此代码也不起作用:

class M(type):
    def __getattr__(*args): return lambda: 0

class X(object):
    __metaclass__ = M

x = X()
type(x).__exit__ # works, returns a lambda

with x: pass # fails, AttributeError
Run Code Online (Sandbox Code Playgroud)


kor*_*nce 5

我不能肯定地说,但在阅读了描述with声明的PEP之后:

http://www.python.org/dev/peps/pep-0343/

这突然袭来了我:

A new statement is proposed with the syntax:

    with EXPR as VAR:
        BLOCK

....

The translation of the above statement is:

    mgr = (EXPR)
    exit = type(mgr).__exit__  # Not calling it yet
    value = type(mgr).__enter__(mgr)

....
Run Code Online (Sandbox Code Playgroud)

在那里.with语句不会调用__getattr__(__exit__)type(a).__exit__不存在给出错误的调用.

所以你只需要定义那些:

class FileHolder(object):                                                                                                                 
    def __init__(self,*args,**kwargs):
        self.f= file(*args,**kwargs)

    def __enter__(self,*args,**kwargs):
        return self.f.__enter__(*args,**kwargs)

    def __exit__(self,*args,**kwargs):
        self.f.__exit__(*args,**kwargs)

    def __getattr__(self,item):
        return getattr(self.f,item)
Run Code Online (Sandbox Code Playgroud)

  • @ GP89:我做了一些挖掘; 这是设计的.有关完整推理,请参阅[此处](http://docs.python.org/reference/datamodel.html#special-method-lookup-for-new-style-classes). (2认同)
  • 我也在邮件列表@nneonneo提到了一些挖掘.以下是一个简短的摘要:http://mail.python.org/pipermail/python-dev/2009-May/089576.html (2认同)