为啥我不能腌制一个 typing.NamedTuple 而我可以腌制一个 collections.namedtuple?

Posted

技术标签:

【中文标题】为啥我不能腌制一个 typing.NamedTuple 而我可以腌制一个 collections.namedtuple?【英文标题】:Why can't I pickle a typing.NamedTuple while I can pickle a collections.namedtuple?为什么我不能腌制一个 typing.NamedTuple 而我可以腌制一个 collections.namedtuple? 【发布时间】:2016-02-21 03:48:27 【问题描述】:

为什么我不能腌制typing.NamedTuple 而我可以腌制collections.namedtuple?我怎样才能做泡菜NamedTuple

这段代码显示了我到目前为止所做的尝试:

from collections import namedtuple
from typing import NamedTuple

PersonTyping = NamedTuple('PersonTyping', [('firstname',str),('lastname',str)])
PersonCollections = namedtuple('PersonCollections', ['firstname','lastname'])

pt = PersonTyping("John","Smith")
pc = PersonCollections("John","Smith")


import pickle
import traceback

try:
    with open('personTyping.pkl', 'wb') as f:
        pickle.dump(pt, f)
except:
    traceback.print_exc()
try:
    with open('personCollections.pkl', 'wb') as f:
        pickle.dump(pc, f)
except:
    traceback.print_exc()

shell 上的输出:

$ python3 prova.py 
Traceback (most recent call last):
  File "prova.py", line 16, in <module>
    pickle.dump(pt, f)
_pickle.PicklingError: Can't pickle <class 'typing.PersonTyping'>: attribute lookup PersonTyping on typing failed
$ 

【问题讨论】:

此问题已在python 3.5.1 中修复。 【参考方案1】:

这是一个错误。我已经开票了:http://bugs.python.org/issue25665

问题是namedtuple 函数在创建类时通过从调用框架的全局变量中查找__name__ 属性来设置其__module__ 属性。在这种情况下,调用者是typing.NamedTuple

result.__module__ = _sys._getframe(1).f_globals.get('__name__', '__main__')

因此,在这种情况下,它最终将其设置为 'typing'

>>> type(pt)
<class 'typing.PersonTyping'>  # this should be __main__.PersonTyping
>>> type(pc)
<class '__main__.PersonCollections'>
>>> import typing
>>> typing.NamedTuple.__globals__['__name__']
'typing'

修复:

NamedTuple 函数应该自己设置它而不是这个:

def NamedTuple(typename, fields):

    fields = [(n, t) for n, t in fields]
    cls = collections.namedtuple(typename, [n for n, t in fields])
    cls._field_types = dict(fields)
    try:
        cls.__module__ = sys._getframe(1).f_globals.get('__name__', '__main__')
    except (AttributeError, ValueError):
        pass
    return cls

现在你也可以这样做:

PersonTyping = NamedTuple('PersonTyping', [('firstname',str),('lastname',str)])
PersonTyping.__module__ = __name__

【讨论】:

您的两个修复都有效,非常感谢!另外,感谢您的解释和已经提交了补丁! 如果AttributeErrorValueError 触发,生成的NamedTuple 仍然不能picklable,对吗?你应该提到这一点。

以上是关于为啥我不能腌制一个 typing.NamedTuple 而我可以腌制一个 collections.namedtuple?的主要内容,如果未能解决你的问题,请参考以下文章

为啥主机中止连接?

Python进程不能腌制

Python多处理,不能腌制thread.lock(pymongo)

在类中使用 ProcessPoolExecutor 时无法腌制协程对象

Python - 如何使这个不可腌制的对象可腌制?

如何腌制一个scapy包?