我正在写一个代表文件的类。此类具有一些可选功能:通常文件存储在内存中,但是有时需要将它们存储在磁盘上,有时我想将它们存储为zip文件,依此类推。我决定使用mixin,可以在其中子类化File类,并在某些情况下需要添加mixins。在这种情况下,读/写文件是一项需要做一些准备和一些清理工作的操作(我需要压缩文件,例如执行一些写操作,然后再次压缩更新版本)。为此,我想使用自定义上下文管理器,以确保即使with语句中间存在异常或return语句,也可以执行这些操作。这是我的代码:
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,然后直接传递给超类(省略中间mixin)。我用python 2和3进行了测试,结果相同。怎么了?
答案 0 :(得分:0)
“ super”调用按您预期的方式工作,两个mixin的read
方法均按预期顺序调用?
但是,您在with self:
和SaveMixin
类的读取方法中都使用了ZipMixin
。
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__
和__exit__
方法两次都是SaveMixin
类。
似乎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
答案 1 :(得分:0)
您要传递的self
的类型为SaveZipFile
。如果您查看SaveZipFile
的MRO(方法解析顺序),则类似这样:
object
/ | \
SaveMixin ZipMixin File
\ | /
SaveZipFile
当您致电with self:
时,最终会致电self.__enter__()
。并且由于self
的类型为SaveZipFile
,因此当我们查看该类的MRO路径时(在图形上“向上”,从左到右搜索路径),在第一个路径(在SaveMixin
中)。
如果要提供zip并将功能另存为mixins,则最好使用try/finally
模式,并让super
确定应调用哪个类的方法以及以什么顺序调用:
class File(object):
def read(self):
return "file content"
class ZipMixin(object):
def read(self):
try:
print("Unzipping")
return super(ZipMixin, self).read()
finally:
print("Zipping back")
class SaveMixin(object):
def read(self):
try:
print("Loading to memory")
return super(SaveMixin, self).read()
finally:
print("Removing from memory, saving on disk")
class SaveZipFile(SaveMixin, ZipMixin, File):
pass