Question: I have a zoo with animals, represented by objects. Historically, only the Animal class existed, with animal objects being created with e.g. x = Animal('Bello'), and typechecking done with isinstance(x, Animal). Recently, it has become important to distinguish between species. Animal has been made an ABC, and all animal objects are now instances of its subclasses such as Dog and Cat. This change allows me to create an animal object directly from one of the subclasses, e.g. with dog1 = Dog('Bello') in the code below. This is cheap, and I can use it as long as I know what kind of animal I'm dealing with. Typechecking isinstance(dog1, Animal) still works as before. However, for usibility and backwards compatibility, I also want to be able to call dog2 = Animal('Bello'), have it (from the input value) determine the species, and return a Dog instance - even if this is computationally more expensive. I need help with the second method. Here is my code: class Animal: def __new__(cls, name): if cls is not Animal: # avoiding recursion return super().__new__(cls) # Return one of the subclasses if name.lower() in ['bello', 'fido', 'bandit']: # expensive tests name = name.title() # expensive data correction return Dog(name) elif name.lower() in ['tiger', 'milo', 'felix']: # ... name = property(lambda self: self._name) present = lambda self: print(f"{self.name}, a {self.__class__.__name__}") # ... and (many) other methods that must be inherited class Dog(Animal): def __init__(self, name): self._name = f"Mr. {name}" # cheap data correction # ... and (few) other dog-specific methods class Cat(Animal): def __init__(self, name): self._name = f"Dutchess {name}" # cheap data correction # ... and (few) other cat-specific methods dog1 = Dog("Bello") dog1.present() # as expected, prints 'Mr. Bello, a Dog'. dog2 = Animal("BELLO") dog2.present() # unexpectedly, prints 'Mr. BELLO, a Dog'. Should be same.  Remarks:  In my use-case, the second creation method is by far the more important one.  What I want to achieve is that calling Animal return a subclass, Dog in this case, initialized with manipulated arguments (name, in this case)  So, I'm looking for a way to keep the basic structure of the code above, where the parent class can be called, but just always returns a child instance.  Of course, this is a contrived example ;)   Many thanks, let me know if more information is helpful.  Suboptimal solutions factory function def create_animal(name) -> Animal: # Return one of the subclasses if name.lower() in ['bello', 'fido', 'bandit']: name = name.title() return Dog(name) elif name.lower() in ['tiger', 'milo', 'felix']: # ... class Animal: name = property(lambda self: self._name) present = lambda self: print(f"{self.name}, a {self.__class__.__name__}") # ... and (many) other methods that must be inherited class Dog(Animal): # ...  This breaks backward compatibility by no longer allowing the creation of animals with a Animal() call. Typechecking is still possible I prefer the symmetry of being able to call a specific species, with Dog(), or use the more general Animal(), in the exact same way, which does not exist here. factory funcion, alternative Same as previous, but change the name of the Animal class to AnimalBase, and the name of the create_animal function to Animal. This fixes the previous problem, but breaks backward compatibility by no longer allowing typechecking with isinstance(dog1, Animal).

Answer: In the end, I went with a class decorator: # defining the decorator def dont_init_twice(Class): """Decorator for child classes of Animal, to allow Animal to return a child instance.""" original_init = Class.__init__ Class._initialized = False def wrapped_init(self, *args, **kwargs): if not self._initialized: object.__setattr__(self, "_initialized", True) #works also on frozen dataclasses original_init(self, *args, **kwargs) Class.__init__ = wrapped_init return Class # decorating the child classes @dont_init_twice class Dog: def __init__(self, name): self._name = f"Mr. {name}" @dont_init_twice class Cat: def __init__(self, name): self._name = f"Dutchess {name}"  IMO, this is the cleanest and least invasive solution.