我在选择Cython类时遇到麻烦,但仅限于在包中定义它时。这个问题被记录为previously online,但是他们没有说明如何解决。这里有两个组件:使用__reduce__方法的Cython pickle 和包装错误。

Cython pickle 成功

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

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 pickle 中的包装失败

但是,一旦将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)

那这是怎么回事?!

最佳答案

问题出在构建脚本中。 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'])]
)

解决了问题。

10-05 21:05
查看更多