python - 基于混合列表的交叉以编程方式定义 python 类的好方法?
问题描述
现在,我有两组 python mixin,A 和 B。
让(A_1, A_2, ..., A_i, ...)
和(B_1, B_2, ..., B_i, ...)
, 分别是这些集合中的元素。
我想创建作为这两个 mixin 集之间的交叉的类,并且能够按名称引用交叉的元素。python中有什么好方法可以做到这一点吗?具体来说,如果说我有 mixins
class A_1():
pass
class A_2():
pass
class B_1():
pass
class B_2():
pass
我希望能够生成类:
class A_1B_1(A_1, B_1):
pass
class A_1B_2(A_1, B_2):
pass
class A_2B_1(A_2, B_1):
pass
class A_2B_2(A_2, B_2):
pass
最好的方法是什么?
另外,假设我想添加一个新的 mixin 来设置 B,调用它B_x
。有没有一种很好的方法来定义一些我可以调用的函数来生成通过所有withB_x
交叉产生的类?即,是否有一个我可以编写的函数A_i
B_x
f
f(B_x, [A_1, A_2, ..., A_n])
将导致能够生成
class A_1B_x(A_1, B_x):
pass
class A_2B_x(A_2, B_x):
pass
# ... and so on
值得注意的是,我希望能够在另一个文件中引用这些生成的类来按名称实例化它们。
四处挖掘,我发现诸如动态地将基类混合到 Python 中的实例之类的东西,但它们会更改实例而不是生成类。
解决方案
使用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'>)
并加入它以动态(或什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__
或者您可以通过例如属性过滤它们)或者在这样的包中没有其他任何东西+忽略所有dunder部分:
# 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'>]
推荐阅读
- python - 在pytest夹具中模拟抽象依赖
- python - 这个 Python 代码在字符串中查找字符串的时间复杂度是多少
- sql - 如何使用 SQL 中的存储过程过滤掉重复名称列表?
- .net - .NET 命令行解析器库中的受限整数选项
- java - Java Flight Recorder 中的 ReentrantLock
- spring-boot - spring webflux hatoas:如何使用主机映射获取完整链接
- javascript - mongodb通过另一个具有ID的数组获取对象数组中的项目(聚合$ in)
- python - Flask - 在没有模板的情况下使用 jinja 宏
- python-3.x - /akolaprofile/ 'MyRegistration' 对象的 AttributeError 没有属性 'get'
- python - 如何在 pytest-bdd 的其他步骤中作为参数获得响应和使用