In python 3, how to create child class instance us

2019-03-06 01:48发布

问题:

For example, creating custom number types in following hierarchy

  • Number
    • Complex
    • Real
      • Int
      • Float

with logic in __new__ methods:

class Number:
    def __new__(cls, value):
        if isinstance(value, complex):
            return Complex(value)
        elif isinstance(value, (int, float)):
            return Real(value)
        else:
            raise TypeError('Ну ты и мудак!!!')

    def __init__(self, value):
        self.value = value


class Complex(Number):
    pass


class Real(Number):
    def __new__(cls, value):
        if isinstance(value, int):
            return Int(value)
        elif isinstance(value, float):
            return Float(value)
        else:
            raise TypeError('Ты больше не можешь вести себя так!!!')


class Int(Real):
    pass


class Float(Real):
    pass

And then, creating Float instance using Number constructor:

number = Number(6.6)
assert isinstance(number, Float)

This code will raise RuntimeError because of recursion limit. It is just example of desired behaviour. But I also tried to implement this using super and it is seems I misunderstand the world.

回答1:

Your Complex, Float and Int classes do not have __new__ methods, so they inherit these from Number; this is what is causing your infinite recursion.

You could test against the __class__ closure to see if you have a subclass or not:

class Number:
    def __new__(cls, value):
        if cls is not __class__:
            # Subclass, create an instance (invokes object.__new__)
            return super().__new__(cls)

        if isinstance(value, complex):
            return Complex(value)
        elif isinstance(value, (int, float)):
            return Real(value)
        else:
            raise TypeError('Ну ты и мудак!!!')

    def __init__(self, value):
        self.value = value

and do the same in Real:

class Real(Number):
    def __new__(cls, value):
        if cls is not __class__:
            # Subclass, create an instance
            return super().__new__(cls, value)

        if isinstance(value, int):
            return Int(value)
        elif isinstance(value, float):
            return Float(value)
        else:
            raise TypeError('Ты больше не можешь вести себя так!!!')

Demo:

>>> class Number:
...     def __new__(cls, value):
...         if cls is not __class__:
...             # Subclass, create an instance
...             return super().__new__(cls)
...         if isinstance(value, complex):
...             return Complex(value)
...         elif isinstance(value, (int, float)):
...             return Real(value)
...         else:
...             raise TypeError('Ну ты и мудак!!!')
...     def __init__(self, value):
...         self.value = value
... 
>>> class Real(Number):
...     def __new__(cls, value):
...         if cls is not __class__:
...             # Subclass, create an instance
...             return super().__new__(cls, value)
...         if isinstance(value, int):
...             return Int(value)
...         elif isinstance(value, float):
...             return Float(value)
...         else:
...             raise TypeError('Ты больше не можешь вести себя так!!!')
... 
>>> class Complex(Number):
...     pass
... 
>>> class Int(Real):
...     pass
... 
>>> class Float(Real):
...     pass
... 
>>> number = Number(6.6)
>>> isinstance(number, Float)
True