为什么在 Python 中更改列表 ‚y‘ 也会更改列表 ‚x‘?
在使用 Python 的过程中,我们经常会遇到这样的情况:当我们进行了一些操作后,原本不相干的列表却出现了相应的变化。这种现象常常会让人感到疑惑,并且在编写程序时也容易出错。本文将会探讨这种现象的原因和解决方法。
阅读更多:Python 教程
列表在 Python 中的存储方式
首先,我们需要了解 Python 中列表的存储方式。Python 中的列表是一个可变的有序集合,可以包含任意类型的对象。但是,列表并不是像 C 语言中的数组那样连续存储的。在 Python 中,列表实际上是基于数组进行的实现,但是为了能够实现动态增长,Python 会在列表中预留一些额外的空间。当列表需要增加元素时,Python 会重新分配更大的空间,并且将原来的元素复制到新的位置。因此,Python 中的列表不像 C 语言中那样需要预先指定大小,而是动态增长的。
引用和赋值
在 Python 中,对象都是通过引用来传递的。当我们新建一个对象时,Python 会为这个对象分配一段内存空间,并返回一个引用,这个引用指向这段空间的地址。我们可以将这个引用保存在一个变量中,从而实现对对象的引用。
当我们进行赋值操作时,Python 将会创建一个新的变量名,并将这个变量名和原来的对象建立起关联。如果原来的对象是可变的(例如列表、字典等),那么对其中一个变量名所指向的对象进行的更改操作,也会影响到所有使用这个对象的变量名。
例如:
x = [1, 2, 3]
y = x
y.append(4)
print(x) # 输出 [1, 2, 3, 4]
在上面的例子中,我们先创建了一个列表 [1, 2, 3]
,并将它赋值给了变量名 x
。然后,我们将 x
中存储的引用赋值给了变量名 y
。这样,x
和 y
都指向了同一个对象。接着,我们对 y
所指向的对象进行了一个更改操作,即向列表中添加了一个元素 4。在这个操作完成后,x
和 y
都指向了更改后的 [1, 2, 3, 4]
。因此,当我们输出 x
的值时,会看到它已经变成了 [1, 2, 3, 4]
。
如何避免这种现象?
对于像上面这样的问题,我们可以采用一些方式来避免。下面是几个实现方法。
使用 copy
方法
Python 列表有一个 copy
方法,可以用来生成一个对象的副本。这个方法会创建一个新的对象,并将原来对象的值复制到新对象中。这样,即使我们对新对象进行更改操作,也不会影响原来的对象。
例如:
x = [1, 2, 3]
y = x.copy()
y.append(4)
print(x) # 输出 [1, 2, 3]
print(y) # 输出 [1, 2, 3, 4]
在上面的例子中,我们先创建了一个列表 [1, 2, 3]
,并将它赋值给了变量名 x
。然后,我们使用 copy
方法创建了一个新的对象,并将它赋值给了变量名 y
。这样,x
和 y
分别指向了不同的对象。接着,我们对 y
所指向的对象进行了一个更改操作,即向列表中添加了一个元素 4。在这个操作完成后,y
指向了更改后的 [1, 2, 3, 4]
,但是 x
仍然指向原来的 [1, 2, 3]
。因此,当我们输出 x
的值时,会看到它仍然是 [1, 2, 3]
,而输出 y
的值时,会看到它已经变成了 [1, 2, 3, 4]
。
使用切片操作
除了使用 copy
方法以外,我们还可以使用切片操作来生成一个对象的副本。切片操作可以用来截取列表中的一段元素,并返回一个新的列表对象。如果我们使用切片操作截取整个列表,就可以生成它的副本。
例如:
x = [1, 2, 3]
y = x[:]
y.append(4)
print(x) # 输出 [1, 2, 3]
print(y) # 输出 [1, 2, 3, 4]
在上面的例子中,我们先创建了一个列表 [1, 2, 3]
,并将它赋值给了变量名 x
。然后,我们使用切片操作 x[:]
创建了一个新的对象,并将它赋值给了变量名 y
。这样,x
和 y
分别指向了不同的对象。接着,我们对 y
所指向的对象进行了一个更改操作,即向列表中添加了一个元素 4。在这个操作完成后,y
指向了更改后的 [1, 2, 3, 4]
,但是 x
仍然指向原来的 [1, 2, 3]
。因此,当我们输出 x
的值时,会看到它仍然是 [1, 2, 3]
,而输出 y
的值时,会看到它已经变成了 [1, 2, 3, 4]
。
避免在列表上直接进行更改操作
除了生成副本以外,我们还可以通过避免在列表上直接进行更改操作,来避免这种现象。当我们需要更改列表中的元素时,可以先生成一个新的列表,然后将原来列表中的值复制到新列表中,并在新列表上进行更改操作。这样,原来的列表就不会受到任何影响。
例如:
x = [1, 2, 3]
y = [i for i in x]
y.append(4)
print(x) # 输出 [1, 2, 3]
print(y) # 输出 [1, 2, 3, 4]
在上面的例子中,我们先创建了一个列表 [1, 2, 3]
,并将它赋值给了变量名 x
。然后,我们通过生成一个新的列表,将它的值复制为和 x
相同的 [1, 2, 3]
,并将它赋值给了变量名 y
。这样,x
和 y
分别指向了不同的对象。接着,我们对 y
所指向的对象进行了一个更改操作,即向列表中添加了一个元素 4。在这个操作完成后,y
指向了更改后的 [1, 2, 3, 4]
,而 x
仍然指向原来的 [1, 2, 3]
。因此,当我们输出 x
的值时,会看到它仍然是 [1, 2, 3]
,而输出 y
的值时,会看到它已经变成了 [1, 2, 3, 4]
。
结论
在 Python 中,对象都是通过引用来传递的。当我们进行赋值操作时,Python 将会创建一个新的变量名,并将这个变量名和原来的对象建立起关联。如果原来的对象是可变的,那么对其中一个变量名所指向的对象进行的更改操作,也会影响到所有使用这个对象的变量名。
为了避免这种现象,我们可以使用 copy
方法或者切片操作来生成对象的副本。这样,即使我们对副本进行更改操作,也不会影响到原对象。另外,我们还可以避免在列表上直接进行更改操作,而是先生成一个新的列表,然后将原来列表中的值复制到新列表中,并在新列表上进行更改操作。这样,原来的列表就不会受到任何影响。