好的,这是基于 Alec Thomas 的答案的答案,经过修改和扩展:照顾多级继承和歧义。如果 _resolve 应该比简单的唯一性检查更复杂并且可能会更改,则它可以作为参数提供而不是类方法。
基类模块 bbb.py:
from __future__ import annotations
from abc import ABC, abstractmethod
from typing import Sequence, Type
class Base(ABC):
def __init__(self, *args, **kwargs):
...
@classmethod
def isit(cls, _s: str) -> bool:
return False
@classmethod
def from_str(cls, s: str, *args, **kwargs) -> Base:
subs = cls._findit(s)
sc = cls._resolve(s, subs)
return sc(*args, **kwargs)
@classmethod
def _findit(cls, s: str) -> Sequence[Type[Base]]:
subs = [cls] if cls.isit(s) else []
subs += [ssc for sc in cls.__subclasses__() for ssc in sc._findit(s)]
return subs
@classmethod
def _resolve(cls, s: str, subs: Sequence[Type[Base]]) -> Type[Base]:
if len(subs) == 0:
raise Exception(f'Cannot find subclass for {s}')
if len(subs) > 1:
raise Exception(
f'Cannot choose unique subclass for {s}: {subs}')
sc = subs[0]
return sc
class B(Base):
@classmethod
def isit(cls, s: str) -> bool:
res = s == 'b class'
return res
enter code here
派生类模块 ccc.py:
from bbb import Base
class C(Base):
@classmethod
def isit(cls, s: str) -> bool:
res = s == 'c class'
return res
class CC(Base):
@classmethod
def isit(cls, s: str) -> bool:
res = s == 'cc class'
return res
如何使用:
In [4]: from bbb import Base
In [5]: import ccc
In [6]: Base.from_str('b class')
Out[6]: <bbb.B at 0x1adf2665288>
In [7]: Base.from_str('c class')
Out[7]: <ccc.C at 0x1adf266a908>
In [8]: Base.from_str('cc class')
Out[8]: <ccc.CC at 0x1adf2665608>