首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >namedtuple实例的筛选通常成功,但当模块被Cythonized化时则失败。

namedtuple实例的筛选通常成功,但当模块被Cythonized化时则失败。
EN

Stack Overflow用户
提问于 2019-03-18 15:06:23
回答 1查看 598关注 0票数 6

我在一个模块中定义了一个namedtuple类型,它由两个类foo和bar组成,分别在模块的唯一文件mod.py中定义。我能够创建foo和bar的实例,而无需发布并对它们进行腌制。我现在正在尝试将其数字化,这样我就可以将模块作为字节码分发。

模块文件结构如下所示:

代码语言:javascript
复制
./mod.pyx
./setup.py
./demo.py

`mod.pyx‘的内容如下:

代码语言:javascript
复制
import collections

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

class bar:

    def __init__(self,A,B):
        self.A = A
        self.B = B

setup.py的内容如下:

代码语言:javascript
复制
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来计算它,该命令创建已编译的模块文件:

代码语言:javascript
复制
./mod.cp37-win_amd64.pyd

运行以下demo.py

代码语言:javascript
复制
import mod, pickle
ham = mod.foo(1,2)
spam = mod.bar(1,2)

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

成功地对类spam (类bar的实例)进行了腌制,但在ham (名称元组foo的实例)上失败,错误消息如下:

代码语言:javascript
复制
PicklingError: Can't pickle <class 'importlib._bootstrap.foo'>: attribute lookup foo on importlib._bootstrap failed

如果重要的话,这都是用Python3.7完成的。看来Pickle已经找不到mod.foo的类定义了,尽管它能够创建一个没有问题的实例。我知道namedtuple在命名它返回的类方面有一些奇怪的行为,我承认我是打包Cython模块的相对新手。

谷歌搜索发现了一些已知的namedtuple和Cython问题,所以我想知道这是否是已知问题的一部分,还是我只是对我的模块进行了错误的打包。

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2019-03-19 08:57:27

为了使pickle工作,必须设置foo-type的属性__module__,并且应该是mod

namedtuple使用诡计/启发 (即在sys._getframe(1).f_globals中查找)获取以下信息:

代码语言:javascript
复制
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 (如代码注释中所指出的那样),即:

代码语言:javascript
复制
foo = collections.namedtuple('foo', 'A B', module='mod')

或者让它更通用:

代码语言:javascript
复制
foo = collections.namedtuple('foo', 'A B', module=__name__)

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

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

票数 7
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/55224383

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档