【问题标题】:pickle updates the class definition of contained objects but dill does notpickle 更新包含对象的类定义,但 dill 不更新
【发布时间】:2022-10-04 18:28:16
【问题描述】:

dill 更新 dilled/undilled 对象本身的类定义,但不更新 dilled/undilled 对象包含的对象的类定义。

在任何一种情况下,pickle 都会更新类定义。

为什么莳萝不遵循与泡菜相同的行为?

泡菜

import os
import pickle
import tempfile
from dataclasses import dataclass, field


def pickle_save(x):
    with tempfile.NamedTemporaryFile(delete=False) as f:
        pickle.dump(x, f)
    return f


def pickle_load(f):
    with open(f.name, "rb") as f:
        x = pickle.load(f)
    os.unlink(f.name)
    return x


@dataclass
class B:
    attribute: str = "old"

    def method_1(self):
        print(f"old class: {self.attribute=}")


@dataclass
class A:
    attribute_1: str = "old"
    instances_of_B: list[B] = field(default_factory=list)

    def method_1(self):
        print(f"old class: {self.attribute_1=}, {self.instances_of_B=}")

    def add_b_instance(self):
        self.instances_of_B.append(B())


old_a = A()
old_a.add_b_instance()
old_a.method_1()
old_a.instances_of_B[0].method_1()
print(f"{old_a = }")
temp_file = pickle_save(old_a)

# old_a has been saved to file
# Next we update our class definitions
# then load old_a from file,
# and see whether the added methods exist

@dataclass
class A:
    attribute_1: str = "new"
    attribute_2: str = "new attribute 2"
    instances_of_B: list[B] = field(default_factory=list)

    def method_1(self):
        print(f"new class: {self.attribute_1=}, {self.instances_of_B=}")

    def method_2(self):
        print("this method from A did not exist before")
        print(f"this attribute did not exist before: {self.attribute_2=}")


@dataclass
class B:
    attribute: str = "new"

    def method_1(self):
        print(f"new class: {self.attribute=}")

    def method_2(self):
        print("this method from B did not exist before")


new_a = pickle_load(temp_file)
print(f"{new_a=}")
new_a.method_1()
new_a.method_2()
new_a.instances_of_B[0].method_1()
new_a.instances_of_B[0].method_2()

腌制的 A 实例和包含的 B 实例的新方法_2 都可以在加载后使用:

old class: self.attribute_1='old', self.instances_of_B=[B(attribute='old')]
old class: self.attribute='old'
old_a = A(attribute_1='old', instances_of_B=[B(attribute='old')])
new_a=A(attribute_1='old', attribute_2='new attribute 2', instances_of_B=[B(attribute='old')])
new class: self.attribute_1='old', self.instances_of_B=[B(attribute='old')]
this method from A did not exist before
this attribute did not exist before: self.attribute_2='new attribute 2'
new class: self.attribute='old'
this method from B did not exist before

莳萝

import dill as pickle

只有腌制的A实例的新方法_2可以在加载后使用,而包含的B实例的新方法_2不能:

old class: self.attribute_1='old', self.instances_of_B=[B(attribute='old')]
old class: self.attribute='old'
old_a = A(attribute_1='old', instances_of_B=[B(attribute='old')])
new_a=A(attribute_1='old', attribute_2='new attribute 2', instances_of_B=[B(attribute='old')])       
new class: self.attribute_1='old', self.instances_of_B=[B(attribute='old')]
this method from A did not exist before
this attribute did not exist before: self.attribute_2='new attribute 2'
old class: self.attribute='old'
Traceback (most recent call last):
  File "c:\question_dill_pickle.py", line 78, in <module>
    new_a.instances_of_B[0].method_2()
AttributeError: 'B' object has no attribute 'method_2'

【问题讨论】:

    标签: python pickle dill


    【解决方案1】:

    我是dill 作者。 dill 不遵循 pickle 的行为,因为 pickle 通过引用序列化类(即它别无选择,但使用当前上下文中使用的任何类定义),而 dill 存储类定义以及腌制实例......所以你可以选择行为。默认是使用存储的类,所以你得到你腌制的东西(更常见的是,这是想要的)。但是,如果您想忽略存储的类,并使用更新的定义,您可以在load 中使用ignore=True 关键字(或在dill.settings 中全局更改它)。

    从文档:

    If *ignore=False* then objects whose class is defined in the module
    *__main__* are updated to reference the existing class in *__main__*,
    otherwise they are left to refer to the reconstructed type, which may
    be different.
    

    【讨论】:

      猜你喜欢
      • 2013-04-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-03-21
      • 2019-07-06
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多