如果pickle模块,__reduce__函数如何正常工作?

时间:2016-08-07 10:28:40

标签: python serialization pickle

我不理解__reduce__函数在Python中pickle模块的情况下如何正常工作。

假设我有以下课程:

class Foo(object):
  def __init__(self, file_name = 'file.txt'):
    self.file_name = file_name
    self.f = open(self.file_name, 'w')

它不能被腌制,因为pickle模块不知道如何编码文件句柄:

foo = Foo()
print(pickle.dumps(foo))

输出:

TypeError: can't pickle file objects

但如果我添加__reduce__函数,它会成功编码:

import pickle

class Foo(object):
  def __init__(self, file_name = 'file.txt'):
    self.file_name = file_name
    self.f = open(self.file_name, 'w')

  def __reduce__(self):
    return (self.__class__, (self.file_name, ))

foo = Foo()
print(pickle.dumps(foo))

输出:

c__main__
Foo
p0
(S'file.txt'
p1
tp2
Rp3
.

我是对的,__reduce__函数只返回"指令"如果pickle.dumps调用失败,解构器是否重新创建原始对象?

从文档中我不清楚。

2 个答案:

答案 0 :(得分:1)

你是对的。 __reduce__方法应该返回提示如何重建(unpickle)对象,以防它无法自动pickle。它可能包含一个对象引用和参数,通过它可以调用它来创建对象的初始版本,对象的状态等。

来自documentation

  

如果返回字符串,则应将该字符串解释为全局变量的名称。它应该是对象相对于其模块的本地名称; pickle模块搜索模块名称空间以确定对象的模块。此行为通常对单身人士有用。

     

返回元组时,它必须介于2到5个项目之间。可以省略可选项,或者可以提供None作为其值。每个项目的语义都是按顺序排列的:

     
      
  • 将调用以创建对象初始版本的可调用对象。
  •   
  • 可调用对象的参数元组。如果callable不接受任何参数,则必须给出一个空元组。
  •   
  • 可选地,对象的状态,将如前所述传递给对象的__setstate__()方法。如果对象没有这样的方法,则该值必须是字典,并且它将被添加到对象的__dict__属性中。
  •   
  • 可选地,迭代器(而不是序列)产生连续的项目。这些项目将使用obj.append(item)或使用obj.extend(list_of_items)批量附加到对象。这主要用于列表子类,但只要具有适当签名的append()extend()方法,其他类就可以使用它。 (是否使用append()extend()取决于使用的pickle协议版本以及要追加的项目数,因此必须支持两者。)
  •   
  • 可选地,迭代器(不是序列)产生连续的键值对。这些项目将使用obj[key] = value存储到对象。这主要用于字典子类,但只要它们实现__setitem__(),就可以被其他类使用。
  •   

答案 1 :(得分:0)

我认为重要的是要注意,在您的示例中使用__reduce__函数对缺少的错误概不负责。您不仅在使用__reduce__函数,而且更重要的是,您并没有像以前尝试的那样在__reduce__函数中腌制打开的文件(请注意,您排除了self.f __reduce__函数)。将self.f添加到__reduce__函数中,您将遇到相同的错误。