Python 的函數(shù)傳遞參數(shù):
創(chuàng)新互聯(lián)公司致力于網(wǎng)站建設(shè)、成都網(wǎng)站建設(shè),成都網(wǎng)站設(shè)計(jì),集團(tuán)網(wǎng)站建設(shè)等服務(wù)標(biāo)準(zhǔn)化,推過標(biāo)準(zhǔn)化降低中小企業(yè)的建站的成本,并持續(xù)提升建站的定制化服務(wù)水平進(jìn)行質(zhì)量交付,讓企業(yè)網(wǎng)站從市場競爭中脫穎而出。 選擇創(chuàng)新互聯(lián)公司,就選擇了安全、穩(wěn)定、美觀的網(wǎng)站建設(shè)服務(wù)!
Python 傳參數(shù)可以理解為 C 的 const 指針(your_type* const your_variable),它所指向的對(duì)象可以被修改產(chǎn)生副作用,但變量本身不能修改指向其他對(duì)象。這個(gè)和 C++ 的 reference 差不多。
所以如果一定要產(chǎn)生 C 的修改指針指向其他對(duì)象的效果,用 list、dict 或其他自定義的 mutable 對(duì)象包裝是一個(gè)辦法,但我認(rèn)為這樣是一種不良實(shí)踐。在 C 語言中用參數(shù)輸出結(jié)果有非常多的理由:
C 語言沒有 tuple,不能返回多值,除非聲明一個(gè) struct 類型。這種情況下劃分 in 參數(shù)和 out 參數(shù)成為一種慣例
C 語言沒有異常機(jī)制,返回值一般要保留給 errno
但這些情況在 Python 中都是不存在的
這個(gè)問題的答案無外乎這幾種說法:傳值,傳引用,對(duì)于可變對(duì)象是傳引用,不可變對(duì)象是傳值。
傳引用
先看下面這個(gè)例子:
def foo(n):
... print id(n)
... n = 3
... print id(n)
n = 2
id(n)
31030000L
foo(n)
31030000L
31029976L
n
2
id(n)
31030000L
由foo中兩次輸出不相等可以看出,傳引用說法并不成立。
傳值
來看下面的例子:
def foo(n):
... print n
... n.append(3)
... print n
n = [1, 2, 4, 8]
foo(n)
[1, 2, 4, 8]
[1, 2, 4, 8, 3]
n
[1, 2, 4, 8, 3]
按傳值的說法,一個(gè)值傳進(jìn)來,在函數(shù)內(nèi)改動(dòng)并不會(huì)影響變量本身的值,上面例子中n變量本身的值也被改變了,說明傳值的說法也不對(duì)。
3.可變對(duì)象傳引用,不可變對(duì)象傳值
相比上面兩種說法,這種說法似乎更靠譜,傳播也更為廣泛,那它到底對(duì)不對(duì)呢?
def foo(n):
... print id(n)
... n = ['1', '2', '3']
... print id(n)
... print n
n = [1,2,3,4,5,6]
id(n)
35637576
foo(n)
35637576
35916168
['1', '2', '3']
n
[1, 2, 3, 4, 5, 6]
按照可變對(duì)象傳引用的說法,上面list類型是可變對(duì)象,應(yīng)該傳引用,這foo方法中兩次調(diào)用id應(yīng)該輸出一樣的值,更改的結(jié)果也應(yīng)該影響到外部變量,但結(jié)果顯然不是這樣的,這說明,這種說法也是不正確的。
那么Python傳值的方法到底是什么樣呢?其實(shí)Python中的函數(shù)參數(shù)所遵循的是傳對(duì)象(call by object),或者叫做穿對(duì)象的引用(call by object reference)。在調(diào)用函數(shù)時(shí),將變量整個(gè)對(duì)象傳入,對(duì)于可變對(duì)象的修改,在函數(shù)內(nèi)外均可見;而對(duì)于不可變對(duì)象,因?yàn)槠洳⒉荒苷嬲饬x上被賦值,修改是通過生成新的對(duì)象來實(shí)現(xiàn)的。
下面來一個(gè)有趣的例子作為結(jié)尾:
def bar(a = []):
... print id(a)
... a.append(7)
... print a
for _ in range(5):
... bar()
#結(jié)果輸出請(qǐng)自己動(dòng)手實(shí)踐,原因應(yīng)該不難理解
首先你要明白,Python的函數(shù)傳遞方式是賦值,而賦值是通過建立變量與對(duì)象的關(guān)聯(lián)實(shí)現(xiàn)的。
對(duì)于你的代碼:
執(zhí)行 d = 2時(shí),你在__main__里創(chuàng)建了d,并讓它指向2這個(gè)整型對(duì)象。
執(zhí)行函數(shù)add(d)過程中:
d被傳遞給add()函數(shù)后,在函數(shù)內(nèi)部,num也指向了__main__中的2
但執(zhí)行num = num + 10之后,新建了對(duì)象12,并讓num指向了這個(gè)新對(duì)象——12。
如果你明白函數(shù)中的局部變量與__main__中變量的區(qū)別,那么很顯然,在__main__中,d仍在指著2這個(gè)對(duì)象,它沒有改變。因此,你打印d時(shí)得到了2。
如果你想讓輸出為12,最簡潔的辦法是:
在函數(shù)add()里增加return num
調(diào)用函數(shù)時(shí)使用d = add(d)
代碼如下:
def add(num):
num += 10
return num
d = 2
d = add(d)
print d
python中所有數(shù)據(jù)都是對(duì)象,所以傳參也是傳的對(duì)象的引用,這個(gè)引用在函數(shù)執(zhí)行前和執(zhí)行后是不會(huì)被改變的,如:
num
=
1
def
change(num):
print(id(num))
num
=
2
print(id(num))
執(zhí)行change(num)后num的值還是1
可以看到在執(zhí)行前num的id值(可以理解為內(nèi)存地址)是某一值
但在執(zhí)行change后,num的id值改變了,也就是說內(nèi)部的num指向了另外的對(duì)象,而外部的num卻還是指向原來的對(duì)象,所以值沒有變;
同理,如:
num_list
=
[1,2]
def
change_list(num_list):
print(id(num_list))
num_list.append(3)
print(id(num_list))
可以看到執(zhí)行change_list后num_list的id值沒有改變,也就是說num_list是在原來的對(duì)象上添加了新的數(shù)據(jù),外部的num_list也是指向這一對(duì)象,所以外部的num_list數(shù)據(jù)也添加了新的數(shù)據(jù)。