我正在编写一个代表文件的类。这个类有一些可选的功能:通常文件存储在内存中,但有时需要将它们存储在磁盘上,有时我想将它们存储为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 的所有调用都不会“链式”传递给所有 mixin,而是两次传递给第一个 mixin,然后直接传递给父类(super class)(省略中间 mixins)。我用 python 2 和 3 测试了它,结果相同。怎么了?
最佳答案
会发生什么?
“super”调用按照您期望的方式工作,两个 mixin 的 read
方法都按预期顺序调用?
但是,您可以在 SaveMixin
和 ZipMixin
类读取方法中使用 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/