包中的Cython Pickling“未找到”错误

时间:2013-08-29 06:11:06

标签: python pickle cython

我在挑选Cython类时遇到了麻烦,但只有当它在包内定义时才会出现问题。注意到这个问题previously online,但他们没有说明它是如何解决的。这里有两个组件:使用__reduce__方法进行Cython酸洗和包错误。

Cython Pickling Success

我将首先展示它如何在没有包装部件的情况下工作。此示例正常工作。

Cython文件

我的Cython文件是reudce.pyx

cdef class Foo(object):
    cdef int n

    def __init__(self, n):
        self.n = n

    def __reduce__(self):
        return Foo, (self.n,)

设置文件

可以使用setup.py

进行编译
from distutils.core import setup
from distutils.extension import Extension
from Cython.Distutils import build_ext

setup(
    cmdclass = {'build_ext': build_ext},
    ext_modules = [Extension("reduce", ["reduce.pyx"])]
)

执行python setup.py build && cp build/lib*/reduce.so .

测试脚本

测试脚本名为test_reduce.py,并且是:

import reduce
import pickle
f = reduce.Foo(4)
print pickle.dumps(f)

执行python test_reduce.py效果很好。

包裹失败中的Cython酸洗

但是,一旦将reduce.pyx放入包中,就会出错。

包创建

要重现这一点,首先要创建一个名为bar的包。

mkdir bar
mv reduce.so bar
echo "from reduce import Foo" > bar/__init__.py 

测试脚本

test_reduce.py文件更改为:

import bar
import pickle
f = bar.Foo(4)
print pickle.dumps(f)

错误消息

运行python test_reduce.py会出现以下错误:

File "/usr/lib/python2.7/pickle.py", line 286, in save
  f(self, obj) # Call unbound method with explicit self
File "/usr/lib/python2.7/pickle.py", line 748, in save_global
  (obj, module, name))
pickle.PicklingError: Can't pickle <type 'reduce.Foo'>: it's not found as reduce.Foo

pickle.py中有一些错误被转换为PicklingError在查看该代码之后,发生的具体错误是:

ImportError: No module named reduce

完整性测试

要检查是否存在某种范围或其他问题,如果我运行pickle模块应执行的步骤,一切正常:

f = bar.Foo(4)
call, args = f.__reduce__()
print call(*args)

那么这里发生了什么?!

2 个答案:

答案 0 :(得分:4)

问题出在构建脚本中。 Pickle模块使用函数/类的__module__属性进行pickle。 __module__属性来自Extension()脚本中setup.py构造函数的第一个参数。由于我将构造函数定义为Extension('reduce', ['reduce.pyx']),因此__module__属性为reduce。它应该是bar/reduce,因为它现在在一个包中。

使setup.py看起来像:

from distutils.core import setup
from distutils.extension import Extension
from Cython.Distutils import build_ext

setup(
    cmdclass = {'build_ext': build_ext},
    ext_modules = [Extension('bar/reduce', ['reduce.pyx'])]
)

解决了这个问题。

答案 1 :(得分:1)

修改

可能是这样的:Foo.__module__ = 'bar.reduce'

中的bar/__init__.py

或者您可以使用模块copyreg。以下是copyreg代码中的一些片段:

"""Helper to provide extensibility for pickle.

This is only useful to add pickle support for extension types defined in
C, not for instances of user-defined classes.
"""

def pickle(ob_type, pickle_function, constructor_ob=None):
    # ...

# Example: provide pickling support for complex numbers.

try:
    complex
except NameError:
    pass
else:

    def pickle_complex(c):
        return complex, (c.real, c.imag)

    pickle(complex, pickle_complex, complex)

OLD VERSION

Pickle这样做:

    try:
        __import__(module, level=0)
        mod = sys.modules[module]
        klass = getattr(mod, name)
    except (ImportError, KeyError, AttributeError):
        raise PicklingError(
            "Can't pickle %r: it's not found as %s.%s" %
            (obj, module, name))

你能试试哪条线路失败吗?

当pickle piclkes类和函数如module.function时,它会重新确认这是正确的函数意味着:

# module.py
def function(): # 1
    pass
a = function
def function(): # 2
    pass

a中的函数1无法被腌制,但函数2可以被腌制,因为它位于__name__下的模块中,它是“函数”。

所以在你的情况下,pickle在Foo模块中找不到与作为参数传递的相同的类reduce。参数Foo声称可以在模块reduce中找到。