如果我们服用b = [1,2,3]
并且尝试这样做:b+=(4,)
它返回b = [1,2,3,4]
,但是如果我们尝试做b = b + (4,)
,则它不起作用。
b = [1,2,3]
b+=(4,) # Prints out b = [1,2,3,4]
b = b + (4,) # Gives an error saying you can't add tuples and lists
我希望b+=(4,)
会失败,因为您无法添加列表和元组,但是它可以工作。因此,我尝试b = b + (4,)
希望得到相同的结果,但是没有用。
答案 0 :(得分:70)
“为什么”问题的问题在于它们通常可能意味着多种不同的含义。我会尽力回答我认为您可能会想到的每个问题。
“为什么它的工作方式可能不同?” ,例如this。基本上,+=
尝试使用不同的对象方法:__iadd__
(仅在左侧选中),与__add__
和__radd__
(“反向添加“,请在右侧检查__add__
的左侧是否没有+
)。
“每个版本的功能是什么?” 简而言之,list.__iadd__
方法的作用与list.extend
相同(但是由于语言设计的原因,仍然存在分配回来)。
例如,这也意味着
>>> a = [1,2,3]
>>> b = a
>>> a += [4] # uses the .extend logic, so it is still the same object
>>> b # therefore a and b are still the same list, and b has the `4` added
[1, 2, 3, 4]
>>> b = b + [5] # makes a new list and assigns back to b
>>> a # so now a is a separate list and does not have the `5`
[1, 2, 3, 4]
+
当然会创建一个新对象,但是明确地需要另一个列表,而不是尝试从其他顺序中拉出元素。
“为什么+ =这样做有用?效率更高; extend
方法不必创建新对象。当然,这有些令人惊讶效果有时(如上),通常Python并不是真正意义上的效率,但是这些决定是很久以前做出的。
“为什么不允许使用+添加列表和元组的原因是什么?” 参见here(感谢@ splash58);一种想法是(tuple + list)应该产生与(list + tuple)相同的类型,并且不清楚结果应该是哪种类型。 +=
不会出现此问题,因为a += b
显然不应该更改a
的类型。
答案 1 :(得分:21)
它们不是等效的:
b += (4,)
的简写:
b.extend((4,))
在+
连接列表时,通过以下方式:
b = b + (4,)
您正在尝试将元组连接到列表
答案 2 :(得分:14)
执行此操作时:
b += (4,)
被转换为此:
b.__iadd__((4,))
内部调用b.extend((4,))
,extend
接受迭代器,这也起作用:
b = [1,2,3]
b += range(2) # prints [1, 2, 3, 0, 1]
但是当您这样做时:
b = b + (4,)
被转换为此:
b = b.__add__((4,))
仅接受列表对象。
答案 3 :(得分:4)
对于mutable sequence types而言,都来自官方文档:
s += t
s.extend(t)
定义为:
将
s
的内容扩展到t
与定义不同的地方是
s = s + t # not equivalent in Python!
这也意味着 任何序列类型都将适用于t
,包括示例中的元组。
但是它也适用于范围和发电机!例如,您还可以执行以下操作:
s += range(3)
答案 4 :(得分:3)
2000年10月发布的Python 2.0中引入了+=
之类的“增强”赋值运算符。PEP 203中描述了设计和原理。这些运营商宣称的目标之一是就地运营的支持。写作
a = [1, 2, 3]
a += [4, 5, 6]
应该就地更新列表a
。如果还有其他引用列表a
,例如,当收到a
作为函数参数时。
但是,由于许多Python类型(包括整数和字符串)是不可变的,因此操作不能总是在原地进行,例如i += 1
代表整数i
可能无法就地操作。
总之,应该在可能的情况下使用扩充的赋值运算符,否则将创建一个新的对象。为了实现这些设计目标,表达式x += y
被指定为如下行为:
x.__iadd__
,则将评估x.__iadd__(y)
。x.__add__
,则将评估x.__add__(y)
。y.__radd__
,则将评估y.__radd__(x)
。通过此过程获得的第一个结果将分配回x
(除非结果是NotImplemented
单例,在这种情况下,查找将继续进行下一步)。
此过程允许支持就地修改的类型实现__iadd__()
。 不不支持就地修改的类型不需要添加任何新的魔术方法,因为Python会自动退回到本质上x = x + y
。
因此,让我们最后来解决您的实际问题–为什么您可以使用增强的赋值运算符将元组添加到列表中。从内存来看,其历史大致是这样的:list.__iadd__()
方法的实现是为了简单地调用Python 2.0中已经存在的list.extend()
方法。在Python 2.1中引入迭代器后,list.extend()
方法已更新为可以接受任意迭代器。这些更改的最终结果是my_list += my_tuple
从Python 2.1开始工作。然而,list.__add__()
方法从来都不应该支持将任意迭代器用作右手参数–对于强类型语言,这被认为是不合适的。
我个人认为,增强运算符的实现最终在Python中过于复杂。它具有许多令人惊讶的副作用,例如此代码:
t = ([42], [43])
t[0] += [44]
第二行引发TypeError: 'tuple' object does not support item assignment
,but the operation is successfully performed anyway – t
将在执行引起错误的那一行后为([42, 44], [43])
。
答案 5 :(得分:2)
大多数人希望X + = Y等于X = X +Y。实际上,Mark Lutz撰写的Python Pocket Reference(第4版)在第57页上说:“以下两种格式大致等效:X = X + Y,X + = Y”。但是,指定Python的人没有使它们等效。可能这是一个错误,只要Python继续使用,就会导致沮丧的程序员花费数小时的调试时间,但是现在只是Python的使用方式。如果X是可变序列类型,则X + = Y等于X.extend(Y)而不是X = X + Y。
答案 6 :(得分:1)
正如here所述,如果chi**2
未实现array
方法,则__iadd__
只是b+=(4,)
的简写,但显然不是,因此b = b + (4,)
确实实现了array
方法。显然__iadd__
方法的实现是这样的:
__iadd__
但是我们知道上面的代码不是def __iadd__(self, x):
self.extend(x)
方法的实际实现,但是我们可以假设并接受类似__iadd__
方法的东西,它接受extend
输入。