首页 > 解决方案 > 超类中子类的自动初始化

问题描述

在 python 中的类内初始化子类中提出了类似的问题。那里的答案得出的结论是应该避免这种方法,我不确定这是否适用于以下情况,我想知道如何实现它,或者我应该做什么。

class Rational():
    def __init__(self, numerator, denominator):
        self.numerator = numerator
        self.denominator = denominator
        if self.denominator == 1:
            pass
            # >>> How to initialize as Integer in this case? <<<

    # ... More methods for rationals


class Integer(Rational):
    def __init__(self, value):
        super().__init__(value, 1)
        self.value = value

    # ... More methods for integers

在这个文件中,我有简单的整数和Rational数字类。由于所有Integers 都是有理数,Integer是有理数的子类,我们调用super()以允许整数调用有理数的方法。

Rational不过,如果每当 a用分母 1 初始化时,它可以被自动识别为整数,那就太好了。例如,我想x = Rational(4, 1)然后允许我调用x.integer_method()甚至Integer(4) == x返回 True。问题是,我不知道是否可以Integer从初始化程序中调用初始化程序,Rational因为我可能会陷入无限循环。

解决此问题的最佳方法是什么(通常不仅适用于整数和有理数,而且适用于任何父子类型,其中 Parent 的实例在初始化之前可能不会被识别为语义上等同于 Child 类型的成员?

标签: pythonclassinheritance

解决方案


用于__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作为 aRational意味着它some_integer.denominator = 3是有效的并产生Rational具有 1/3 初始值的 a。


推荐阅读