概括
TLDR:是否有可能调用/实例化基类实际上返回一个初始化的子类实例?
例子
考虑这个Animal
基类Cat
和Dog
子类:
from abc import ABC, abstractmethod
class Animal(ABC):
@property
@abstractmethod
def weight(self) -> float:
"""weight of the animal in kg."""
...
class Dog(Animal):
def __init__(self, weight: float = 5):
if not (1 < weight < 90):
raise ValueError("No dog has this weight")
self._weight = weight
weight: float = property(lambda self: self._weight)
class Cat(Animal):
def __init__(self, weight: float = 5):
if not (0.5 < weight < 15):
raise ValueError("No cat has this weight")
self._weight = weight
weight: float = property(lambda self: self._weight)
这按预期工作:
c1 = Cat(0.7) # no problem
c2 = Cat(30) # ValueError
现在,我想扩展它,以便调用Animal
类应该返回它的一个子类,即第一个不会引发错误的子类。
所以,我想c3 = Animal(0.7)
返回一个Cat
实例。
试图
我知道在实例化基类时如何从子类返回一个实例,但前提是它可以在运行之前__init__
确定,它是哪一个。
所以,这行不通……
class Animal(ABC):
def __new__(cls, *args, **kwargs):
if cls in cls._subclasses():
return object.__new__(cls)
for cls in [Dog, Cat]: # prefer to return a dog
try:
return object.__new__(cls)
except ValueError:
pass
@property
@abstractmethod
def weight(self) -> float:
"""weight of the animal in kg."""
...
...因为ValueError
只有在实例已经创建并返回时才会引发:
c3 = Animal(0.7) # ValueError ('no dog has this weight') instead of Cat instance.
有没有办法做到这一点?
当前的解决方法
这可行,但与类分离,并且感觉集成度差/高度耦合。
def create_appropriate_animal(*args, **kwargs) -> Animal:
for cls in [Dog, Cat]:
try:
return cls(*args, **kwargs)
except ValueError:
pass
raise("No fitting animal found.")
c4 = create_appropriate_animal(0.7) # returns cat
编辑:
- 感谢@chepner 的
__subclasses__()
建议;我已将其整合到问题中。