【问题标题】:Cython Pickling in Package "not found as" Error包装中的 Cython 酸洗“未找到”错误
【发布时间】:2013-08-29 06:11:05
【问题描述】:

我在挑选 Cython 类时遇到问题,但前提是它是在包中定义的。这个问题被注意到previously online,但他们没有说明它是如何解决的。这里有两个组件:使用__reduce__ 方法的 Cython 酸洗和包错误。

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

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

ImportError: No module named reduce

健全性测试

为了检查没有某种范围或其他问题,如果我运行 pickle 模块应该执行的步骤,一切正常:

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

那么这里发生了什么?!

【问题讨论】:

  • 查看类似问题stackoverflow.com/a/16405319/204882的答案。基本上,您不能只是移动已编译的扩展。包名必须在构建时指定。
  • 嗯,成功了。我想并没有真正理解 setup.py 中的 Extension 对象是如何工作的。我会更新答案。

标签: python pickle cython


【解决方案1】:

问题出在构建脚本中。 Pickle 模块使用函数/类的__module__ 属性进行酸洗。 __module__ 属性来自setup.py 脚本中Extension() 构造函数的第一个参数。由于我将构造函数定义为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'])]
)

解决问题。

【讨论】:

    【解决方案2】:

    编辑

    也许这样的事情是可能的:Foo.__module__ = 'bar.reduce' in bar/__init__.py

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

    """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)
    

    旧版本

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

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

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

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

    【讨论】:

    • 感谢您抽出宝贵时间查看我的问题。我发现它实际上是一个导入错误。我已经更新了我的问题。它说没有称为reduce的模块。为了清楚起见,如果我不使用 cython,这个例子可以正常工作。
    • 我添加了一些关于 pickle 使用的 copyreg 的值得注意的细节。
    • 第一个建议可以解决问题,但是不能设置扩展类型的静态属性。如果你尝试一下,你会遇到 TypeError。可以通过遵循this questions 中的示例来完成该行为,其中在扩展类型上定义了静态属性。不过它看起来和我的一样复杂。
    • 我不是很了解 copyreg 之一。你能提供一个关于它在reduce.pyx 中如何工作的sn-p 吗?
    • 它应该像 complex 一样工作 - 用 Foo 替换 complex 就足够了。那么__reduce__ 就不需要了。
    猜你喜欢
    • 2014-09-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-04-01
    • 1970-01-01
    • 2015-02-22
    • 2013-09-28
    • 1970-01-01
    相关资源
    最近更新 更多