前言
在python中,一切事物皆是对象,变量是对象在内存中的存储和地址的抽象。类型也是属于对象的,而不是变量。变量和对象是分离的,对象是内存中储存数据的实体,变量则是指向对象的指针。
“=”(赋值号)是将右侧对象的内存地址赋值给左侧的变量
a = 1
Python解释器其实顺序干了以下步骤:
1.在内存中创建一个名为a的变量(变量没有类型,只是用来存储某个类型的指针,个人理解为C++中的void*,如有错求指正)
2.创建一个int对象,该对象存储3
3.变量a存储该int对象的地址,相当于a指向该int对象,python中称为变量a引用该对象
不可变对象
不可变对象,顾名思义该对象所指向的内存中的值不能被改变。数值类型(int float)、字符串str、元组tuple都是不可变的数据类型。
当改变一个变量的时候,由于该变量所指向的内存中的值不能被改变,而我们又需要改变,大家各退一步,编译器会将原来的值复制一份后再改变,具体做法是重新开辟一块空间存放新的值,并将该变量指向这个空间。
可变对象
可变对象是该对象所指向的内存中的值可以被改变。变量(准确的说是引用)改变后,实际上是其所指的值直接发生改变,并没有发生复制行为,也没有开辟新的出地址,通俗点说就是原地改变。列表list, 字典dist, 集合set是可变的数据类型。
怎么理解第二句话,还是看动画演示:
list
dist
set
引申-深拷贝和浅拷贝
引申-值传递和引用传递
Python 中,根据实际参数的类型不同,函数参数的传递方式可分为 2 种,分别为值传递和引用(地址)传递:
- 值传递:适用于实参类型为不可变类型(字符串、数字、元组);
- 引用(地址)传递:适用于实参类型为可变类型(列表,字典);
值传递和引用传递的区别是,函数参数进行值传递后,若形参的值发生改变,不会影响实参的值;而函数参数继续引用传递后,改变形参的值,实参的值也会一同改变。
值传递
将实际参数值的副本(复制品)传入函数,而参数本身不会受到任何影响。
引用传递
如果实际参数的数据类型是可变对象(列表、字典),则函数参数的传递方式将采用引用传递方式。需要注意的是,引用传递方式的底层实现,采用的依然还是值传递的方式。
可以看到引用传递时函数中形参和外面的实参指向同一个列表。但是这里很容易造成一种错觉,会让人以为在调用 swap() 函数时,传入 swap() 函数的就是a、b列表本身,而不是它的复制品。
这是一种错觉,开头就说过,创建一个变量其实是创建一个对象,然后创建一个变量保存该对象的地址(引用变量,其实就是指针)。然后a、b变量作为参数传入 swap() 函数,这里采用的是值传递方式!把实参a传递给形参a,就是让形参a也保存列表[1, 2, 3]的地址,以此实现引用传递。所谓的引用传递实际也是值传递,形参a保存的是实参a的副本,只不过这个副本是地址,所以会影响到本体。
为什么要深究引用传递就是值传递这一点,看下面的例子:
如图所示,通过变量=None的方式,形参ab都脱离了对本体的控制。如果按照我们之前的理解:形参ab就是实参ab的话,那变量=None应该会让实参也脱离对本体的控制。