Mutable vs. Immutable
这里介绍一下,python 中的可变数据类型和不可变数据类型。
当一个对象被初始化的时候,这个对象会被分配一个唯一的 id,用来标示它在内存中的位置。 这里的可变和不可变对象指的是在对象 id(对应到一块内存区域)不变的情况下,一个对象的内容能否改变。
id
id() 是 python 内置函数,它返回一个数字类型,为一个对象的标示。
下面我们看一些例子:
a = 'Hello World'
b = 'Hello World'
id(a)
id(b)
a = 'Hello'
id(a)
输出的内容为:
4450937520
4450937616
4450864064
这里第一个和第二个对应的是同一个字符串,它们的 id 竟然也不同,但是有些情况是相同的。关于这部分可以再写一些东西了(这个坑以后补上)。 这里我们重点看下,第三个和第一个是不同的,因为 a 是不可变对象,如果对它赋值,相当于创建一个新的对象。
我们在看下 list 对象的表现:
l = [1, 2, 3]
id(l)
l[0] = 4
id(l)
下面输出的结果是:
4451063856
4451063856
这里我们对 l 的内容做了修改,但是它的 id 值没有改变。
可变与不可变对象
可变对象 | 不可变对象 |
---|---|
list | int |
dict | float |
set | string |
byte array | tuple |
frozen set | |
bytes | |
complex |
上面列举的是可变可不可变的对象,但是不可变对象是永远不可变吗?其实不是的,下面举一个例子。 对于 tuple 本身来说,它是不可变的,但是如果 tuple 里面的 element 是一个可变的对象呢?
t = (1, [1, 2, 3])
id(t)
t[1][0] = 2
id(t)
上面的两个 id 的输出结果是一样的。这里我们需要特别的注意。
函数传值方式
在函数参数传递的时候,它们的区别可能表现的更加明显。
简单来说,就是不可变对象传的是值,而可变对象传的是引用。其实本质上它们应该都是传引用,但是对于不可变对象来说,它做不到啊,因为我们没有办法在它原来的内存空间去更新它的内容,只能创建一个新的对象,这种表现其实就是传值了。
对于可变对象来说,这里就有些复杂了。下面看个例子:
a = [1]
def hello(a = []):
print("id:", id(a))
a.append('test')
print(a)
我们通过两种方式去调用它,
hello() # 使用默认参数
hello(a)
它们的 id 其实是不同的,因为第一种情况,a 的生存期只是在函数内部,在外部定义的 a 和它没有任何的关系。 在使用 hello(a)的时候,通过 append 操作,函数外边定义的 a 也发生了改变也就是说它传递的是引用。