为什么`with`使用类的__enter__`而不是对象?

时间:2017-01-15 16:37:29

标签: python dynamic with-statement

在尝试在运行时修补上下文管理器时,我注意到以下代码的行为与我的预期不符:

class B:
    def __enter__(self):
        print('normal')
    def __exit__(self, *stuff):
        pass

    def modify(self, x):
        def other(self):
             print('not normal: ', x)
        self.__enter__ = other.__get__(self, type(self))

def main():
    b = B()
    b.__enter__()
    b.modify('hi')
    b.__enter__()
    with b:
        print('in with')
    b.__enter__()

if __name__ == '__main__':
    main()

执行,打印:

normal
not normal:  hi
normal
in with
not normal:  hi

main的第一部分,显式调用__enter__,行为符合预期(方法 正确修改),with - 声明似乎忽略了这一点。

经过一些搜索,我发现根据PEP 343显示了一个示例翻译,它解释了行为;也就是说,with mgr: ...的翻译内部使用类似

的内容
type(mgr).__enter__(mgr)

而不是直接的方法调用,就像我上面做的那样。

我想知道为什么这样做了。只是为了防止像我这样的人搞乱,还是有更深层次的原因?

1 个答案:

答案 0 :(得分:0)

语言描述中定义了一些名为special method lookup的内容。基本上,这种查找(使用type(obj).__method__(obj))适用于所有“魔术方法”;有两个原因:

  • 可以进行性能优化
  • 有些情况只能正常使用此变体,即当特殊查找延迟到类型时(例如,int.__hash__type(int).__hash__不同,我们通常真正想要的)