为什么 `with` 使用 class 的 `__enter__` 而不是对象?
Why does `with` use `__enter__` of the class, not the object?
在尝试在运行时修补上下文管理器时,我注意到以下代码的行为与我预期的不同:
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)
而不是像我上面那样直接调用方法。
我想知道为什么要这样做。是为了防止我这种人乱来,还是有更深层次的原因?
语言描述中定义了一个叫做special method lookup的东西。基本上,这种查找(使用 type(obj).__method__(obj)
)发生在所有 "magic methods" 上;给出两个原因:
- 性能优化是可能的
- 有些情况只能使用此变体正确工作,即当特殊查找延迟到类型时(例如,
int.__hash__
与 type(int).__hash__
不同,我们通常真正想要的)
在尝试在运行时修补上下文管理器时,我注意到以下代码的行为与我预期的不同:
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)
而不是像我上面那样直接调用方法。
我想知道为什么要这样做。是为了防止我这种人乱来,还是有更深层次的原因?
语言描述中定义了一个叫做special method lookup的东西。基本上,这种查找(使用 type(obj).__method__(obj)
)发生在所有 "magic methods" 上;给出两个原因:
- 性能优化是可能的
- 有些情况只能使用此变体正确工作,即当特殊查找延迟到类型时(例如,
int.__hash__
与type(int).__hash__
不同,我们通常真正想要的)