具有上下文管理器的 Python mixin 无法正确解决 "super"调用

标签 python mixins contextmanager

我正在编写一个代表文件的类。这个类有一些可选的功能:通常文件存储在内存中,但有时需要将它们存储在磁盘上,有时我想将它们存储为zip文件等等。我决定使用 mixins,我可以在其中子类化 File 类,并在需要时添加我在某些情况下实际需要的 mixins。在这种情况下,读取/写入文件是一项需要一些准备和一些清理的操作(我需要压缩文件,执行一些写入,例如,然后再次压缩更新版本)。为此,我想使用自定义上下文管理器,以确保即使在 with 语句中间存在异常或返回语句,也能执行这些操作。这是我的代码:

class File(object):

    def read(self):
        return "file content"

class ZipMixin(object):

    def read(self):
        with self:
            return super(ZipMixin, self).read()

    def __enter__(self):
        print("Unzipping")
        return self

    def __exit__(self, *args):
        print("Zipping back")


class SaveMixin(object):

    def read(self):
        with self:
            return super(SaveMixin, self).read()

    def __enter__(self):
        print("Loading to memory")
        return self

    def __exit__(self, *args):
        print("Removing from memory, saving on disk")

class SaveZipFile(SaveMixin, ZipMixin, File):
    pass

f = SaveZipFile()
print(f.read())

但是,输出非常令人失望:

Loading to memory
Loading to memory
Removing from memory, saving on disk
Removing from memory, saving on disk
file content

虽然应该是:

Loading to memory from disk
Unzipping
Zipping back
Removing from memory, saving on disk
file content

显然,在具有上下文管理器的 mixin 中对 super 的所有调用都不会“链式”传递给所有 m​​ixin,而是两次传递给第一个 mixin,然后直接传递给父类(super class)(省略中间 mixins)。我用 python 2 和 3 测试了它,结果相同。怎么了?

最佳答案

会发生什么?

“super”调用按照您期望的方式工作,两个 mixin 的 read 方法都按预期顺序调用?

但是,您可以在 SaveMixinZipMixin 类读取方法中使用 with self:

self 在这两种情况下是相同的,导致使用相同的 __enter____exit__ 方法,无论声明类如何。

按照SaveZipFile类的方法解析顺序,使用SaveMixin类的方法:

>>> SaveZipFile.__mro__
(<class '__main__.SaveZipFile'>, <class '__main__.SaveMixin'>, <class '__main__.ZipMixin'>, <class '__main__.File'>, <class 'object'>)

简而言之 SaveMixin 和 ZipMixin 类的读取方法按正确的顺序调用,但 with self: 使用 __enter__和 SaveMixin 类的 __exit__ 方法两次。

如何解决这个问题?

看起来 with 语句对于 Mixins 的使用来说并不是最佳的,但一个可能的解决方案是使用 Decorator Pattern :

class File(object):
    def read(self):
        return "file content"

class ZipDecorator(object):
    def __init__(self, inner):
        self.inner = inner

    def read(self):
        with self:
            return self.inner.read()

    def __enter__(self):
        print("Unzipping")
        return self

    def __exit__(self, *args):
        print("Zipping back")


class SaveDecorator(object):
    def __init__(self, inner):
        self.inner = inner

    def read(self):
        with self:
            return self.inner.read()

    def __enter__(self):
        print("Loading to memory")
        return self

    def __exit__(self, *args):
        print("Removing from memory, saving on disk")

class SaveZipFile(object):
    def read(self):
        decorated_file = SaveDecorator(
            ZipDecorator(
                File()
            )
        )

        return decorated_file.read()


f = SaveZipFile()
print(f.read())

输出:

Loading to memory
Unzipping
Zipping back
Removing from memory, saving on disk
file content

关于具有上下文管理器的 Python mixin 无法正确解决 "super"调用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53742249/

相关文章:

python - mailgun 发送的附件是 1kb 的空白图片

css - 使用 css 变量的正确方法

python - 迭代多个文件的上下文管理器类型 - 测试

python - 串行端口上下文管理器

python - 上下文管理器严格适用于生成器吗?

python - 如何使用自定义 CTC 层正确保存和加载模型(Keras 示例)

python - pymysql - pymysql.err.InternalError : 1054, 用户输入字符串用作列名

java - POJO 到 JSON 的转换不适用于以 "is"开头的字符串变量

css - grunt-sass 两次调用 mixin 导致错误

python - 如何打印字典的键?