Python 中的对象变动

Python 中的数据类型包含可变(mutable)与不可变(immutable),有时候会让人很头疼。

首先让我们来看一个例子:

1
2
3
4
5
6
7
>>> before = ['hi']
>>> after = before
>>> after += ['hello']
>>> before
['hi', 'hello']
>>> after
['hi', 'hello']

再来看一个例子:

1
2
3
4
5
6
7
>>> before = "hi"
>>> after = before
>>> after += "hello"
>>> before
'hi'
>>> after
'hihello'

我们对第一个的例子的,预期应该同第二个例子一样,before 变量前后的值是不变的,只有 after的值会变化:

1
2
3
4
5
6
7
>>> before = ['hi']
>>> after = before
>>> after += ['hello']
>>> before
['hi']
>>> after
['hi', 'hello']

但是,实际结果并不符合预期,这是为什么呢?因为 Pyhton 中对象可变性在作祟。什么是对象可变?就是每当你将一个变量赋值为另一个可变类型的变量时,对这个数据的任意改动会同时反映到这两个变量上去。新变量只不过是老变量的一个别名而已。这个情况只是针对可变数据类型。列表(list)是属于可变类型的,因此 ,第一个例子中的before 变量前后的值是不一样的。而字符串(string)是属于不可变类型的,所以,第二个例子中的 before 变量前后不变。

下面再举一个例子说明,这样可以更理解对象可变这个概念,同时又能够避免有些问题。

1
2
3
4
5
6
7
8
9
10
def add_to(num, target=[]):
target.append(num)
return target

>>> add_to(1)
[1]
>>> add_to(2)
[1, 2]
>>> add_to(3)
[1, 2, 3]

你期待的函数的表现应该是这个样子的:

1
2
3
4
5
6
7
8
9
10
def add_to(num, target=[]):
target.append(num)
return target

>>> add_to(1)
[1]
>>> add_to(2)
[2]
>>> add_to(3)
[3]

这也是因为列表是可变对象的原因。上面的函数还暴露出一个问题,即在 Python 中,当函数被定义时,默认参数只会运算一次,而不是每次被调用时都会重新运算。所以,应该避免定义可变类型的参数。如果希望每次调用函数的时候,默认函数都重新运算,那么上面的函数可以改写成下面的形式:

1
2
3
4
5
6
7
8
9
10
11
12
13
def add_to(num, target=None):
if target == None:
target = []
target.append(num)

return target

>>> add_to(1)
[1]
>>> add_to(2)
[2]
>>> add_to(3)
[3]

最后,总结一下 Python 中哪些对象可变,哪些对象不可变。

  • 可变对象:dictlist
  • 不可变对象:stringintfloattuple

以上就是对象可变的所有内容了。