使用上下文管理器时对象变为None

30 投票
3 回答
33424 浏览
提问于 2025-04-16 12:23

为什么这个不管用:

class X:
    var1 = 1
    def __enter__(self): pass
    def __exit__(self, type, value, traceback): pass

with X() as z:
    print z.var1

我得到的是:

print z.var1
AttributeError: 'NoneType' object has no attribute 'var1'

3 个回答

4

你在'with'和'as'之间定义的函数必须只有一个返回值。'with'会把这个值传给它的内置方法 __enter__()

在Python中,如果你调用一个类类型的对象,但没有定义返回值,它是不会返回任何值的。

同样,如果你调用一个类类型的对象的方法,而这个方法不返回任何东西,它也会抛出异常。

你不能这样写:

with open('file.txt').readlines() as lines:

这样会生成两个返回值,而在Python中不允许把它们传给一个变量。

但是这样写就可以:

with open('file.txt') as f:
    lines = f.readlines()
9

可以查看文档了解上下文管理器:

__enter__( ) 是进入运行时上下文的方法,它会返回这个对象或者与运行时上下文相关的其他对象。这个方法返回的值会绑定到使用这个上下文管理器的 with 语句中的 as 子句里的标识符上。比如,文件对象就是一个返回自身的上下文管理器。文件对象在 __enter__() 方法中返回自身,这样就可以在 with 语句中使用 open() 作为上下文表达式。

另一个例子是上下文管理器返回一个相关对象,比如通过 decimal.Context.get_manager() 返回的那个。这些管理器会把活动的十进制上下文设置为原始十进制上下文的一个副本,然后返回这个副本。这样,在 with 语句的主体中对当前十进制上下文的修改就不会影响到 with 语句外的代码。

你的 __enter__ 方法没有返回任何东西,这和返回 None 是一样的。

50

X的定义改成下面这样:

class X(object):
    var1 = 1
    def __enter__(self):
        return self
    def __exit__(self, type, value, traceback):
        pass

with会把__enter__()方法的返回值赋给as后面的名字。你写的__enter__()返回了None,所以这个None被赋值给了z

我还把这个类改成了新式类(这并不是让它能工作的关键)。

撰写回答