我将在示例中使用以下配置字典:
configA = {
'kind': 'A',
'parameters': {}
}
configB = {
'kind': 'B',
'parameters': {
'name': 'Foo'
}
}
configC = {
'kind': 'C',
'parameters': {
'age': '17'
}
}
configD = { # ChildD inherits from both ChildC, ChildA
'kind': 'D',
'parameters': {
'name': 'Bar',
'age': '17'
}
}
__subclasses__
,如果它们存在的话。我定义
extract_all_subclasses
作为函数,因为它可以用于(几乎任何)类。
def extract_all_subclasses(cls):
output = set()
# I use the set to avoid repeating classes in case of multiple inheritance -
# but lists is possible alternative, just filter it later
subclasses = set(cls.__subclasses__())
output = output.union(subclasses)
for subcls in subclasses:
if subcls.__subclasses__(): # if there's some subclasses
for subcls_inner in subcls.__subclasses__():
output = output.union(extract_all_subclasses(subcls))
else: # just append
output = output.union(subclasses )
return output
上
Base
:
from abc import ABC, abstractmethod
class Base(ABC):
@abstractmethod
def say(self):
pass
@classmethod
def from_config(cls, config):
subclasses = extract_all_subclasses(cls) # extracting all the subclasses
subcls = {c.kind: c for c in subclasses}[config['kind']] # get one from dictionary
return subcls(**config['parameters'])
目前还没有子类:
extract_all_subclasses(Base)
Out:
set()
但是:
class ChildA(Base):
kind = 'A'
def say(self):
return 'Hello, I am A'
class ChildB(Base):
kind = 'B'
def __init__(self, name, *args, **kwargs):
self._name = name
def say(self):
return 'Hello, I am {}'.format(self._name)
class ChildC(ChildB):
kind = 'C'
def __init__(self, age, *args, **kwargs):
self._age = age
def say(self):
return 'Hello, I am Base\'s grandson, my age is {}'.format(self._age)
class ChildD(ChildC, ChildA):
kind = 'D'
def __init__(self, age, name):
self._age = age
self._name = name
def say(self):
return 'Hello, I am Base\'s grandson, my age is {} and my name is {}'.format(self._age, self._name)
extract_all_subclasses(Base)
Out:
{__main__.ChildA, __main__.ChildB, __main__.ChildC, __main__.ChildD}
所以,即使在多重继承的情况下,它也可以正常工作。过来看:
print(Base.from_config(configA).say())
print(Base.from_config(configB).say())
print(Base.from_config(configC).say())
print(Base.from_config(configD).say())
Out:
Hello, I am A
Hello, I am Foo
Hello, I am Base's grandson, my age is 17
Hello, I am Base's grandson, my age is 17 and my name is Bar
我刚刚删除了对超类构造函数的调用,因为如果它需要一个您没有提供的参数,或者您为它提供了一个未执行的参数,那么它可能会导致错误。但这只在嵌套子类的情况下是危险的,而且很容易避免。我不明白你为什么需要它们——如果你重新定义了网络上的行为
__init__
(可能只是简化而已)。
可能出现的错误示例(错误代码)
基础
class ChildA(Base):
kind = 'A'
def say(self):
return 'Hello, I am A'
class ChildB(Base):
kind = 'B'
def __init__(self, name):
super().__init__()
self._name = name
def say(self):
return 'Hello, I am {}'.format(self._name)
class ChildC(ChildB):
kind = 'C'
def __init__(self, age):
super().__init__()
self._age = age
def say(self):
return 'Hello, I am Base\'s grandson, my age is {}'.format(self._age)
print(Base.from_config(configA).say())
print(Base.from_config(configB).say())
print(Base.from_config(configC).say())
Out:
Hello, I am A
Hello, I am Foo
---------------------------------------------------------------------------
...
<ipython-input-4-6932e3173d3f> in __init__(self, age)
21
22 def __init__(self, age):
---> 23 super().__init__()
24 self._age = age
25
TypeError: __init__() missing 1 required positional argument: 'name'
编辑
回答后一个问题,是的,但有限制。您可以像往常一样构造项目,但是如果您希望它工作,您应该将子类导入到名称空间中。否则就不行了。
我使用以下结构:
.
âââ __init__.py
âââ a.py
âââ b.py
âââ base.py
âââ c.py
âââ d.py
âââ extractor.py
âââ main.py
其中base.py是
基础
,a.py,b.py,c.py,d.py-的定义
ChildA
ChildB
ChildC
,
ChildD
子类
提取器函数和main具有以下列表:
from subclasses.base import Base
from subclasses.a import ChildA
from subclasses.b import ChildB
from subclasses.c import ChildC
from subclasses.d import ChildD
config = [
{
'kind': 'A',
'parameters': {}
},
{
'kind': 'B',
'parameters': {
'name': 'Foo'
}
},
{
'kind': 'C',
'parameters': {
'age': '17'
}
},
{ # ChildD inherits from both ChildC, ChildA
'kind': 'D',
'parameters': {
'name': 'Bar',
'age': '17'
}
}
]
if __name__ == '__main__':
print(
[
Base.from_config(cfg).say() for cfg in config
]
)
-它按计划工作。但是如果我忽略了
ChildX
-它引发了一个关键错误-因为该案例中的child没有导入和删除
提取所有子类
无法提取它们-它们不存在。