当 pickle 一个类时,我在 python 中得到与 cython 中不同的行为

mar*_*ako 5 python import pickle cython

我有以下文件层次结构:

python/apps/A.py
      /geometrylib/__init__.py
      /geometrylib/B.py
      /geometrylib/geometry.py
      /geometrylib/goemetry.pyx
      /geometrylib/goemetry.pyd
Run Code Online (Sandbox Code Playgroud)

Geometry.pyx 和 Geometry.py 包含相同的 Camera 类(cython 版本使用 cdef 定义该类)。A.py 和 B.py 都导入几何模块。

如果我导入 cython 版本(编译为 Geometry.pyd),我可以从 python/geometrylib 文件夹中的 B.py 中正确pickle Camera。但我无法从 python/apps 文件夹中的 A.py 中 pickle Camera,出现以下异常:

pickle.PicklingError:无法pickle:找不到geometry.Camera

但是,如果我删除geometry.pyd并导入python版本(geometry.py),那么我可以从A.py或B.py中pickle Camera。除了删除geometry.pyd之外,没有其他任何变化,相同的python命令行,在两种情况下都从相同的文件夹运行。 为什么会有这种差异?

挖掘了一下,我发现异常发生在 C:\Python27\Lib\pickle.py 第 742 行

try:
    __import__(module)            #line 742
    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))
Run Code Online (Sandbox Code Playgroud)

在 A.py 中,我导入 cython 版本(geometry.pyd)(并且我腌制了一个 Camera 实例以触发期望)模块是“geometry”并__import__(module)触发异常。在 A.py 中,我导入 python 版本(geometry.py),(并且我腌制了一个 Camera 实例以触发期望)模块是“geometrylib.geometry”并__import__(module)正确导入该模块。

我通过将 python/geometrylib 添加到 PYTHONPATH 解决了这个问题,然后我可以使用 cython 版本正确地从 A.py 和 B.py 中pickle Camera。

这是它应该如何工作的吗?我不喜欢我的解决方案。有人有更好的解决方案吗?

编辑添加一些额外信息。

另外,根据请求,这是我用来构建 cython 扩展的 setup.py。

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

setup(
    cmdclass = { 'build_ext': build_ext},
    ext_modules = [Extension("geometry", ['geometry.pyx'], include_dirs=[numpy.get_include(), "."])])
Run Code Online (Sandbox Code Playgroud)

Nik*_*kin 4

您正在将 Geometry.pyx 构建为顶级模块,而实际上它是geometrylib包的一部分。因此,Camera类被分配了错误的__module__值(geometry而不是geometrylib.geometry),并且 pickler 在尝试查找名为 的顶级模块时失败geometry

您应该遵循标准打包指南,即将 setup.py 放入“Python”文件夹中,位于顶级模块(geometrylib)旁边。设置调用将如下所示:

setup(
    cmdclass = {'build_ext': build_ext},
    ext_modules = [Extension("geometrylib.geometry", ['geometrylib/geometry.pyx'], include_dirs=[numpy.get_include(), "."])])
Run Code Online (Sandbox Code Playgroud)