Pulpcode

捕获,搅碎,拼接,吞咽

0%

python tuple 的几个问题

众所周知,python的tuple对象是不可变对象,创建之后就是不可变对象,无法修改。

1
2
3
4
s = (1, 2, 3)
s[2] = 5
# 会抛出以下错误
# TypeError: 'tuple' object does not support item assignment

那么如果是这样修改呢?

1
2
3
4
5
6
7
8
a = 1
b = 2
c = 3
s = (a, b, c)
a = "str"

print s
# s 的结果依旧是 (1, 2, 3)

为什么会这样?很简单,python是强类型语言,但是同是又是动态语言,动态语言使得变量名像标签一样,被“贴”到了变量对象上 s = (a, b, c)的时候,已经用a,b,c三个所贴的对象,初始化了一个tuple s对象,之后只不过又把a这个标签贴到了一个字符串对象而已。所以s当然不会变了。

但是如果这个tuple里面放了一些list,那么list里面的值能不能修改呢?

实验一下,就会发现是可以修改的。

1
2
3
4
5
def foo():
t = ([1],["a"])
t[0].append(2)
t[1].append("b")
print t

你会发现t的值确实发生变化了,实际上这有点类似于c++中常量指针。

指针常量是指针的常量,它是不可改变地址的指针,但可以对它所指向的内容进行修改。

1
2
int a = 0;
int *const p = &a;

这样p就不能被指向别的内容了,但是p本身指向的内容是可以修改的。

你可以想象,tuple就像是一个维护了n个不可改变的标签的容器。

有趣的题目

接下来来看此片博客的重头戏,先看此题目:

下面这个操作会获得什么结果:

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

以下是四个选项:

  1. t = (1, 2, [30, 40, 50, 60])
  2. TypeError
  3. 都不是
  4. 1,2都发生

答案是4,实际上,它会抛异常TypeError: 'tuple' object does not support item assignment,告诉你tuple是不可变的类型,然后t实际上又发生了改变。

原因就是 += 这个操作,实际上发生了类似如下的故事:

1
2
3
temp = t[2]
temp += [50, 60]
t[2] = temp

更深入的理解

关键还要区分,在python中, a = a + ba += b是两个不同的概念。

‘+’代表连接操作,其结果是创建了一个新的列表。

而’+=’实际上是把新列表添加到了旧有列表里,类似extend。

所以上题改成t[2] = t[2] + [50, 60]就玩不了了,会直接报错。
当然改成 t[2].extend([50,60])不会报错,看来+=和extend,看起来效果是一样的,但实际上并不完全等价。