如何加载独立于任何当前实现的pickle文件?

问题描述 投票:0回答:1

假设我们有一个这样的类定义:

class Foo:
    class Bar:
        def __init__(self, _):
            pass  # Imagine some implementation

    def __init__(self, a):
        self._a = Foo.Bar(a)

然后我们实例化它并使用 pickle 将对象转储到文件中。

import pickle
obj = Foo('whatever')
pickle.dump(obj, open('./tmp.pkl', 'wb'))

现在假设我们对类的命名或结构进行更改(在本例中,“Bar”被重命名为“Baz”)。

class Foo:
    class Baz:
        def __init__(self, _):
            pass  # Imagine some implementation

    def __init__(self, a):
        self._a = Foo.Baz(a)

如果我们最终尝试在这种情况下加载转储的对象,如下所示:

obj = pickle.load(open('./tmp.pkl', 'rb'))

我们会给我们一个...

AttributeError: Can't get attribute 'Foo.Bar' on <module '__main__' from 'myscript.py'>

问题:有没有一种方法可以独立于“Foo”的当前实现来加载pickle对象? 由于我们在 Python 中有

__dict__
属性,我想可能有一个选项可以将 pickle 对象作为字典来获取?

python oop pickle
1个回答
0
投票

除了非常有问题的嵌套类设计之外,通过 Pickle 进行的 Python 序列化是相当可定制的。

例如,可以编写一个

__setstate__
方法,在 unpickling 时将调用该方法来为重构的类赋值。

在这种情况下它不会有帮助,因为Python会首先尝试取消嵌套属性 -

self._a
“Foo.Bar”实例,然后才尝试创建一个新的
Foo
实例,设置
._a 
价值。

您可以在 pickling 时使用

__getstate__
方法自定义 pickle 文件,并将新的
Baz
类存储在序列化文件中 - 并且构建一个包含这两个类并执行此转换的中间版本是可行的.

或者,在这种情况下更容易的是,只需在 Unpickle 时使用某种机制创建类来重新映射嵌套类名称 - 我认为它需要一个带有

__getattr__
的元类 - 它将在 unpickle 时调用未找到嵌套在“Foo”中的类的名称,并且可以只返回新创建的类:

class M(type):
    def __getattr__(cls, attr):
        if attr == "Bar":
            return getattr(cls, "Baz")
        raise AttributeError()

class Foo(metaclass=M):
    class Baz:
        def __init__(self, _):
            pass  # Imagine some implementation

    def __init__(self, a):
        self._a = Foo.Baz(a)

上面的“Foo”实例可以取消序列化的类似类,该类的内部类被命名为“Bar”。当然,如果有多个这样的实例,您可以只使用元类内部的映射'

__getattr__
而不是硬编码的“if”

© www.soinside.com 2019 - 2024. All rights reserved.