dynamically creating a subclass - Printable Version +- Python Forum (https://python-forum.io) +-- Forum: Python Coding (https://python-forum.io/forum-7.html) +--- Forum: General Coding Help (https://python-forum.io/forum-8.html) +--- Thread: dynamically creating a subclass (/thread-6333.html) |
dynamically creating a subclass - sidereal - Nov-16-2017 I have a situation where I know what superclass I am creating (using the superclass sort of like a java abstract class), and will be receiving an string argument to control which type of subclass I return. Here is a simplified snippet that accomplishes what I'm trying to do: class Person: def __init__(self, pet_types): self.pets = [Pet().factory(t) for t in pet_types] def get_pets(self): print([pet.name for pet in self.pets]) class Pet: def factory(self, t): klass = next((cls for cls in self.__class__.__subclasses__() if cls.__name__ == t), self.__class__) return klass() def __init__(self): self.name = 'unknown' class Cat(Pet): def __init__(self): self.name = 'Garfield' class Dog(Pet): def __init__(self): self.name = 'Fido' Person(['Cat', 'Dog', 'Bird']).get_pets()It works, but I'm wondering if there is a more pythonic (or just, programming practices in general) way of doing this. Or if this is a "code smell". For context, I'm receiving a json file which defines object types. RE: dynamically creating a subclass - nilamo - Jan-04-2018 Do they HAVE to be strings? I think the more pythonic way would be to just pass classes that should be instanciated: Person([Cat, Dog, Bird]).get_pets() RE: dynamically creating a subclass - Gribouillis - Jan-04-2018 If you really need to convert strings, you could use a metaclass to collect the subclasses class PetMeta(type): pet_classes = {} def __new__(cls, clsname, superclasses, attributedict): if clsname in cls.pet_classes: raise ValueError( "Redefinition of Pet subclass '{}'".format(clsname)) tp = type.__new__(cls, clsname, superclasses, attributedict) cls.pet_classes[clsname] = tp return tp def pet_class(name): return PetMeta.pet_classes[name] class Person: def __init__(self, pet_types): self.pets = [pet_class(t)() for t in pet_types] class Pet(metaclass=PetMeta): pass class Dog(Pet): pass class Cat(Pet): pass class Bird(Pet): pass john = Person(['Dog', 'Cat']) print(john.pets)The drawback of __subclasses__() is that it only knows direct subclasses.
|