在 init 中使用 contextmanager

Use contextmanager inside init

在下面的代码中,我不明白为什么 MyFileIO2 中的 with super().__init__(*args, **kwargs): 行会抛出有关缺少 __exit__ 的错误,而 MyFileIO class 一切正常。我真的不明白在 init 内部或外部执行 with 之间到底有什么区别。有人能告诉我这是怎么回事吗?

import io

class MyFileIO(io.FileIO):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

    def __enter__(self, *args, **kwargs):
        f = super().__enter__(*args, **kwargs)
        print('first byte of file: ', f.read(1))
        return f

class MyFileIO2(io.FileIO):
    def __enter__(self, *args, **kwargs):
        f = super().__enter__(*args, **kwargs)
        print('first byte of file: ', f.read(1))
        return f

    def __init__(self, *args, **kwargs):
        with super().__init__(*args, **kwargs): # AttributeError: __exit__
            pass

path = 'some_file.bin'

with MyFileIO(path, 'rb'):
    pass

MyFileIO2(path, 'rb')

您需要在 self 上调用上下文管理器,因为 __init__ 实际上 return 什么都没有。

class MyFileIO2(io.FileIO):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        with self:
             pass

    def __enter__(self, *args, **kwargs):
        f = super().__enter__(*args, **kwargs)
        print('First byte of file: ', f.read(1))
        return f

为了测试,我创建了一个包含内容 "hello world".

的二进制文件
_ = MyFileIO2(path, 'rb')    
# First byte of file:  b'h'

发生的事情是 super().__init__ 的 return 值正在通过上下文管理器传递,因此您实际上拥有:

with None:
     pass

AttributeError: __enter__

上下文管理器尝试在 NoneType 对象上调用 __enter__ 方法,但这是一个无效操作。