jmkmay
jmkmay

Reputation: 1506

Pythonic way of instantiating an object based on user input

I'm writing an OOP style Python wrapper for FFmpeg. I've created the concept of multiple codec Classes, each with their own set of instance variables. For instance, the AACCodec class might have a cutoff_frequency instance variable like:

class AACCodec(Codec):

    def __init__(self, **kwargs):
        self.cutoff_frequency = 20000

where the FLAC Codec class might have compression_ratio instance variable, like:

class FLACCodec(Codec):

    def __init__(self, **kwargs):
        self.compression_ratio = 0.78

Basically, elsewhere in my code (in ffmpeg.py) I'd like to be able to create the correct codec object by making a single call such as:

import ffcodecs
newCodec = somefunctioncall('aac', 'key1'=value1, 'key2'=value2)

The way I considered implementing this was to use a function inside ffcodecs.py which returns the codec object based on input string:

def getCodec(name, **kwargs):
     codecObjects = {
        'aac' : AACCodec(**kwargs),
        'ogg' : OGGCodec(**kwargs),
        'flac': FLACCodec(**kwargs)
    }

    return codecObjects[name]

The problem with this is that whenever I call getCodec() it creates an instance of each codec when codecObjects is declared. I have logic in each codec __init__ to check if **kwargs matches the object's instance variables (self.cutoff_frequency, self.compression_ratio etc.) and error if it doesn't.

What's the most Pythonic way of instantiating the correct object and ONLY the correct object in a single call?

One solution would be to just have a set of if/else if statements to match each codec string and return each object accordingly, but if I end up maintaining a bunch of different codecs, I don't want a hundred if/else statements each time I instantiate a new object.

There's got to be a more elegant way of doing this.

Upvotes: 0

Views: 176

Answers (2)

joel
joel

Reputation: 7867

Perhaps not the best, but you could do

def getCodec(name, **kwargs):
    codecObjects = {
        'aac' : AACCodec,
        'ogg' : OGGCodec,
        'flac': FLACCodec
    }

    return codeObjects[name](**kwargs)

After all, you are trying to map a string to a class, but only need to instantiate one

Upvotes: 0

zvone
zvone

Reputation: 19362

There are several questions here.

This one is the most straight-forward:

The problem with this is that whenever I call getCodec() it creates an instance of each codec when codecObjects is declared.

Change the function so that it does not instantiate all of the classes:

def getCodec(name, **kwargs):
     codecClasses = {
        'aac' : AACCodec,
        'ogg' : OGGCodec,
        'flac': FLACCodec
    }

    return codecClasses[name](**kwargs)

Is it the best way to do it? Well, there are things I would do differently...

For instance, I would explicitly define the arguments:

def __init__(self, cutoff_frequency=20000):
    self.cutoff_frequency = cutoff_frequency

You can still use **kwargs in getCodec.

On the other hand, I don't see why you need getCodec at all. In the end, you have to call it with arguments specific to the current codec, so you already have to know which codec to instantiate. So you can do it explicitly.

Upvotes: 1

Related Questions