Tom*_*ich 8 python oop multiple-inheritance
我正在寻求有关设计代码的建议.
我有几个类,每个类代表一种文件类型,例如:MediaImageFile,MediaAudioFile和generic(以及基类)MediaGenericFile.
每个文件都有两个变体:Master和Version,因此我创建了这些类来定义它们的特定行为.编辑:版本表示主文件的调整大小/裁剪/修剪/等变体.它主要用于预览.
编辑: 为什么我想动态地这样做的原因是这个应用程序应该是可重用的(它是Django-app),因此应该很容易实现其他MediaGenericFile子类而不修改原始代码.
首先,用户应该能够注册自己的MediaGenericFile子类而不会影响原始代码.
文件是版本还是主文件很容易(一个正则表达式)可以从文件名识别.
/path/to/master.jpg -- master
/path/to/.versions/master_version.jpg -- version
Run Code Online (Sandbox Code Playgroud)Master/Version类使用MediaGenericFile的一些方法/属性,如filename(您需要知道filename才能生成新版本).
MediaGenericFile扩展了LazyFile,它只是懒惰的File对象.
现在我需要把它放在一起......
在我开始编写"版本"功能之前,我有工厂类MediaFile,它根据扩展名返回适当的文件类型类:
>>> MediaFile('path/to/image.jpg')
<<< <MediaImageFile 'path/to/image.jpg'>
Run Code Online (Sandbox Code Playgroud)
类Master和Version定义了使用MediaGenericFile等方法和属性的新方法.
一种方法是创建动态新类型,它继承Master(或Version)和MediaGenericFile(或子类).
class MediaFile(object):
def __new__(cls, *args, **kwargs):
... # decision about klass
if version:
bases = (Version, klass)
class_name = '{0}Version'.format(klass.__name__)
else:
bases = (Master, klass)
class_name = '{0}Master'.format(klass.__name__)
new_class = type(class_name, bases, {})
...
return new_class(*args, **kwargs)
Run Code Online (Sandbox Code Playgroud)
第二种方法是在Master/Version中创建方法'contribution_to_instance'并在创建new_class后调用它,但这比我想象的更棘手:
classs Master(object):
@classmethod
def contribute_to_instance(cls, instance):
methods = (...)
for m in methods:
setattr(instance, m, types.MethodType(getattr(cls, m), instance))
class MediaFile(object):
def __new__(*args, **kwargs):
... # decision about new_class
obj = new_class(*args, **kwargs)
if version:
version_class = Version
else:
version_class = Master
version_class.contribute_to_instance(obj)
...
return obj
Run Code Online (Sandbox Code Playgroud)
但是,这不起作用.调用Master/Version的方法仍然存在问题.
实现这种多重继承的好方法是什么?
这个问题怎么称呼?:)我试图找到一些解决方案,但我根本不知道如何命名这个问题.
提前致谢!
对我的情况进行比较和实例检查不会有问题,因为:
无论如何都要重新定义比较
class MediaGenericFile(object):
def __eq__(self, other):
return self.name == other.name
Run Code Online (Sandbox Code Playgroud)我永远不需要检查isinstance(MediaGenericFileVersion,instance).我正在使用isinstance(MediaGenericFile,instance)和isinstance(Version,instance),两者都按预期工作.
然而,每个实例创建新类型听起来像是一个相当大的缺陷.
好吧,我可以在元类中动态创建两个变体然后使用它们,例如:
>>> MediaGenericFile.version_class
<<< <class MediaGenericFileVersion>
>>> MediaGenericFile.master_class
<<< <class MediaGenericFileMaster>
Run Code Online (Sandbox Code Playgroud)
然后:
class MediaFile(object):
def __new__(cls, *args, **kwargs):
... # decision about klass
if version:
attr_name = 'version_class'
else:
attr_name = 'master_class'
new_class = getattr(klass, attr_name)
...
return new_class(*args, **kwargs)
Run Code Online (Sandbox Code Playgroud)
最后,设计模式是工厂级的.MediaGenericFile子类是静态类型的,用户可以实现和注册自己的子类.主/版本变体是在元类中动态创建的(从多个mixin粘合在一起)并存储在'cache'中以避免larsmans提到的 风险.
谢谢大家的建议.最后我理解了元类概念.好吧,至少我认为我理解它.推送原始主...
您不必为每个实例创建一个新类。不要在__new__create 它们 in中创建新类__metaclass__。在基类或 base_module 中定义元类。这两个“变体”子类很容易保存为其通用父类的类属性,然后__new__只需根据它自己的规则查看文件名并决定返回哪个子类。
请注意,__new__返回的类不是构造函数调用期间“指定”的类。 您可能需要采取措施__init__从内部调用__new__
子类必须:
import编辑,然后让父级或工厂通过递归搜索找到它们cls.__subclasses(每次创建可能必须发生一次,但这对于文件处理来说可能不是问题)entry_points类型的工具找到,但这需要用户付出更多的努力和协调