Python中的append方法是一个常用的方法,可以将一个对象添加到列表末尾。

例如:

list = [1,2,3]
list.append(4)
# 得到的新的列表就变成了[1,2,3,4]

考虑这样一段代码,

a = [1,2]
b = []

b.append(a)
print(b)

a.append(0)
print(b)

他的执行效果如下,明明两次打印之间并没有对b操作,那么为什么b会发生改变呢?

[[1, 2]]
[[1, 2, 0]]

原来,append方法是浅拷贝。在python中,对象赋值实际上是对象的引用,当创建一个对象,然后把它赋值给另一个变量的时候,python并没有拷贝这个对象,而只是拷贝了这个对象的引用,这就是浅拷贝。 

C语言中,在声明变量的时候,int a,int b,这两条语句为a,b两个变量分别赋予了两块不同的内存空间,然后赋值的时候再将相应的值存储到对应的存储空间。

但是在python中变量的赋值与C语言是截然不同的,考虑下面的代码,

a = 1
b = 1

print(id(a))        # id函数用于获取对象的内存地址
print(id(b))

运行结果为,

2021455792
2021455792
#a,b的内存空间是一样的。

 在python中,先生成对象,变量再对对象进行引用,在本例中,1就是对象,然后a再对1进行引用,由于常数是不可变类型,所以1的内存空间是一样的,所以a,b引用的是用一块内存空间。虽然变量名不一样,但是他们引用的对象是相同的。

python append字符 python string append_python append字符

回到文章最开始的代码。在开始的问题中,

a = [1,2]
b = []

b.append(a)
print(b)

a.append(0)
print(b)

我们逐步来看。首先,b.append(a)就是对a进行了浅拷贝,结果为b=[[1,2]],但b[0]与a引用的对象是相同的,下面可以通过id函数进行验证,

a = [1,2]
b = []
b.append(a)

print(id(a))
print(id(b[0]))

执行结果为:

23768904
23768904

可见,b[0]与a指向同个内存地址。

下一步,代码执行a.append(0),列表是可变类型(*见后文补充可变类型和不可变类型的知识),这一步在原地址的列表的末尾添加0,原地址的内容被改变了但是地址没有变,所以a和b[0]的内容同时被改变了,这就是为什么对a进行append操作b会跟着发生改变。

python append字符 python string append_python append字符_02

发生这些的前提是对同一个地址上的内容进行操作,所以影响了指向该地址的所有变量。

如果是进行下面的操作,则是另一种结果:

a = [1,2]
b = []

b.append(a)
print(b)
a = [1,2]
a.append(0)
print(b)

执行结果为:

[[1, 2]]
[[1, 2]]

只是多加了一条语句,b就不会随着a的改变而改变,原因就是第二句a=[1,2]重新生成了一个不同的对象列表list是可变类型,当存在多个值相同的list类型变量时,指向的内存地址不同),a引用了这个对象,这时候就跟b没有关系了。

 

python append字符 python string append_内存地址_03

补充知识:Python的可变类型与不可变类型

 Python的每个对象都分为可变和不可变,主要的核心类型中,数字、字符串、元组是不可变的,列表、字典是可变的。

不可变类型:

对不可变类型的变量重新赋值,实际上是重新创建一个不可变类型的对象,并将原来的变量重新指向新创建的对象(如果没有其他变量引用原有对象的话(即引用计数为0),原有对象就会被回收)。

值不同,内存地址不同。例如,假设i=5,  执行 i += 1 时,内存地址都会变化,实际上 i += 1 并不是真的在原有的int对象上+1,而是重新创建一个value为6的int对象,i引用自这个新的对象。因此int 类型是不可变的。

值相同,内存地址相同。对于不可变类型int,无论创建多少个不可变类型(如i , j , k),只要值相同,都指向同个内存地址。

另,浮点类型是不可变类型。但,修改代码声明两个相同值的浮点型变量,查看它们的id,发现它们并不是指向同个内存地址,这点和int类型不同(这方面涉及Python内存管理机制,Python对int类型和较短的字符串进行了缓存,无论声明多少个值相同的变量,实际上都指向同个内存地址)。

可变类型:

以list为例。list在append之后,还是指向同个内存地址,因为list是可变类型,可以在原处修改。

当存在多个值相同的list类型变量时,指向的内存地址不同。我们也可以通过b = a 的赋值语句,让他们指向同个内存地址。这个时候需要注意,因为a、b指向同个内存地址,而a、b的类型都是List,可变类型,对a、b任意一个List进行修改,都会影响另外一个List的值。