【问题标题】:Possible to Unpickle class instances after being converted from old to new style?从旧样式转换为新样式后可以 Unpickle 类实例吗?
【发布时间】:2012-04-19 15:54:49
【问题描述】:

在 python 中,是否可以在转换为新样式后取消腌制为旧样式 python 类的对象? (即,具有不同“类签名”的对象)。 *

例如,假设某些实例被保存为:

class foo: # old style

然后在类更改为从对象继承后,又腌制了一些对象:

class foo(object): # new style

带有一个pickle.dump'ed 的对象可以被同一样式类pickle.load'ed,但两者都不会加载。我认为pickle使用的策略会根据继承发生变化(从对象继承的类有一个自动定义的 __reduce__ 方法,但没有继承的类没有)。当试图从没有继承(旧式定义)的代码中加载带有继承的旧式泡菜时,我得到了与SO question 中看到的相同的参数错误;即使第二个参数是多余的,它仍然会更改“类签名”和预期参数,并阻止加载。为了解决这个问题,我很乐意编写一个 unpickler,尽管我担心它可能涉及两个单独的子类 unpickler a la the docs...如果我知道如何正确解开每个我会很好的.

关于我的情况的一点背景...我正在使用 TrialHandler 类来保存和重新加载按钮按下和反应时间以进行行为心理学实验。我们重构了 TrialHandler 类以从更抽象的 baseTrialHandler 继承,但这样做暂时将类签名更改为从 object 继承。但是,我们无法解开旧的 trial_handler 文件,所以它被改回来了。我想查看使用两个版本的试验处理程序运行的同一个实验的数据,因此想在同一个脚本中取消两种类型的已保存日志文件。

或者,如果我不能编写一个自定义 unpickler 来解开两个对象,是否有另一种方法来序列化它们?我尝试直接转储到 yaml,但看起来我必须将类注册为可以被 yaml 化的东西,无论如何。

PsychoPy 邮件列表中包含有特定错误的完整 description of the problem。任何解释中间酸洗细节甚至python继承的博客文章都将受到欢迎;我找到的最接近的是一个很好的解释,为什么pickling is insecure 将酸洗描述为“简单的基于堆栈的虚拟机”,这很好,但并不足以让我弄清楚我自己拆开的基本知识。

【问题讨论】:

    标签: python inheritance pickle


    【解决方案1】:

    遍历部分:

    1. Python 中没有所谓的“类签名”,尽管我 从你的问题中得到了灵感
    2. 不应从 Python 2 程序中的“对象”继承 认为是一个错误。不从对象继承的类 - 也 被称为“旧式”的类,只是保留在语言上 向后兼容性,当引入新样式类时 Python 2.2(大约 2000/2001 年)——旧式类少了很多 一致且缺乏许多特性,这些特性使今天的 python 如此 语言很好。

    除此之外:unpickle 将尝试根据类限定名称反序列化对象 - 就像在<module>.<class> 中一样,就像在酸洗时对象的__class__.__name__ 属性上一样。因此,可以有一种天真的方式,在 unpickle 异常(它引发 TypeError)时,交换具有相同名称的可用类,然后重试 unpickle 操作 再说一遍:你的类应该继承自“对象”(更具体地说,有一个基于“类型”的元类)

    至于我提到的例子,随便写点东西:

    try:
        new_obj = pickle.load(data_stream)
    except TypeError: # if you know this was caused due to the new version of "Foo" class:
        current_foo = foomodule.Foo
        foomodule.Foo = foomodule.OldFoo
        new_obj = pickle.load(data_stream)
        foomodule.Foo = current_foo
    

    【讨论】:

    • 好建议 - 我没有意识到这是声明新旧样式类之间的关键区别。也许我应该将问题的标题更改为“如何在将对象从旧样式转换为新样式后解开对象?”我知道python没有严格的“签名”/协议,有更好的说法吗?至少我会回去编辑以将签名放在引号中。
    • 我想发布一个完整的示例,从交互式解释器中运行它——但是,我编写的简化的旧样式类在被解开为新样式时没有问题。但是,是的,我确实使用“旧”和“新”后缀在它们的名称(“Foo_old”和“Foo_new”)中声明两者,将它们的__name__ 属性设置为“Foo”,并动态设置模块“Foo”在 unpickle 时可变为一个或另一个。
    • 我正在考虑基于名称错误进行救援,但我没有同时使用 Foo 和 OldFoo,因为我将一个转换为另一个。并排声明它们的最佳方式是什么?可能是这样的?: class Foo(object): # New Style def __init__(self, ...args): # 实际代码 class OldFoo: # Old Style def __init__(self, ...args): Foo(*args) OldFoo 在哪里调用新的?根据定义,旧式类不能从新式继承......有没有更好的方法让我不需要留下一个存根的旧式类?
    • 另外,这个旧式与新式的问题可以联系起来:stackoverflow.com/questions/10043963/…
    • 我认为您最好有一个函数(或其他构造)将对象从旧类型转换为新类型。您确实声明了两个类 - 旧的具有不同的名称 - 并使用名称交换,如上所示来取消对象。然后恢复名称,调用转换器函数实例化一个新对象,使用当前类定义,并根据传递的对象中的属性设置其属性。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-04-22
    • 2010-10-06
    • 2016-12-09
    • 2016-12-06
    相关资源
    最近更新 更多