【发布时间】:2021-11-16 10:58:55
【问题描述】:
我希望有选择地扩展和覆盖某些功能的一系列类。对于这些类,我希望添加相同的功能,因为它们都是兼容的。我正在使用 Python 3.8+。我实现这一点的方法是将类创建为具有附加功能的type,并将父类作为基础传递。作为一个基本的例子:
class A:
def __init__(self, a, **kwargs):
self.a = a
self.params = kwargs
class B:
def __init__(self, b, **kwargs):
self.b = b
self.params = kwargs
def extend_class_with_print_params(base_class):
def print_params(self):
for k, v in self.params.items():
print(k, v)
return type(
f"Extended{base_class.__name__}",
(base_class,),
dict(print_params=print_params),
)
在上面,我定义了 A 和 B。函数 extend_class_with_print_params 添加了与两者兼容的功能。我的实际用例是为特定 sklearn 预测器的某些实例添加训练前和预测后挂钩,这就是我需要父级可配置的原因。
import joblib
from test_classes import *
normal_a = A(a=10)
joblib.dump(normal_a, "normal_a")
normal_a = joblib.load("normal_a")
extended_a = extend_class_with_print_params(A)(a=15, alpha=0.1, beta=0.2)
joblib.dump(extended_a, "extended_a")
extended_a = joblib.load("extended_a")
转储extended_a时,抛出如下错误:
_pickle.PicklingError: Can't pickle <class 'test_classes.ExtendedA'>: it's not found as test_classes.ExtendedA
正如以下帖子之一所建议的,我尝试在全局变量中设置 new_class_name 以在返回函数之前指向新类。这使我能够成功转储,但不能在不同的会话中加载文件,这是有道理的,因为全局变量将被重置。一般来说,我也不希望修改全局变量。
我已经尝试但未能根据以下情况使用__reduce__ 找到解决方案:
- Pickling dynamically generated classes?
- How can I pickle a dynamically created nested class in python?
- Pickle a dynamically parameterized sub-class
我没有发现上述方法清楚地适用于我的情况。内容可能是相关的,直接适用的,但是我没找到办法。
我也完全愿意改变我的模式(即使这意味着不动态定义类)。总之,我有以下要求:
- 扩展和覆盖任意父类的功能,但并非在所有情况下,因为扩展/覆盖类是可选的
- 对象必须是可腌制的
- 必须使用 joblib 或 pickle 库来腌制对象,而不是像
cloudpickle这样的东西
【问题讨论】:
标签: python class dynamic metaprogramming pickle