使用type(name, bases, dict),因为您基本上是在使用元类,但是以一种简化的方式。
class X:
pass
class Y:
pass
XY = type("xy", (X, Y), {})
print(XY.__mro__)
# (<class '__main__.xy'>, <class '__main__.X'>, <class '__main__.Y'>, <class 'object'>)
(__mro__ + explanation)
将它与itertools.combinations() 结合起来动态生成所有可能的碱基(或itertools.permutations() 或itertools.product() 甚至):
combinations([X, Y], 2)
# <itertools.combinations object at 0x7fbbc2ced4a0>
list(combinations([X, Y], 2))
# [(<class '__main__.X'>, <class '__main__.Y'>)]
因此有点像这样,如果您将它们放在单独的包中:
import your_package
classes = [
getattr(your_package, cls)
for cls in dir(your_package)
if filter_somehow(getattr(your_package, cls))
]
combined = {}
for comb in combinations(classes, 2):
name = "".join(cls.__name__ for cls in comb)
combined[name] = type(name, comb, {})
print(combined)
对于文件外部的引用,要么将该字典导入为常量,要么直接使用globals() 而不是combined,您将在模块的命名空间中动态创建此类变量。
对于过滤isinstance(<class>, type) 应该会有所帮助。
或者您可以通过例如过滤它们__name__ attribute) 或者这样的包里没有其他东西+忽略所有dunder parts:
# empty test.py file
import test
dir(test)
# ['__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__']
...或为您的所有类创建一些基类,然后使用issubclass() 仅从dir(package) 获取这些。
对于普通功能,这应该足够了:
# f(B_x, [A_1, A_2, ..., A_n])
def f(base, mixins):
classes = []
for mixin in mixins:
bases = (mixin, base)
name = "".join(cls.__name__ for cls in bases)
classes.append(type(name, bases, {}))
return classes
# example
class Base: pass
class A: pass
class B: pass
class C: pass
f(Base, [A,B,C])
# [<class '__main__.ABase'>, <class '__main__.BBase'>, <class '__main__.CBase'>]
让我们让它扩展一点:
# f(B_x, [A_1, A_2, ..., A_n])
from typing import Generic, Iterable
from itertools import combinations
def f(base: Generic, mixins: Iterable, base_count: int = 1):
classes = []
for mixin_comb in combinations(mixins, base_count):
bases = (*mixin_comb, base)
name = "".join(cls.__name__ for cls in bases)
classes.append(type(name, bases, {}))
return classes
f(Base, [A,B,C], 2)
# [<class '__main__.ABBase'>, <class '__main__.ACBase'>, <class '__main__.BCBase'>]