Python -- dynamic multiple inheritance

multiple-inheritance, oop, python

Solution

You do not have to create a new class for each instance. Don't create the new classes in `__new__` create them in `__metaclass__`. define a metaclass in the base or in the base_module. The two "variant" subclasses are easily saved as as class attributes of their genaric parent and then `__new__` just looks at the filename according to it's own rules and decides which subclass to return.

Watch out for `__new__` that returns a class other than the one "nominated" during the constructor call. You may have to take steps to invoke `__init__` from withing `__new__`

Subclasses will either have to:

- "register" themselves with a factory or parent to be found

- be `import`ed and then have the parent or factory find them through a recursive search of `cls.__subclasses` (might have to happen once per creation but that's probably not a problem for file handeling)

- found through the use of "setuptools" `entry_points` type tools but that requires more effort and coordination by the user

Problem

I'm seeking advice about design of my code. Introduction I have several classes, each represents one file type, eg: MediaImageFile, MediaAudioFile and generic (and also base class) MediaGenericFile. Each file have two variants: Master and Version, so I created these classes to define theirs specific behaviour. EDIT: Version represents resized/cropped/trimmed/etc variant of Master file. It's used mainly for previews. EDIT: The reason, why I want to do it dynamically is that this app should be reusable (it's Django-app) and therefore it should be easy to implement other MediaGenericFile subclass without modifying original code. What I want to do First of all, user should be able to register own MediaGenericFile subclasses without affecting original code. Whether file is version or master is easily (one regexp) recognizable from filename. ``` /path/to/master.jpg -- master /path/to/.versions/master_version.jpg -- version ``` Master/Version classes use some methods/properties of MediaGenericFile, like filename (you need to know filename to generate new version). MediaGenericFile extends LazyFile, which is just lazy File object. Now I need to put it together… Used design Before I start coding 'versions' feature, I had factory class MediaFile, which returns appropriate file type class according to extension: ``` >>> MediaFile('path/to/image.jpg') <<< <MediaImageFile 'path/to/image.jpg'> ``` Classes Master and Version define new methods which use methods and attributes of MediaGenericFile and etc. Approach 1 One approach is create dynamically new type, which inherits Master (or Version) and MediaGenericFile (or subclass). ``` 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) ``` Approach 2 Second approach is create method 'contribute_to_instance' in Master/Version and call it after creating new_class, but that's more tricky than I thought: ``` 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 ``` However, this doesn't work. There are still problems with calling Master/Version's methods. Questions What would be good way to implement this multiple inheritance? How is this problem called? :) I was trying to find some solutions, but I simply don't know how to name this problem. Thanks in advance! Note to answers ad larsmans Comparison and instance check wouldn't be problem for my case, because: Comparisons are redefined anyway ``` class MediaGenericFile(object): def __eq__(self, other): return self.name == other.name ``` I never need to check isinstance(MediaGenericFileVersion, instance). I'm using isinstance(MediaGenericFile, instance) and isinstance(Version, instance) and both works as expected. Nevertheless, creating new type per instance sounds to me as considerable defect. Well, I could create both variations dynamically in metaclass and then use them, something like: ``` >>> MediaGenericFile.version_class <<< <class MediaGenericFileVersion> >>> MediaGenericFile.master_class <<< <class MediaGenericFileMaster> ``` And then: ``` 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) ``` Final solution Finally the design pattern is factory class. MediaGenericFile subclasses are statically typed, users can implement and register their own. Master/Version variants are created dynamically (glued together from several mixins) in metaclass and stored in 'cache' to avoid perils mentioned by larsmans. Thanks everyone for their suggestions. Finally I understand the metaclass concept. Well, at least I think that I understand it. Push origin master…

Original source

Related problems