酸洗和取消酸洗动态加载的模块

时间:2021-07-09 10:30:58

标签: python python-3.x pickle python-module python-importlib

我正在通过以下方式创建一个对象(我知道这很可怕,不要问为什么,这是必要的):

with tempfile.NamedTemporaryFile(suffix='.py') as temp:
    temp.write(code.encode('utf-8'))
    import importlib.util
    spec = importlib.util.spec_from_file_location('a_temp_module', temp.name)
    temp_module = importlib.util.module_from_spec(spec)
    spec.loader.exec_module(temp_module)
    special_snowflake_object = temp_module.SpecialSnowflake()

现在类 SpecialSnowflake 实现了两种用于保存和加载的方法,它们本质上只是腌制和取消腌制:

def save(self, file_path: str):
    with open(file_path, 'w') as fp:
        pickle.dump(self, fp)

@staticmethod
def load(file_path: str):
    with open(file_path, 'r') as fp:
        return pickle.load(fp)

但是,在调用 save 时出现错误:

_pickle.PicklingError: Can't pickle <class 'a_temp_module.SpecialSnowflake'>: import of module 'a_temp_module' failed

这很奇怪,因为我不知道你可以携带从调用实例化的对象而不携带类定义本身,但显然,这就是这里发生的事情。

我假设加载和保存可以通过:

with tempfile.NamedTemporaryFile(suffix='.py') as temp:
    temp.write(code.encode('utf-8'))
    import importlib.util
    spec = importlib.util.spec_from_file_location('a_temp_module', temp.name)
    temp_module = importlib.util.module_from_spec(spec)
    spec.loader.exec_module(temp_module)
    special_snowflake_object_1.save(path_to_pickle)
    special_snowflake_object_2 = temp_module.SpecialSnowflake.load(path_to_pickle)

但我更希望 save 无需重新加载模块即可调用,我想对于加载来说别无选择。我目前的想法是我可以通过将模块的加载隐藏在 save 函数中并将代码作为对象的属性携带来做到这一点……但我希望可能有更多优雅的解决方案。

1 个答案:

答案 0 :(得分:0)

引用 pickle 模块 docs

<块引用>

pickle 可以透明地保存和恢复类实例,但是类定义必须是可导入的,并且与存储对象时存在于同一模块中。

pickle 不能腌制 a_temp_module.SpecialSnowflake 的实例,因为存储在 a_temp_module 变量中的模块对象实际上并未使用 import 语句导入到全局上下文中.

正如@martineau 在评论中所说的那样,pickle 模块实际上并不在 pickle 文件中存储类定义。类定义必须可从同一模块导入。

再次引用 pickle docs

<块引用>

可以腌制以下类型:

...

  • 在模块的顶层定义的类

SpecialSnowflake 类的定义也未在 __main__ 环境(或示例中的代码所在的任何其他环境)的顶层定义,因此您不能进行 pickle 和 unpickle该环境中的 SpecialSnowflake 个实例。

相关问题