我在挑选Cython类时遇到了麻烦,但只有当它在包内定义时才会出现问题。注意到这个问题previously online,但他们没有说明它是如何解决的。这里有两个组件:使用__reduce__
方法进行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
效果很好。
但是,一旦将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)
那么这里发生了什么?!
答案 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
中找到。