1. 可更改(mutable)对象与不可更改(immutable)对象
Python的语言特性决定了变量是没有类型的,而类型如int,double都是对象。因此,所有变量都可以认为是内存中相应类型对象的引用。这里的面试点就是:类型是属于对象的,而不是变量
var=10 #var是一个变量,在赋值之前没有类型,赋值之后是int对象的引用
Python的类型对象可分为可更改(mutable)对象与不可更改(immutable)对象
- 不可更改(immutable)对象,该对象所指向的内存中的值不能被改变。当改变某个变量时候,由于其所指的值不能被改变,相当于把原来的值复制一份后再改变,这会开辟一个新的地址,变量再指向这个新的地址。Python中的String字符串,Number数值类型(如int,double),Tuple元组都是不可更改的。
- 可更改(mutable)对象,该对象所指向的内存中的值可以被改变。变量(准确的说是引用)改变后,实际上是其所指的值直接发生改变,并没有发生复制行为,也没有开辟新的出地址,通俗点说就是原地改变。Python中的List列表,Dictionary字典,Set集合都是可更改的。
实验证明:
# id()函数输出变量的内存地址
var=10
print(var,id(var))
var=20
print(var,id(var))
mylist=[]
print(mylist,id(mylist))
mylist.append(10)
print(mylist,id(mylist))
2. Python函数传参
C/C++中函数传参分为值传递,引用传递。值传递后函数内部修改不会影响外部的值,但对引用传递的参数进行修改会同时改变外部的值。
Python的函数传参本质上都是类似值传递的复制方式,即将原始值的拷贝传递给函数。但由于变量存储的实际是类型对象的内存地址,所以可更改对象与不可更改对象在函数传参的表现并不相同。
2.1 不可更改对象
不可更改对象的函数传递仍旧表现为值传递,即内部的修改不会改变外部的值
var=10
def func(a):
print("-------- in func --------")
print("a init in func:",a,id(a))
a=10
print("a with save value:",a,id(a))
a=20
print("a with different value:",a,id(a))
print("-------- end func --------")
print(var,id(var))
func(var)
print(var,id(var))
在上面的代码中由于var是数值类型,不可更改对象,所以a也是不可更改的。值传递后进入函数,var,a指向相同的内存地址。将a重新赋予相同的值也不会修改其指向的地址,因为值没变,所以类型对象的地址相同。当a赋予不同的值,则a的地址发生改变,但不会影响var的值和指向的内存地址。
2.2 可更改对象
可更改对象的函数传递虽然是值传递,但却表现为引用传递,即内部的修改会改变外部的值
mylist=[]
def func(templist):
print("-------- in func --------")
print("templist init in func:",templist,id(templist))
# templist=[] 此操作可避免类似引用传递的效果,因为相当于将templist指向不同的地址
templist.append(1)
print("templist add value:",templist,id(templist))
templist.append(2)
print("templist add value:",templist, id(templist))
print("-------- end func --------")
print(mylist,id(mylist))
func(mylist)
print(mylist,id(mylist))
在上面的代码中由于mylist是列表,可更改对象,所以templist也是可更改的。值传递后进入函数,mylist,templist指向相同的内存地址。当向templist添加值时,操作是允许的,因此地址不变,所以外部的mylist也会发生变化。(想要避免这样,可以在函数中重新赋值)