6

我在腌制 Cython 类时遇到了麻烦,但只有在它是在包中定义的时候。这个问题之前在网上已经注意到了,但他们没有说明它是如何解决的。这里有两个组件:使用__reduce__方法和包错误的 Cython 酸洗。

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

在查看该代码后,有一个错误都变成了 PicklingError ,pickle.py发生的具体错误是:

ImportError: No module named reduce

理智测试

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

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

那么这里发生了什么?!

4

2 回答 2

4

问题出在构建脚本中。该Pickle模块使用__module__函数/类的属性进行酸洗。该__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'])]
)

解决问题。

于 2013-08-29T17:54:57.437 回答
2

编辑

也许这样的事情是可能的: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)

旧版

泡菜这样做:

    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 picklkes 类和函数时,module.function它会向自己保证这是正确的函数意味着:

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

函数 1a不能被腌制,但函数 2 可以腌制,因为它位于__name__它拥有的“功能”模块下。

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

于 2013-08-29T09:24:17.690 回答