前提:
1、Python 中万物皆为对象,变量以引用的方式指向对象。
2、身份
2.1 可变对象:对象的内存值可以被改变,引用变量改变后,实际上是其所指向的值发生了变化,当前这块内存区域中存放的内容发生了改变,包括:list dict set;
2.2 不可变对象:对象的内存值不能被改变,如果变量引用了不可变的对象,当改变该变量时,由于其所指向的对象的值不能都被改变,因此需要把原来的值复制出来一份后再改变;即在内存中开辟一块新的区域,变量再指向这个新的区域地址,那么变量就引用了新的对象,包括:tuple string int float bool
值传递(passl-by-value)过程中,被调函数的形式参数作为被调函数的局部变量处理,即在堆栈中开辟了内存空间来存放由主调函数放进来的实参的值,从而想成为了实参的一个副本。值传递的特点是被调函数对形参的任何操作都是作为局部变量进行,不会影响主调函数的实参变量的值。
示例:
def ChangeInt(a):
a = 10
print('函数内a的内存地址:',id(a))
a = 2
print('执行函数前,a的内存地址:', id(a))
ChangeInt(a)
print('执行函数后,a的内存地址:', id(a))
print('a =',a)
执行结果:
执行函数前,a的内存地址: 8791093155632
函数内a的内存地址: 8791093155888
执行函数后,a的内存地址: 8791093155632
a = 2
可以看出,实参a的变量引用前后未发生变化。形参a指向了内存中的新地址。
引用传递(pass-by-reference)过程中,被调函数的形式参数虽然也作为局部变量在堆栈中开辟了内存空间,但这时存放的是由主调函数放进来的实参变量的地址。被调函数对形参的任何操作都被处理成间接寻址,即通过堆栈中存放的地址访问主调函数中的实参变量。正因为如此,被调函数对形参做任何的操作都影响了主调函数中的实参变量。
示例:
def changeme(mylist):
mylist.append([1,2,3,4])
print("函数内取值: ", mylist)
print('函数内的内存地址:',id(mylist))
return
mylist = [10,20,30]
changeme(mylist)
print("函数外取值: ",mylist)
print('函数外的内存地址:', id(mylist))
执行结果:
函数内取值: [10, 20, 30, [1, 2, 3, 4]]
函数内的内存地址: 34405056
函数外取值: [10, 20, 30, [1, 2, 3, 4]]
函数外的内存地址: 34405056
可以看出,形参的变化影响了实参。实参、形参指向了内存中的同一个地址。
python参数传递采用的是“传对象引用”的方式。这种方式相当于传值和传引用的一种结合。如果函数收到的是一个可变对象(字典、列表、集合)的引用,就能修改对象的原始值--相当于‘传引用’来传递对象。如果函数收到的是一个不可变对象(数字、字符或元组)的引用,就不能直接修改原始对象--相当于通过‘值传递’来传递对象。