22

我正在尝试腌制一堂大课并获得

TypeError:无法腌制模块对象

尽管浏览了网络,但我无法完全弄清楚这意味着什么。而且我不确定module object是哪个造成了麻烦。有没有办法找到罪魁祸首?堆栈跟踪似乎没有任何指示。

4

4 回答 4

18

Python 无法腌制模块对象是真正的问题。有充分的理由吗?我不这么认为。使模块对象不可提取会导致 python 作为并行/异步语言的脆弱性。如果你想腌制模块对象,或者 python 中的几乎任何东西,那么使用dill.

Python 3.2.5 (default, May 19 2013, 14:25:55) 
[GCC 4.2.1 Compatible Apple Clang 4.1 ((tags/Apple/clang-421.11.66))] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import dill
>>> import os
>>> dill.dumps(os)
b'\x80\x03cdill.dill\n_import_module\nq\x00X\x02\x00\x00\x00osq\x01\x85q\x02Rq\x03.'
>>>
>>>
>>> # and for parlor tricks...
>>> class Foo(object):
...   x = 100
...   def __call__(self, f):
...     def bar(y):
...       return f(self.x) + y
...     return bar
... 
>>> @Foo()
... def do_thing(x):
...   return x
... 
>>> do_thing(3)
103 
>>> dill.loads(dill.dumps(do_thing))(3)
103
>>> 

dill到这里:https : //github.com/uqfoundation/dill

于 2014-03-22T15:22:29.213 回答
17

我可以通过这种方式重现错误消息:

import cPickle

class Foo(object):
    def __init__(self):
        self.mod=cPickle

foo=Foo()
with file('/tmp/test.out', 'w') as f:
    cPickle.dump(foo, f) 

# TypeError: can't pickle module objects

你有一个引用模块的类属性吗?

于 2010-05-07T18:41:18.563 回答
10

递归查找泡菜失败

灵感来自wump的评论: Python: can't pickle module objects error

这是一些快速代码,可以帮助我递归地找到罪魁祸首。

它检查有问题的对象,看它是否酸洗失败。

然后迭代尝试腌制键以__dict__返回仅失败的腌制列表。

代码片段

import pickle

def pickle_trick(obj, max_depth=10):
    output = {}

    if max_depth <= 0:
        return output

    try:
        pickle.dumps(obj)
    except (pickle.PicklingError, TypeError) as e:
        failing_children = []

        if hasattr(obj, "__dict__"):
            for k, v in obj.__dict__.items():
                result = pickle_trick(v, max_depth=max_depth - 1)
                if result:
                    failing_children.append(result)

        output = {
            "fail": obj, 
            "err": e, 
            "depth": max_depth, 
            "failing_children": failing_children
        }

    return output

示例程序

import redis

import pickle
from pprint import pformat as pf


def pickle_trick(obj, max_depth=10):
    output = {}

    if max_depth <= 0:
        return output

    try:
        pickle.dumps(obj)
    except (pickle.PicklingError, TypeError) as e:
        failing_children = []

        if hasattr(obj, "__dict__"):
            for k, v in obj.__dict__.items():
                result = pickle_trick(v, max_depth=max_depth - 1)
                if result:
                    failing_children.append(result)

        output = {
            "fail": obj, 
            "err": e, 
            "depth": max_depth, 
            "failing_children": failing_children
        }

    return output


if __name__ == "__main__":
    r = redis.Redis()
    print(pf(pickle_trick(r)))

示例输出

$ python3 pickle-trick.py
{'depth': 10,
 'err': TypeError("can't pickle _thread.lock objects"),
 'fail': Redis<ConnectionPool<Connection<host=localhost,port=6379,db=0>>>,
 'failing_children': [{'depth': 9,
                       'err': TypeError("can't pickle _thread.lock objects"),
                       'fail': ConnectionPool<Connection<host=localhost,port=6379,db=0>>,
                       'failing_children': [{'depth': 8,
                                             'err': TypeError("can't pickle _thread.lock objects"),
                                             'fail': <unlocked _thread.lock object at 0x10bb58300>,
                                             'failing_children': []},
                                            {'depth': 8,
                                             'err': TypeError("can't pickle _thread.RLock objects"),
                                             'fail': <unlocked _thread.RLock object owner=0 count=0 at 0x10bb58150>,
                                             'failing_children': []}]},
                      {'depth': 9,
                       'err': PicklingError("Can't pickle <function Redis.<lambda> at 0x10c1e8710>: attribute lookup Redis.<lambda> on redis.client failed"),
                       'fail': {'ACL CAT': <function Redis.<lambda> at 0x10c1e89e0>,
                                'ACL DELUSER': <class 'int'>,
0x10c1e8170>,
                                .........
                                'ZSCORE': <function float_or_none at 0x10c1e5d40>},
                       'failing_children': []}]}

根本原因 - Redis 无法腌制 _thread.lock

就我而言,创建一个Redis我保存为对象属性的实例破坏了酸洗。

当你创建它的一个实例时,Redis它也会创建一个并且线程锁不能被pickle。connection_poolThreads

Redismultiprocessing.Process腌制之前,我必须在其中创建和清理。

测试

就我而言,我试图腌制的课程必须能够腌制。所以我添加了一个单元测试,它创建了一个类的实例并将其腌制。这样,如果有人修改了该类使其不能被腌制,从而破坏了它在多处理(和 pyspark)中使用的能力,我们将检测到该回归并立即知道。

def test_can_pickle():
    # Given
    obj = MyClassThatMustPickle()

    # When / Then
    pkl = pickle.dumps(obj)

    # This test will throw an error if it is no longer pickling correctly

于 2020-01-21T00:22:49.373 回答
2

根据文档:

什么可以腌制和不腌制?

可以腌制以下类型:

  • 无、真和假

  • 整数、浮点数、复数

  • 字符串、字节、字节数组

  • 仅包含可提取对象的元组、列表、集合和字典

  • 在模块顶层定义的函数(使用 def,而不是 lambda)

  • 在模块顶层定义的内置函数

  • 在模块顶层定义的类

  • 这些类的实例,其__dict__或调用的结果__getstate__()是可腌制的(有关详细信息,请参阅腌制类实例部分)。

如您所见,模块不在此列表中。请注意,这在使用时也是如此,deepcopy不仅适用于pickle模块,如以下文档中所述deepcopy

此模块不复制模块、方法、堆栈跟踪、堆栈帧、文件、套接字、窗口、数组或任何类似类型等类型。它通过返回原始对象不变来“复制”函数和类(浅层和深层);这与 pickle 模块处理这些的方式兼容。

一种可能的解决方法是使用@property装饰器而不是属性。例如,这应该有效:

    import numpy as np
    import pickle

    class Foo():
        @property
        def module(self):
            return np

    foo = Foo()
    with open('test.out', 'wb') as f:
        pickle.dump(foo, f)
于 2019-09-21T13:39:08.220 回答