Python 为什么';是否与退出一起工作?

Python 为什么';是否与退出一起工作?,python,attr,with-statement,Python,Attr,With Statement,当我试图找出另一个答案时,我觉得这有点意外 这对我来说似乎非常奇怪,我认为值得问这个问题。为什么\uuu getattr\uuu似乎不能与一起工作 如果我制作此对象: class FileHolder(object): def __init__(self,*args,**kwargs): self.f= file(*args,**kwargs) def __getattr__(self,item): return getattr(self.f,i

当我试图找出另一个答案时,我觉得这有点意外

这对我来说似乎非常奇怪,我认为值得问这个问题。为什么
\uuu getattr\uuu
似乎不能与
一起工作

如果我制作此对象:

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

    def __getattr__(self,item):
        return getattr(self.f,item)
并将其与
一起使用

>>> 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>
>a=文件持有者(“a”、“w”)
>>>写
>>>以a作为f:
...   打印f
...
回溯(最近一次呼叫最后一次):
文件“”,第1行,在
AttributeError:\uuu退出__
>>>出口__
为什么会发生这种情况

编辑
>>>对象。退出__
回溯(最近一次呼叫最后一次):
文件“”,第1行,在
AttributeError:类型对象“object”没有属性“exit”

它肯定不是继承
\uuuu exit\uuuu

with
语句操作码
SETUP\u,with
查找
\uuu exit\uuuu
作为一种“特殊方法查找”,它忽略了新样式类(而不是旧样式类)上的
\uu getattr\uuu
\uu getattribute\uuu
。有关更多信息,请参阅,其中他们讨论了将特殊方法查找语义添加到带有
(他们最终会这样做)。另请参阅,以获取有关为什么以这种方式查找这些特殊方法的详细讨论

特别是,特殊方法查找也会绕过类型对象上的
\uuu getattr\uuu
。因此,即使文档中说该方法被查找为
type(mgr)。\uuuuu exit\uuuuu
,但该代码不起作用:

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

我不能肯定,但在阅读了政治公众人物的陈述后:

我突然想到:

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)

....
就在那里。with语句不调用
\uuuuu getattr\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu

因此,您只需要定义这些:

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)

前面的回答已经解释了这样一个事实,
\uuuuuGetAttr\uuuuuuuuuuuuu
\uuuuuuuuuuuuu输入和
\uuuuuuuuuuu退出不兼容。我在这里想一想为什么它不应该起作用

在对象上定义
\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu
方法的唯一原因是我们需要在
with。这两种方法帮助我们隐式地获取和释放资源,因此我们通常这样定义它们:

类资源(对象):
...
定义输入(自我):
回归自我
def uuu退出(自我,*exc):
self.close()
然后您可以编写如下代码:

使用Resource()作为资源:#u_uenter_uu被调用并返回一个值作为'Resource'`
用资源做点什么
#调用'resource.\uuuuu exit\uuuuuu'
正如您所注意到的,我们获取和发布的资源正是我们定义的类的一个实例

如果我们将一个资源作为一个属性保存,并用
\uu getattr\uuu
代理它的
\uuuuu进入\uuuuuu
会怎么样?我们编写了如下代码:

类资源代理(对象):
定义初始化(自):
self.\u resource=resource()
def_u_ugetattr_u_u(self,key):
返回getattr(self.\u资源,键)
假设
\uuuu getattr\uuuuuuu
\uuuuuuuuu输入和
\uuuuuuuuuuuuuuuu退出都可以正常工作,下面是
语句中会发生的情况:

使用ResourceProxy()作为资源:#proxied uu_uuenter_u_u调用
#现在'resource'不是ResourceProxy实例,因为我们称之为'u resource.\u__`
用资源做点什么
#调用并正确关闭“资源”。“退出”。
#这里与ResourceProxy无关,因为它从未输入`with`上下文
上述行为很奇怪,可能不像用户预期的那样,原因如下:

  • 使用
  • 上下文输入的
    资源不是我们发送的对象
    
  • 当使用
    上下文退出
    时,将调用代理对象的
    \uuuu exit\uuuu
    方法,而不是我们发送的外部对象。您可能会认为,如果我们在外部类上添加一个
    \uuuu exit\uuuu
    定义可能会有所帮助,但答案是否定的,因为外部类从未使用
    上下文进入

  • 综上所述,如果我们让
    \uuuuu getattr\uuuuuuuuuu
    \uuuuuuuuu进入和
    \uuuuuuu退出
    一起工作,就会导致不良行为。这不是一个好的设计。

    这里发生了一些事情,在您的类定义中,您将
    FileHolder
    作为
    对象的子类。但在它下面的代码中,它表示
    a
    文件
    对象。这是不一致的。@jedwards说实话,事实并非如此。你自己测试:)@jedwards,
    \uuuu exit\uuu
    来自分配给
    self.f
    文件
    对象,如果你问
    键入(a)
    ,你会得到
    文件持有者
    @Adam,你是对的——我实际上没有创建这个类(我做了类似
    类文件持有者(对象):pass
    )--很好。令人惊讶的是,文档在这一点上并不完全正确--特殊方法查找也会忽略
    类型(x)。\uuu getattr\uuu
    。看我的答案…我不能肯定。我唯一的猜测是这是故意的。这可能是邮件列表上的问题。@GP89:我做了更多的挖掘;这是设计的。我也在邮件列表@nneonneo中做了一些挖掘。这里有一个简短的总结:它确实提出了一个问题,为什么
    \uuuuuu\uuuuuu
    没有获得
    属性错误?除非它首先查找退出(这似乎不像@korylprince的回答所指出的那样),否则会首先查找
    \uuuuu exit\uuuu
    ,这样它们就不会在调用
    \uuuu enter\uuuu
    时遇到问题,而不使用功能性
    \uuu exit\uu
    。您可以看到确切的CPython实现。
    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)