【问题标题】:Did something about `namedtuple` change in 3.5.1?3.5.1 中的 `namedtuple` 有什么变化吗?
【发布时间】:2016-03-13 23:56:40
【问题描述】:

在 Python 3.5.0 上:

>>> from collections import namedtuple
>>> cluster = namedtuple('Cluster', ['a', 'b'])
>>> c = cluster(a=4, b=9)
>>> c
Cluster(a=4, b=9)
>>> vars(c)
OrderedDict([('a', 4), ('b', 9)])

在 Python 3.5.1 上:

>>> from collections import namedtuple
>>> cluster = namedtuple('Cluster', ['a', 'b'])
>>> c = cluster(a=4, b=9)
>>> c
Cluster(a=4, b=9)
>>> vars(c)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: vars() argument must have __dict__ attribute

似乎namedtuple 的某些内容发生了变化(或者可能是vars() 的某些内容?)。

这是故意的吗?难道我们不应该再使用这种模式将命名元组转换为字典了吗?

【问题讨论】:

  • @user2357112 - 是的,我认为这种更改应该在更改日志中注明。这就是让我起初认为这可能是一个错误的原因。
  • “我们不应该再使用这种模式将命名元组转换为字典了吗”我想我们从来没有不应该使用这种模式,因为vars(x) 被记录为返回x.__dict__,但我认为从未记录过namedtuple 实例具有__dict__ 属性。我知道,argparse 模块中的Namespace 实例 记录了这种模式,所以也许这就是趋势出现的地方。无论如何,我敢肯定会有一些代码中断的开发人员感到惊讶,所以这是一个很好的问题。
  • 很公平。此外,我更正了 vars() 的使用从未被记录在案:在 Python 3.3 docs 它指出 the same effect can be achieved by using the built-in vars() function 在提及 _asdict() 的功能时。
  • @BlacklightShining 这确实是python中的约定,但这是规则的例外。 docs 中提到“为防止与字段名冲突,方法名和属性名以下划线开头。”
  • @JeremyBanks 可以说他们确实修复了一个错误。该错误允许第一个示例工作,但在第二个示例中破坏了它。当然,正如 jme 所提到的,该错误实际上被记录为一个功能......

标签: python typeerror python-3.5 namedtuple


【解决方案1】:

来自docs

命名元组实例没有每个实例的字典,因此它们是轻量级的,并且不需要比常规元组更多的内存。

docs(和help(namedtuple))说要使用c._asdict() 转换为字典。

【讨论】:

  • 有趣的是 vars 在 python3.5.0 上工作。 . .
  • 3.5.1 中可能已经删除了实例字典
  • 很公平,这似乎是官方认可的方式。遗憾的是,this question 推荐 vars() 的大部分建议现在都不正确。我想知道这是否是开发人员故意的,还是只是一个意外。
  • vars() 仅在对象具有 __dict__ 属性时才有效。如果 namedtuples 有这个字典,它们将需要比常规元组更多的内存。这被确定为不受欢迎的。因此__dict__ 被删除,因此vars() 不起作用。这是有意的。
  • @ChadS。 __dict__ 属性已经只是一个属性,namedtuples 有 __slots__。
【解决方案2】:

每Python bug #24931:

[__dict__] 消失了,因为它在 Python 3 中从根本上被破坏了,所以它必须被删除。提供__dict__ 会破坏子类化并产生奇怪的行为。

Revision that made the change

具体来说,未定义 __slots__ 的子类会表现得很奇怪:

>>> Cluster = namedtuple('Cluster', 'x y')
>>> class Cluster2(Cluster):
    pass
>>> vars(Cluster(1,2))
OrderedDict([('x', 1), ('y', 2)])
>>> vars(Cluster2(1,2))
{}

使用._asdict()。

【讨论】:

    【解决方案3】:

    __dict__ 被实现为@property 并已被删除;可以看到源代码的变化:

    3.5.0:

    def __repr__(self):
        'Return a nicely formatted representation string'
        return self.__class__.__name__ + '({repr_fmt})' % self
    
    @property
    def __dict__(self):
        'A new OrderedDict mapping field names to their values'
        return OrderedDict(zip(self._fields, self))
    
    def _asdict(self):
        'Return a new OrderedDict which maps field names to their values.'
        return self.__dict__
    
    def __getnewargs__(self):
        'Return self as a plain tuple.  Used by copy and pickle.'
        return tuple(self)
    
    def __getstate__(self):
        'Exclude the OrderedDict from pickling'
        return None
    

    3.5.1:

    def __repr__(self):
        'Return a nicely formatted representation string'
        return self.__class__.__name__ + '({repr_fmt})' % self
    
    def _asdict(self):
        'Return a new OrderedDict which maps field names to their values.'
        return OrderedDict(zip(self._fields, self))
    
    def __getnewargs__(self):
        'Return self as a plain tuple.  Used by copy and pickle.'
        return tuple(self)
    

    【讨论】:

      猜你喜欢
      • 2018-11-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-10-18
      • 1970-01-01
      • 2020-09-23
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多