【问题标题】:Tuple assignment in Python, Is this a bug in Python? [duplicate]Python中的元组赋值,这是Python中的错误吗? [复制]
【发布时间】:2017-02-02 01:36:10
【问题描述】:

我正在阅读这篇有趣的帖子https://asmeurer.github.io/blog/posts/tuples/

作者在脚注处展示了这个例子

>>> t=1,2,[3,4]
>>> t
(1, 2, [3, 4])
>>> t[2]+=[5,6]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'tuple' object does not support item assignment

虽然 Python 引发了异常,但它确实改变了元组

>>> t
(1, 2, [3, 4, 5, 6])

不确定这里发生了什么,这是一个错误吗?

2.7.10 和 3.5.1 中的行为相同

【问题讨论】:

  • 请注意,t[2]=t[2]+[1,2] 会引发,但不会修改元组

标签: python list python-2.7 python-3.x tuples


【解决方案1】:

这不是错误,但引发异常时您将处于的状态可能会令人困惑。

不得更改元组的设计,但这仅适用于元组而不适用于它的项目。如果它包含一个可变项目,则可以修改该项目。在您的情况下,列表是可以修改的项目。

现在+= 运算符或多或少地映射为+,然后将其分配给原始变量。 + 操作是可能的,您可以添加两个列表。出于优化的原因,+ 的实现类似于extend() 并进行了更新。但是分配失败,因为元组项不能被偶然发现。

基本上有两个教训:

  1. Python 运算符可以按类型实现,其行为可能与琐碎的预期不同。
  2. 如果在调用函数时引发异常并且未正确处理,则它可能已经做了一些未清理的事情。

【讨论】:

    【解决方案2】:

    这是因为+= 运算符(即__iadd__,或内部就地添加)实际上确实在分配发生后返回了一些东西。在list 中,这将转换为extend 调用(或类似的调用),因此在返回对列表的引用以分配给t[2] 之前,新项目已经进入,然后引发异常。现在您检查值,您可以看到它已添加。以下是演示这一点的最少代码:

    >>> class AddIDemo(object):
    ...     def __init__(self, items):
    ...         self.items = list(items) 
    ...     def __iadd__(self, other):
    ...         print('extending other %r' % other) 
    ...         self.items.extend(other.items)
    ...         print('returning self to complete +=')
    ...         return self 
    ...     def __repr__(self):
    ...         return self.items.__repr__() 
    ... 
    >>> demo = AddIDemo([1, 2])
    >>> demo += AddIDemo([3, 4]) 
    extending other [3, 4]
    returning self to complete +=
    >>> demo 
    [1, 2, 3, 4]
    >>> t = 1, 2, demo 
    >>> t 
    (1, 2, [1, 2, 3, 4])
    >>> 
    >>> t[2] += AddIDemo([5, 6]) 
    extending other [5, 6]
    returning self to complete +=
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    TypeError: 'tuple' object does not support item assignment
    >>> t 
    (1, 2, [1, 2, 3, 4, 5, 6])
    >>> 
    

    请注意,我添加了更多打印语句来显示被调用的函数以及操作是如何发生的,就像它在通过 +=__iadd__ 的标准 list 操作中所做的那样。

    【讨论】:

      【解决方案3】:

      所以+= 的行为有点奇怪。对于像整数这样的不可变对象,它必须将新对象分配给相同的名称:

      a = 4
      a += 3
      

      对于可变类型,例如列表中,对象被原地改变,但也返回相同的对象被分配给相同的名称。第一步适用于您的元组,但不适用于第二步。

      这就是列表扩展后引发异常的原因。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-02-21
        • 2019-01-22
        • 1970-01-01
        • 2014-12-26
        • 1970-01-01
        • 2012-03-25
        相关资源
        最近更新 更多