Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python: Programatically create subclasses based on __init__ values

I have a base class from which I would like to make many subclasses. The subclasses differ only in the arguments used to call the base class during instantiation. The example below shows how to make a subclass Apple. Is there a way to do this programatically, without having to code the subclass' __init__ method? This seems like a job for metaclasses, but in this case I cannot modify the base class.

apple = {'color': 'red', 'shape': 'sphere'}
pear = {'color': 'yellow', 'shape': 'cone'}
melon = {'color': 'green', 'shape': 'prolate'}

class Fruit(object):
    def __init__(self, color, shape):
        self.color = color
        self.shape = shape        

class Apple(Fruit):
    def __init__(self):
        Fruit.__init__(self, **apple)
like image 987
user807435 Avatar asked Dec 20 '22 01:12

user807435


2 Answers

See the type() function.

def make_fruit(name, kwargs):
    def my_init(self):
        Fruit.__init__(self, **kwargs)
    return type(name, (Fruit,), {'__init__': my_init})

Apple = make_fruit('Apple', apple)
like image 71
user9876 Avatar answered Feb 19 '23 21:02

user9876


Using type:

class Fruit(object):
    def __init__(self, color, shape):
        self.color = color
        self.shape = shape        

apple = {'color': 'red', 'shape': 'sphere'}
pear = {'color': 'yellow', 'shape': 'cone'}
melon = {'color': 'green', 'shape': 'prolate'}

g = globals()
for clsname, attrs in [('Apple', apple), ('Pear', pear), ('Melon', melon)]:
    def temp(attrs):
        g[clsname] = type(clsname, (Fruit,), {
            '__init__': lambda self: Fruit.__init__(self, **attrs)
        })
    temp(attrs)

>>> a = Apple()
>>> p = Pear()
>>> m = Melon()
>>> assert a.color == 'red' and a.shape == 'sphere'
>>> assert p.color == 'yellow' and p.shape == 'cone'
>>> assert m.color == 'green' and m.shape == 'prolate'
like image 21
falsetru Avatar answered Feb 19 '23 20:02

falsetru