I'd like to override __new__ in a child class to force it to create instances of the parent class, providing defaults for some of the keyword arguments of the parent.
It's designed to just be some nice sugar when creating parent class instances.
class Person:
def __init__(self, name: str, profession: str):
self.name = name
self.profession = profession
class Teacher(Person):
def __new__(self, name: str):
return Person(name=name, profession="teacher")
rita = Person("Rita", profession="surgeon")
bob = Teacher("Bob") # type checkers think `bob` is a Teacher.
As this comment rightly points out, a user would be surprised to request a Teacher but not get one:
>>> bob = Teacher("Bob")
>>> isinstance(bob, Teacher)
False # ???
This is a good argument against this pattern.
A more typical approach might involve some helper methods on the parent class to assist with creation:
class Person:
def __init__(self, name: str, profession: str):
self.name = name
self.profession = profession
@classmethod
def create_teacher(cls, name: str) -> Person:
return cls(name=name, profession="teacher")
This means users of your code can create teachers without having to remember the magic string "teacher". Instead they can use autocomplete:

Perhaps the OO-wizards on StackOverflow can come up with an even more appealing method than this, but this satisfies my requirements, and my type checker is happy that bob is indeed a Person.
Here's a suggestion that's a slight tweak on @LondonRob's answer: if possible, use an Enum instead of magic strings to specify a Person's profession.
from enum import Enum, auto
from typing import TypeVar
class Profession(Enum):
TEACHER = auto()
SURGEON = auto()
P = TypeVar('P')
class Person:
def __init__(self, name: str, profession: Profession) -> None:
self.name = name
self.profession = profession
@classmethod
def create_teacher(cls: type[P], name: str) -> P:
return cls(name=name, profession=Profession.TEACHER)
This has the advantage that you can test the person's profession much more cleanly:
>>> Bob = Person.create_teacher('Bob')
>>> Bob.profession is Profession.TEACHER
True
In fact, you could even generalise your alternative constructor by making it an instance method of your Profession class, rather than a classmethod on your Person class:
from __future__ import annotations
from enum import Enum, auto
class Person:
def __init__(self, name: str, profession: Profession) -> None:
self.name = name
self.profession = profession
class Profession(Enum):
TEACHER = auto()
SURGEON = auto()
def create(self, name: str) -> Person:
return Person(name=name, profession=self)
In usage:
>>> Bob = Profession.TEACHER.create(name='Bob')
>>> Suzie = Profession.SURGEON.create(name='Suzie')
As an aside, if your class is essentially a wrapper around structured data, then you could also consider using dataclasses (whether or not you go the Enum route):
from enum import Enum, auto
from dataclasses import dataclass
from typing import TypeVar
class Profession(Enum):
TEACHER = auto()
SURGEON = auto()
P = TypeVar('P')
@dataclass
class Person:
name: str
profession: Profession
@classmethod
def create_teacher(cls: type[P], name: str) -> P:
return cls(name=name, profession=Profession.TEACHER)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With