python - 命名元组实例的酸洗正常成功,但在模块被 Cythonized 时失败

标签 python python-3.x cython

我在由两个类 foo 和 bar 组成的模块中定义了一个 namedtuple 类型,定义在模块的唯一文件 mod.py 中。 .我能够毫无问题地创建 foo 和 bar 的实例并将它们腌制。我现在正在尝试对它进行 Cythonize,以便我可以将模块作为字节码分发。

模块文件结构如下:

./mod.pyx
./setup.py
./demo.py

`mod.pyx' 的内容是:
import collections

foo = collections.namedtuple('foo', 'A B')

class bar:

    def __init__(self,A,B):
        self.A = A
        self.B = B
setup.py的内容是:
from distutils.core import setup
from distutils.extension import Extension
from Cython.Build import cythonize

setup( 
      ext_modules= cythonize([Extension('mod', ['mod.pyx'])])
)

我使用命令 python setup.py build_ext --inplace 对它进行 cythonize ,它创建编译后的模块文件:
./mod.cp37-win_amd64.pyd

运行以下 demo.py :
import mod, pickle
ham = mod.foo(1,2)
spam = mod.bar(1,2)

print(pickle.dumps(spam))
print(pickle.dumps(ham))

腌制成功spam ,类的实例bar ,但在 ham 上失败,namedtuple 的实例foo ,带有错误消息:
PicklingError: Can't pickle <class 'importlib._bootstrap.foo'>: attribute lookup foo on importlib._bootstrap failed

如果重要的话,这一切都在 Python 3.7 中完成。似乎 Pickle 找不到 mod.foo 的类定义,即使 Python 能够毫无问题地创建实例。我知道 namedtuple 在命名它返回的类方面有一些奇怪的行为,我承认我是打包 Cython 模块的相对新手。

一些谷歌搜索发现了一些关于 namedtuples 和 Cython 的已知问题,所以我想知道这是否是已知问题的一部分,或者我只是错误地打包了我的模块。

最佳答案

为了 pickle工作,属性__module__foo -type 必须设置并且应该是 mod .
namedtuple使用 a trick/heuristic (即在 sys._getframe(1).f_globals 中查找)以获取此信息:

def namedtuple(typename, field_names, *, rename=False, defaults=None, module=None):
    ...
    # For pickling to work, the __module__ variable needs to be set to the frame
    # where the named tuple is created.  Bypass this step in environments where
    # sys._getframe is not defined (Jython for example) or sys._getframe is not
    # defined for arguments greater than 0 (IronPython), or where the user has
    # specified a particular module.
    if module is None:
        try:
            module = _sys._getframe(1).f_globals.get('__name__', '__main__')
        except (AttributeError, ValueError):
            pass
    if module is not None:
        result.__module__ = module
    ...

Cython 或 C 扩展的问题在于,这种启发式方法不起作用,_sys._getframe(1).f_globals.get('__name__', '__main__')将产生 importlib._bootstrap而不是 mod .

要解决此问题,您需要正确传递 module -姓名为namedtuple -factory(如代码注释中指出的那样),即:
foo = collections.namedtuple('foo', 'A B', module='mod')

或者让它更通用:
foo = collections.namedtuple('foo', 'A B', module=__name__)

现在,导入后,foo.__module__mod正如 pickle 所期望的那样和 ham可以腌制。

顺便说一下bar的酸洗函数,因为 Cython 显式设置了正确的 __module__属性(即 mod ),同时构造类。

关于python - 命名元组实例的酸洗正常成功,但在模块被 Cythonized 时失败,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55224383/

相关文章:

python - 同时为 Python 2.7 和 3.6 安装 virtualenvwrapper

python - Cython并行循环问题

python - 如何在 cython 中获取类型化内存 View 的大小

谁能把这个翻译成Python?

python - python 中针对周期性边界条件的有效符号函数

python - 使用 pandas 包用 python 清理 excel 数据

python - 可作为 dict.get 的默认参数调用,如果键存在则不调用它

javascript - python 是否具有与 javascript 等效的函数注释?

python 动态字典

python-3.x - 带有 TensorFlow 后端的 Keras 自定义损失函数用于图像