0

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.

2 Answers 2

1

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.

Sign up to request clarification or add additional context in comments.

5 Comments

This works great. Didn't know you can references classes in a dictionary and call them by accessing the key. That's pretty cool. I want getCodec because I'd like generating new codec objects to be contained to a single line of code (don't want codecClasses to exist where it doesn't need to be).
@jmkmay Everything is a first-class object in Python. Integers, functions, methods, classes, modules... it's all just a bunch of objects.
@zvone I personally feel we should be more explicit with that statement. for, in and = aren't objects afaik
@zvone there needs to be some form of map from strings as it's from user input
@zvone also I solve the kwargs thing by using this in constructor self.__dict__.update(**kwargs) so there aren't like 40 keyword arguments to each constructor
0

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

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.