第2章:序列构成的数组-序列的增量赋值(+=、*=)

增量赋值运算符 += 和 *= 的表现取决于它们的第一个操作对象,是可变类型还是不可变类型。
+= 背后的特殊方法是 __iadd__ (用于“就地加法”)。但是如果操作对象没有实现这个方法的话,Python 会退一步调用 __add__ 。

对可变序列使用增量赋值运算符:

        如果 a 实现了 __iadd__ 方法,就会调用这个方法。对可变序列(例如 listbytearray 和 array.array)来说,a 会就地改动,就像调用了 a.extend(b) 一样。

a = [1, 2, 3]
b = [4, 5, 6]

print(id(a))  # 4566048648

a += b
print(a)  # [1, 2, 3, 4, 5, 6]

print(id(a))  # 4566048648  id 没有发生变化,说明仍然是原来的列表

对不可变序列使用增量赋值运算符:

        但是如果 a 没有实现 __iadd__ 的话(不可变序列没有实现此方法),a += b 这个表达式的效果就变得跟 a = a + b 一样了:首先计算 a + b,得到一个新的对象,然后赋值给 a

a = (1, 2, 3)
b = (4, 5, 6)

print(id(a))  # 4308234672

a += b
print(a)  # (1, 2, 3, 4, 5, 6)

print(id(a))  # 4307974760  id 发生变化,说明是新建对象

         对不可变序列进行重复拼接操作的话,效率会很低,因为每次都会创建一个新的对象,解释器需要把原对象中的元素复制到新对象中,然后再添加新的元素。

总体来讲,可变序列一般都实现了 __iadd__ 方法,因此 += 是就地加法;而不可变序列根本就不支持这个操作,没有实现这个方法。

上面所说的这些关于 += 的概念也适用于 *=,不同的是,后者相对应的是 __imul__

特殊的字符串类型:

        字符串( str )类型也是不可变序列,但是因为字符串拼接操作太常见了,所以 CPython 对字符串拼接操作做了优化,为 str 初始化内存空间时,程序会为它留出额外的可扩展空间,因此在进行增量操作的时候,并不会涉及复制原有字符串到新位置的操作。

一个关于+=的谜题:

在终端中,下面代码中的打印结果是什么?

A. t 变成 (1, 2, [30, 40, 50, 60])。
B. 因为 tuple 不支持对它的元素赋值,所以会抛出 TypeError 异常。
C. 以上两个都不是。
D. a 和 b 都是对的。

>>> t = (1, 2, [30, 40])
>>> t[2] += [50, 60]
>>> 
>>> print(t)

答案是:D,原因如下:

Python 在整个过程中干了什么?
➊ 将 t[2] 的值存入 TOS(Top Of Stack,栈的顶端)。
➋ 计算 TOS += [50, 60]。这一步能够完成,是因为 TOS 指向的是一个可变对象(列表)。
➌ t[2] = TOS 赋值。这一步失败,是因为 s 是不可变的元组,抛出了错误 TypeError: 'tuple' object does not support item assignment;
❹ 再次打印 t 。输出结果:(1, 2, [30, 40, 50, 60]) ;

总结:

1. 不要把可变对象放在元组里;
2. 增量赋值不是一个原子操作。我们刚才也看到了,它虽然抛出了异常,但还是完成了操作。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值