Automati c Инициализация подкласса в суперклассе - PullRequest
0 голосов
/ 17 июня 2020

Аналогичный вопрос был задан в Инициализировать подкласс внутри класса в python. В ответах было сказано, что такого подхода следует избегать, я не уверен, верно ли это для следующего случая, и я хотел бы знать, как этого можно достичь, или что мне следует делать вместо этого. 1004 *

В этом файле у меня есть простые классы для целых чисел и Rational чисел. Поскольку все Integer являются рациональными, Integer является подклассом рациональных чисел, и мы вызываем super(), чтобы методы для рациональных чисел вызывались целым числом.

Было бы неплохо, если бы всякий раз, когда a Rational инициализировалось знаменателем 1, оно могло автоматически распознаваться как целое число. Например, я хотел бы, чтобы x = Rational(4, 1) затем разрешил мне вызвать x.integer_method() или даже Integer(4) == x вернуть True. Проблема в том, что я не знаю, могу ли я вызвать инициализатор Integer из инициализатора Rational, потому что я могу попасть в бесконечный l oop.

Каков наилучший способ решить эту проблему (в общем случае, который работает не только для целых чисел и рациональных чисел, но и для любого типа Parent-Child, где экземпляр Parent может не распознаваться как семантически эквивалентный члену типа Child до времени инициализации?

1 Ответ

1 голос
/ 17 июня 2020

Используйте __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 начального значения.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...