参数的传递

函数的参数传递本质上就是:从实参到形参的赋值操作。Python中“一切皆对象”,所有的赋值操作皆是“引用赋值”,所以,Python中参数的传递都是“引用传递”,不是“值传递”,具体操作可分为两类:

  1. 对“可变对象”进行“写操作”,直接作用于原对象本身。
  2. 对“不可变对象”进行“写操作”,会产生一个新的“对象空间”,并用新的值填充这块空间(起到其他语言的“值传递”效果,但不是“值传递”)。

可变对象有:

       字典、列表、集合、自定义的对象等

不可变对象有:

       数字、字符串、元组、function等

传递可变对象的引用

传递参数是可变对象(如:字典、列表、自定义的其他可变对象等),实际传递的还是对象的引用。在函数体中不创建新的对象拷贝,而是可以直接修改所传递的对象。

例子:

b=[10,20]
def one(n):
    print("n",id(n))   #b和n是同一个对象,n是局部变量
    n.append(55)       #由于n是可变对象,不创建对象拷贝,直接修改这个对象
one(b)
print("b",id(b))
print(b)

执行返回:  

>>>n 1542470710600
      b 1542470710600
     [10, 20, 55]

注意(个人理解)(图里缺局部栈帧那一块)

  • 上面函数的内存分析。栈为变量,堆为内存。变量b指向内存对象(内存地址为id(b)也就是打印出来1542470710600),内存对象有包含两个对象(10和20)有可以理解内存对象的索引1和2分别指向10和20。最终指向b。
  • 调用函数的把对象b传递给n,因为n是局部变量,将形成栈帧,栈帧引用指向b的内存对象,所以n加55,在b的内存对象里面也是增加一个对象指向30的对象,同时对象30地址给了内存对象里面。栈帧消失,结果不变

传递不可变对象的引用

传递参数是不可变对象(例如:int、float、字符串、元组、布尔值等),实际传递的还是对象的引用,早“赋值操作”时,由于不可变对象无法修改,系统会创建一个新的对象。

操作:参数传递,传递不可变对象的引用

a=100
def one(n):
    print("n",id(n))    #传递进来的是a对象地址
    n=n+100             #由于a是不可变对象,因此创建新的对象n
    print("n1",id(n))   #n已经变成新的对象
    print(n)

one(a)
print("A",id(a))

返回值:

>>>n 1655292064
      n1 1655295264
      200
      A 1655292064

显然,通过id值可以看到n和a一开始是一个对象,给n赋值后,n是新的对象。

参数传递,不可变对象含可变对象

传递不可变对象时用的是浅拷贝

传递参数是不可变对象(例如:int,float,字符串、元组、布尔值),实际传递的还是对象的引用。但在“写操作”时,会创建一个新的对象拷贝。这个拷贝使用的是“浅拷贝”,不是深拷贝。

a=(10,20,[2,3])  #元组是不可变对象,列表是可变对象
print("a:",id(a))

def test_1(b):
    print('b:',id(b))
    b[2][0]=666
    print(b)
    print('b_:',id(b))
test_1(a)
print(a)

返回:

>>>a: 2128142246680
      b: 2128142246680
      (10, 20, [666, 3])
      b_: 2128142246680
      (10, 20, [666, 3])

注:传递不可变对象时,不可变对象里面包含的子对象是可变的,则方法内修改这个可变对象,源对象已发生了变化。