在Python中动态创建类时,exec与type



我有一个luigi管道。我们有很多定期更改的外部文件,我们希望能够从元数据构建管道。

我动态地创建类,并找到了两种方法:

使用exec:

exec("""
class {system}(DeliverySystem):
pass
""".format(system='ClassUsingExec'))

使用类型:

name = 'ClassUsingType'
globals()[name] = type(name, (DeliverySystem,),{})

这两种方法在单线程环境中都能很好地工作,但当我开始运行luigi时,许多工人都会生成子进程,exec版本很好,但类型版本会出现错误,如本文和本文所述(有关更完整的堆栈跟踪,请参阅它们(:

PicklingError: Can't pickle <class 'abc.ClassUsingType'>: attribute lookup abc.ClassUsingType failed.

我能在两者之间找到的唯一区别是模块:

print(ClassUsingExec.__dict__) #=>
mappingproxy({'__module__': '__main__',
'__doc__': None,
'__abstractmethods__': frozenset(),
'_abc_impl': <_abc_data at 0x15b5063c120>,
'_namespace_at_class_time': ''})

print(ClassUsingType.__dict__) #=>
mappingproxy({'__module__': 'abc',
'__doc__': None,
'__abstractmethods__': frozenset(),
'_abc_impl': <_abc_data at 0x15b3f870450>,
'_namespace_at_class_time': ''})

模块似乎不同,这可能是差异的来源。

使用Python 3.6,Windows 10,luigi 2.8.9。

问题:

有没有一种方法可以使用type来创建一个类,这样它的模块就是定义它的模块,而不是在abc中?

这两种方法之间还有其他我遗漏的区别吗?根据这篇帖子,应该没有什么区别,但我不认为是这样。

出现问题的原因是:

  • 在Windows中,子进程无法访问父变量
  • Luigi Tasks使用Register(以及ABC的扩展(作为它的元类
  • 当动态创建一个以ABC(抽象基类(作为元类的类时,该类的模块将是abc,而不是定义该类的模的名称

当工作人员被分配任务时,它将转到模块加载任务。由于模块设置为abc,而不是动态创建类的模块,因此它将失败。

要使它工作,所需要的就是修改类的创建来修改模块:

type(name, (DeliverySystem,),{})

成为

type(name, (DeliverySystem,),{'__module__':__name__})

现在,当工作人员被分配任务时,它将进入正确的模块并重新创建类,一切都会正常进行!

相关内容

  • 没有找到相关文章

最新更新