Используйте __new__
, чтобы определить, как создается класс, включая создание других классов. Избегайте определения __init__
, поскольку он не вызывается автоматически, когда __new__
возвращает объект другого типа. Поскольку эта схема сильно связывает классы вместе, Integer
может избежать вызова super().__new__
для простоты.
class Rational():
def __new__(cls, numerator, denominator=1):
if denominator == 1:
return Integer(numerator)
self = object.__new__(cls)
self.numerator = numerator
self.denominator = denominator
return self # new always returns an instance
def __repr__(self):
return f'Rational({self.numerator}/{self.denominator})'
class Integer(Rational):
denominator = 1 # as a Rational, Integer must expose numerator and denominator
def __new__(cls, value):
self = object.__new__(cls)
self.numerator = value
return self
def __repr__(self):
return f'Integer({self.numerator})'
Этого достаточно, чтобы динамически создать соответствующий подкласс:
>>> Rational(12, 3)
Rational(12/3)
>>> Rational(15, 1)
Integer(15)
В идеале, такие классы должны быть неизменными; в противном случае, Integer
, являющееся Rational
, подразумевает, что some_integer.denominator = 3
допустимо и дает Rational
с 1/3 начального значения.