解释:
__enter__
返回None
,因为没有return
语句,所以会直接触发__exit__
,因为None
没有name
属性,例如:
>>> None.name
Traceback (most recent call last):
File "<pyshell#0>", line 1, in <module>
None.__name__
AttributeError: 'NoneType' object has no attribute 'name'
>>>
如果你设置为调用 __class__.__name__
(None
对象具有该属性,这将给出 NoneType
),你可以轻松地找到问题:
class Test:
def __init__(self, name):
self.name = name
def __enter__(self):
print(f'entering {self.name}')
def __exit__(self, exctype, excinst, exctb) -> bool:
print(f'exiting {self.name}')
return True
with Test('first') as test:
print(f'in {test.__class__.__name__}')
test = Test('second')
with test:
print(f'in {test.__class__.__name__}')
输出:
entering first
in NoneType
exiting first
entering second
in Test
exiting second
正如你所看到的,它显示了 in NoneType
,因为没有返回任何值导致了这个结果。在很多情况下,__enter__
不需要返回值,但在这种情况下,Test
类需要它返回。
解决方案:
解决方案是保留 Test
实例,使其在上下文管理器 __enter__
的结果后调用返回的 self
的 name
。目前 __enter__
的结果为 None
,因此 None.name
属性不存在。因此,如果您返回 self
,则 test.name
属性将存在。
解决方案是在 __enter__
神奇方法实现中返回 self
:
...
def __enter__(self):
print(f'entering {self.name}')
return self
...
完整代码:
class Test:
def __init__(self, name):
self.name = name
def __enter__(self):
print(f'entering {self.name}')
return self
def __exit__(self, exctype, excinst, exctb) -> bool:
print(f'exiting {self.name}')
return True
with Test('first') as test:
print(f'in {test.name}')
test = Test('second')
with test:
print(f'in {test.name}')
输出:
entering first
in first
exiting first
entering second
in second
exiting second
我提供的额外信息是其他答案没有提供的,这更具体地证明了
__enter__
方法实现返回
None
。我还展示了一个例子。
with
的行为与 OP 预期的一致。这在 PEP-343 中被更改了。我想知道为什么他们没有将对象作为默认上下文,当__enter__
返回None
时,因为这是一个非常常见的情况。 - Barmar__enter__
会返回None
,因为as
目标确实应该设置为None
。如果将返回值为None
特殊处理,那么会破坏一些真正需要使用None
的情况。节省几行return self
的代码不值得这样做。 - user2357112None
语句的__enter__
方法。我说的是如果__enter__
返回某些状态变量的值,该值允许为None
,或者如果它返回某些计算的结果,其输出可能为None
的情况。) - user2357112None.__exit__()
时出错吗? - Barmarwith
调用上下文管理器的__exit__
方法,而不是__enter__
返回值的__exit__
方法。 - user2357112