Metaclass vs inheritance for predefined class creation

北战南征 提交于 2020-12-13 09:27:15

问题


I'm writing some code for a program that will be able to run some software, read the inputs/outputs and do some data processing along the way. For example: (this is not the real case, but just to give an idea)

class MusicFile(object):
   extension = [""]

   def setup(self):
       #set up the data

   def runsoftware(self):
       #play the song

class MP3(MusicFile):
   extension = [".mp3"]

   def setup(self):
       #setupMP3file

   def runsoftware(self):
       #runMP3software

I have about 4 general classes, and then file extensions will be defined and treated differently. Each class has a defined number of methods that are required for it to work. For now, only one or two file extensions will be supported, but I want to leave a structure done where adding more extensions is easy (perhaps intuitive) for others to do.

I've been coding for not so long and I was given this task, which I want to do the best possible way.

Is the use of metaclasses appropriate for this case? In order to set up some strict classes that must contain the methods I have previously defined, that way it will be uniform across all of them. Should I stick to just simple inheritance?

I want to be able to maybe have all the subclasses of MusicFile registered to be able to instantiate the right one (given a file path, for example).

I have read about metaclasses, and they seem like a good fit for this case, but since I've read also that they don't need to be used if you don't know them very well, I want to ask for your expert opinion.


回答1:


This answer only applies for Python 3.6+. If you have the option to upgrade to the most recent Python version, there are numerous reasons why you should and the following may be one of them.

Python 3.6 introduced the __init_subclass__ hook that is exectued after a class is subclassed.

class MusicFile:
    _register = {}

    def __init_subclass__(cls, **kwargs):
        if not hasattr(cls, 'extension'):
            raise ValueError(cls.__name__ + ' has no extension')

        MusicFile._register.update({e: cls for e in cls.extension})

    @classmethod
    def get_class_by_extension(cls, ext):
        return cls._register.get(ext)

Example

class MP3(MusicFile):
    extension = ['mp3']

class MIDI(MusicFile):
    extension = ['midi']

MusicFile.get_class_by_extension('mp3') # <class '__main__.MP3'>
MusicFile.get_class_by_extension('midi') # <class '__main__.MIDI'>

Note that this is very similar to the factory approach suggested by bipll, but in a more straightforward and maintainable way.




回答2:


I want to be able to maybe have all the subclasses of MusicFile registered to be able to instantiate the right one (given a file path, for example).

So you probably want to implement a Factory?

This factory can probably be auto-built with metaclasses. One serious issue I see is using class's own extension member to register it, so to me, it is easier done with metaclasses' eternal competition, decorators:

class MusicFile(object):
    known_types = {}

    @staticmethod
    def processor(ext):
        return MusicFile.known_types[ext]()

def file_types(*exts):
    def registered_class(cls):
        for ext in exts: MusicFile.known_types[ext] = cls
        return cls
    return registered_class

@file_types('mp3')
class Mp3(MusicFile):
    def greet(self):
        print 'Hi .mp3!'

@file_types('mid', 'midi')
class Midi(MusicFile):
    def greet(self):
        print 'Hi, Music Instruments Digital Interface!'

pcsor = MusicFile.processor('mp3')
pcsor.greet()                      # Hi .mp3!
pcsor = MusicFile.processor('mid')
pcsor.greet()                      # Hi, Music Instruments Digital Interface!
pcsor = MusicFile.processor('midi')
pcsor.greet()                      # Hi, Music Instruments Digital Interface!
pcsor = MusicFile.processor('s3m') # KeyError: 's3m'
pcsor.greet()


来源:https://stackoverflow.com/questions/51923555/metaclass-vs-inheritance-for-predefined-class-creation

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!