Python中的函數(shù)(二)
在上一篇文章中提到了Python中函數(shù)的定義和使用,在這篇文章里我們來討論下關(guān)于函數(shù)的一些更深的話題。在學(xué)習(xí)C語言函數(shù)的時(shí)候,遇到的問題主要有形參實(shí)參的區(qū)別、參數(shù)的傳遞和改變、變量的作用域。同樣在Python中,關(guān)于對(duì)函數(shù)的理解和使用也存在這些問題。下面來逐一講解。
一.函數(shù)形參和實(shí)參的區(qū)別
相信有過編程語言經(jīng)驗(yàn)的朋友對(duì)形參和實(shí)參這兩個(gè)東西并不陌生。形參全稱是形式參數(shù),在用def關(guān)鍵字定義函數(shù)時(shí)函數(shù)名后面括號(hào)里的變量稱作為形式參數(shù)。實(shí)參全稱為實(shí)際參數(shù),在調(diào)用函數(shù)時(shí)提供的值或者變量稱作為實(shí)際參數(shù)。舉個(gè)例子:
二.參數(shù)的傳遞和改變
在大多數(shù)高級(jí)語言當(dāng)中,對(duì)參數(shù)的傳遞方式這個(gè)問題的理解一直是個(gè)難點(diǎn)和重點(diǎn),因?yàn)樗斫馄饋聿⒉皇悄敲粗庇^明了,但是不理解的話在編寫程序的時(shí)候又極其容易出錯(cuò)。下面我們來探討一下Python中的函數(shù)參數(shù)的傳遞問題。
首先在討論這個(gè)問題之前,我們需要明確一點(diǎn)就是在Python中一切皆對(duì)象,變量中存放的是對(duì)象的引用。這個(gè)確實(shí)有點(diǎn)難以理解,“一切皆對(duì)象”?對(duì),在Python中確實(shí)是這樣,包括我們之前經(jīng)常用到的字符串常量,整型常量都是對(duì)象。不信的話可以驗(yàn)證一下:
print id(5)print id('python')x=2print id(x)y='hello'print id(y)
這段代碼的運(yùn)行結(jié)果是:
先解釋一下函數(shù)id( )的作用。下面這段話是官方文檔對(duì)id()函數(shù)的解釋:
顯而易見,id(object)函數(shù)是返回對(duì)象object在其生命周期內(nèi)位于內(nèi)存中的地址,id函數(shù)的參數(shù)類型是一個(gè)對(duì)象,因此對(duì)于這個(gè)語句
id(2)沒有報(bào)錯(cuò),就可以知道2在這里是一個(gè)對(duì)象。下面再看幾個(gè)例子:
x=2print id(2)print id(x)y='hello'print id('hello')print id(y)
其運(yùn)行結(jié)果為:
從結(jié)果可以看出,id(x)和id(2)的值是一樣的,id(y)和id('hello')的值也是一樣的。
在Python中一切皆對(duì)象,像2,'hello'這樣的值都是對(duì)象,只不過5是一個(gè)整型對(duì)象,而'hello'是一個(gè)字符串對(duì)象。上面的x=2,在Python中實(shí)際的處理過程是這樣的:先申請(qǐng)一段內(nèi)存分配給一個(gè)整型對(duì)象來存儲(chǔ)整型值2,然后讓變量x去指向這個(gè)對(duì)象,實(shí)際上就是指向這段內(nèi)存(這里有點(diǎn)和C語言中的指針類似)。而id(2)和id(x)的結(jié)果一樣,說明id函數(shù)在作用于變量時(shí),其返回的是變量指向的對(duì)象的地址。因?yàn)樽兞恳彩菍?duì)象,所以在這里可以將x看成是對(duì)象2的一個(gè)引用。
下面再看幾個(gè)例子:
其運(yùn)行結(jié)果為:
從運(yùn)行結(jié)果可以看到id(x)和id(y)的結(jié)果是相同的,id(s)和id(t)的結(jié)果也是相同的。這說明x和y指向的是同一對(duì)象,而t和s也是指向的同一對(duì)象。x=2這句讓變量x指向了int類型的對(duì)象2,而y=2這句執(zhí)行時(shí),并不重新為2分配空間,而是讓y直接指向了已經(jīng)存在的int類型的對(duì)象2.這個(gè)很好理解,因?yàn)楸旧碇皇窍虢oy賦一個(gè)值2,而在內(nèi)存中已經(jīng)存在了這樣一個(gè)int類型對(duì)象2,所以就直接讓y指向了已經(jīng)存在的對(duì)象。這樣一來不僅能達(dá)到目的,還能節(jié)約內(nèi)存空間。t=s這句變量互相賦值,也相當(dāng)于是讓t指向了已經(jīng)存在的字符串類型的對(duì)象'hello'(這個(gè)原理和C語言中指針的互相賦值有點(diǎn)類似)。
看這幅圖就理解了:
下面再看幾個(gè)例子:
x=2print id(2)print id(x)x=3print id(3)print id(x)L=[1,2,3]M=Lprint id(L)print id(M)print id(L[2])L[0]=2print id(L)print M
運(yùn)行結(jié)果為:
下面來分析一下這個(gè)結(jié)果,兩次的id(x)的值不同,這個(gè)可能讓人有點(diǎn)難以理解。注意,在Python中,單一元素的對(duì)象是不允許更改的,比如整型數(shù)據(jù)、字符串、浮點(diǎn)數(shù)等。x=3這句的執(zhí)行過程并不是先獲取x原來指向的對(duì)象的地址,再把內(nèi)存中的值更改為3,而是新申請(qǐng)一段內(nèi)存來存儲(chǔ)對(duì)象3,再讓x去指向?qū)ο?,所以兩次id(x)的值不同。然而為何改變了L中的某個(gè)子元素的值后,id(L)的值沒有發(fā)生改變?在Python中,復(fù)雜元素的對(duì)象是允許更改的,比如列表、字典、元組等。Python中變量存儲(chǔ)的是對(duì)象的引用,對(duì)于列表,其id()值返回的是列表第一個(gè)子元素L[0]的存儲(chǔ)地址。就像上面的例子,L=[1,2,3],這里的L有三個(gè)子元素L[0],L[1],L[2],L[0]、L[1]、L[2]分別指向?qū)ο?、2、3,id(L)值和對(duì)象3的存儲(chǔ)地址相同,看下面這個(gè)圖就明白了:
因?yàn)長和M指向的是同一對(duì)象,所以在更改了L中子元素的值后,M也相應(yīng)改變了,但是id(L)值并沒有改變,因?yàn)檫@句L[0]=2只是讓L[0]重新指向了對(duì)象2,而L[0]本身的存儲(chǔ)地址并沒有發(fā)生改變,所以id(L)的值沒有改變( id(L)的值實(shí)際等于L[0]本身的存儲(chǔ)地址)。
下面就來討論一下函數(shù)的參數(shù)傳遞和改變這個(gè)問題。
在Python中參數(shù)傳遞采用的是值傳遞,這個(gè)和C語言有點(diǎn)類似。先看幾個(gè)例子:
def modify1(m,K): m=2 K=[4,5,6] return def modify2(m,K): m=2 K[0]=0 returnn=100L=[1,2,3]modify1(n,L)print nprint Lmodify2(n,L)print nprint L
程序運(yùn)行結(jié)果為:
從結(jié)果可以看出,執(zhí)行modify1( )之后,n和L都沒有發(fā)生任何改變;執(zhí)行modify2( )后,n還是沒有改變,L發(fā)生了改變。因?yàn)樵赑ython中參數(shù)傳遞采用的是值傳遞方式,在執(zhí)行函數(shù)modify1時(shí),先獲取n和L的id( )值,然后為形參m和K分配空間,讓m和K分別指向?qū)ο?00和對(duì)象[1,2,3]。m=2這句讓m重新指向?qū)ο?,而K=[4,5,6]這句讓K重新指向?qū)ο骩4,5,6]。這種改變并不會(huì)影響到實(shí)參n和L,所以在執(zhí)行modify1之后,n和L沒有發(fā)生任何改變;在執(zhí)行函數(shù)modify2時(shí),同理,讓m和K分別指向?qū)ο?和對(duì)象[1,2,3],然而K[0]=0讓K[0]重新指向了對(duì)象0(注意這里K和L指向的是同一段內(nèi)存),所以對(duì)K指向的內(nèi)存數(shù)據(jù)進(jìn)行的任何改變也會(huì)影響到L,因此在執(zhí)行modify2后,L發(fā)生了改變。
三.變量的作用域
在Python中,也存在作用域這個(gè)問題。在Python中,會(huì)為每個(gè)層次生成一個(gè)符號(hào)表,里層能調(diào)用外層中的變量,而外層不能調(diào)用里層中的變量,并且當(dāng)外層和里層有同名變量時(shí),外層變量會(huì)被里層變量屏蔽掉。舉個(gè)例子:
在函數(shù)function中,while循環(huán)外面和while循環(huán)里面都有變量x,此時(shí),while循環(huán)外面的變量x會(huì)被屏蔽掉。注意在函數(shù)內(nèi)部定義的變量作用域都僅限于函數(shù)內(nèi)部,在函數(shù)外部是不能夠調(diào)用的,一般稱這種變量為局部變量。
還有一種變量叫做全局變量,它是在函數(shù)外部定義的,作用域是整個(gè)文件。全局變量可以直接在函數(shù)里面應(yīng)用,但是如果要在函數(shù)內(nèi)部改變?nèi)肿兞?,必須使用global關(guān)鍵字進(jìn)行聲明。
關(guān)于函數(shù)的形參和實(shí)參、參數(shù)的傳遞以及變量的作用域問題這里就討論這么多了,關(guān)于函數(shù)參數(shù)的類型問題會(huì)在《Python中的函數(shù)(三)》中繼續(xù)講解。
聯(lián)系客服