第一个问题:值传递与引用传递

简单理解,值传递就是在一个参数传入到函数中后,函数中对该参数的操作不会影响函数外该参数的变量的值;而引用传递,则是参数传递进来的相当于内存地址,对该参数的操作会直接影响到外部指向其值的变量。

先说结论

python中的变量没有类型,变量相当于一个指针,可以指向任何类型的对象,也就是变量引用了某个对象;python对象是有类型的,一般看变量是什么类型需要看其引用的对象是什么类型。

python中没有严格的定义值传递与引用传递,总的看来,函数传递参数都可以看做是引用传递的(因为python变量相当于指针,传递指针就相当于传递了地址的引用),只不过因为python中的有些对象是不可变的,因此让有些值传递的过程中又像是值传递的。

当python中的函数传递过来的是不可变的值时(比如数字常量、元组和字符串),相当于 C语言中的值传递的;若传递过来的参数是可变的(如列表、字典等),则相当于引用传递。

不可变的对象作参数

看个例子:

x = 10
print("xid=",id(x))
def A(x):
    print("axid=",id(x))
    return x
def B(x):
    x += x
    return x
print("ax=",A(x))
print("bx=",B(x))
print("x=",x)

结果为:

xid= 140718279271088
axid= 140718279271088
ax= 10
bx= 20
x= 10

当x传递进函数的时候,会被当作一个局部变量,也就是会新开辟一个空间存放变量,该变量引用了参数的引用。因为原参数引用的是一个不可变的对象,所以局部变量会与函数外的变量指向同一个内存区域;该局部变量不会影响函数外的变量,但在函数内给x重新赋值的时候,会重新生成一个对象,并让局部变量x指向新的对象,而外部变量x不变。因此相当于值传递。

当传递的对象是可变对象时

x = [10]
print("xid=",id(x))
def A(x):
    print("axid=",id(x))
    return x
def B(x):
    x += x
    return x
print("ax=",A(x))
print("bx=",B(x))
print("x=",x)

结果为:

xid= 2769750468232
axid= 2769750468232
ax= [10]
bx= [10, 10]
x= [10, 10]

原理与上面的类似,但有区别,x传递进函数仍然可以看作生成了一个局部变量x,其引用了外部变量x的引用,与不变的对象的运算不同的是,此时的x += [10] 操作,不会生成新的列表对象,而是修改了原来存储空间中的列表对象,因此外部的x的值也会跟着改变。
下面的例子与传递不可变参数时是一样的:

x = [10]
print("xid=",id(x))
def A(x):
    print("axid=",id(x))
    return x
def B(x):
    x = [10,10]
    print("bxid", id(x))
    return x
print("ax=",A(x))
print("bx=",B(x))
print("x=",x)

结果为:

xid= 2769750458632
axid= 2769750458632
ax= [10]
bxid 2769750460616
bx= [10, 10]
x= [10]

这里是因为赋值号=生成了新的列表导致了局部变量x指向了新的对象,原对象不变。

总结

python中的值传递与引用传递是一个相对的概念,原值有没有变化关键在原来的变量引用的对象有没有发生改变,而这些需要根据变量引用的对象来判断。