python - 命名元组实例的酸洗正常成功,但在模块被 Cythonized 时失败
问题描述
我在一个模块中定义了一个 namedtuple 类型,该模块由两个类 foo 和 bar 组成,定义在模块的唯一文件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'])])
)
我使用命令对它进行 cythonize 处理,该命令python setup.py build_ext --inplace
创建编译后的模块文件:
./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
class 的实例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 的已知问题,所以我想知道这是否可能是已知问题的一部分,或者我只是错误地打包了我的模块。
解决方案
为了工作,必须设置 -type 的属性并且应该pickle
是.__module__
foo
mod
namedtuple
使用技巧/启发式(即在 中查找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
-name 传递给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
)。
推荐阅读
- mongodb - 使用 Tailable ReactiveMongo 扩展 Spring WebFlux
- ios - XCode 11.4 - 在 UITest 中重新安装应用程序失败:“找不到已安装的应用程序”
- java - 如何在从 xml 文件加载配置时给出 jooq 生成器自定义策略的地址?
- c# - RestSharp 错误:从 result.Data 中给出 null
- php - 显示带有翻译的产品表中的所有数据,但如果产品没有翻译,则使用默认语言显示
- react-native - Razorpay sucessCallBack() 后 React Native 应用程序关闭
- php - 如何在 php 中使用 get 方法发送数据
- javascript - 调用一个动作来更新本地存储(无需 API 调用)——redux 持久化,反应原生
- node.js - 如何在只有部分文件名的目录中搜索文件?
- python - Hadoop 错误:_COPYING_ 只能复制到 0 个节点而不是 minReplication (=1)