1

Suppose I have a class Fruit and two Subclasses of it Orange(Fruit) and Banana(fruit)

Fruit has an initializer, and I pass some parameters to it. But I don't want it to just create and return a fruit necessarily, but based upon the parameters passed to it, to possibly return one of several different subclasses of it. For example:

Fruit(color="yellow") might return a Banana object whereas Fruit(color="orange") would return an Orange.

How can I do this? I can't do it in __init__ because the class has already been created. I tried doing it in __new__ but this wound up messy because my could would create the subclass (as expected), but would then wind-up recursivley calling the Superclass's __new__.

My workaround has been to just define a Fruit.create() static method which creates the appropriate subclass, but this seems a little un-Pythonic.

Is there a "correct" way to do this?

4
  • docs.python.org/3/reference/datamodel.html#metaclasses ? Commented Dec 21, 2021 at 15:53
  • 2
    This seems a little backwards, Fruit is a class so why would you expect it to when initalised return a different class. That seems way more unpythonic than creating a static method that gives you the right fruit class Commented Dec 21, 2021 at 16:08
  • @ChrisDoyle If you told someone to give you a Fruit, and they gave you a Banana, would they be wrong? ;-) Commented Dec 21, 2021 at 19:11
  • 1
    I am not against the factory method of asking from a fruit. what seemed back wards to me was the idea of var = foo() where foo is a class but end up returning not an instance of foo from its init but an instance of bar. however calling a factory method in fruit to get a subclass makes sense Commented Dec 22, 2021 at 10:00

1 Answer 1

1

My point of view is the same as Chris Doyle.

You can use factory mode or your Fruit.create(), I think they are very good

If you want to use your ideas, you can do so


class Meta(type):
    def __call__(cls, *args, **kwargs):
        if cls is Fruit:
            color = kwargs.pop('color', None)
            if color is None:
                raise ValueError("missing keyword argument `color`")
            match color:
                case 'yellow':
                    return Banana()
                case 'orange':
                    return Orange()
                case _:
                    raise ValueError("invalid color: %s" % color)

        return super().__call__(*args, **kwargs)


class Fruit(metaclass=Meta):
    def __init__(self, *, color=None):  # noqa
        pass


class Banana(Fruit):
    pass


class Orange(Fruit):
    pass


if __name__ == '__main__':
    print(Fruit(color='yellow'))
    print(Fruit(color='orange'))
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.