怎么在Python中定義函數(shù)?很多新手對(duì)此不是很清楚,為了幫助大家解決這個(gè)難題,下面小編將為大家詳細(xì)講解,有這方面需求的人可以來(lái)學(xué)習(xí)下,希望你能有所收獲。
1.函數(shù)的意義
一般數(shù)學(xué)上的函數(shù)是,一個(gè)或者幾個(gè)自變量,通過(guò)某種計(jì)算方式,得出一個(gè)因變量。
y = f(x)
在Python中,為了使操作更加簡(jiǎn)潔,就引入了函數(shù)這個(gè)概念。
Python中的函數(shù),可以把一大串要反復(fù)使用的代碼“定義”(封裝)成一個(gè)函數(shù),給予這個(gè)函數(shù)一個(gè)標(biāo)識(shí)符作為函數(shù)名,設(shè)置自變量和因變量。然后要使用這一大串代碼的時(shí)候,就調(diào)用這個(gè)我們自己創(chuàng)造的函數(shù),輸入自變量,然后會(huì)返回給我們因變量。
在Python中,對(duì)應(yīng)數(shù)學(xué)函數(shù)的自變量和因變量的值,叫做參數(shù)和返回值。
用def來(lái)定義一個(gè)函數(shù),下面是一個(gè)通用的模型,函數(shù)都這么定義:
def 函數(shù)名(參數(shù)列表): ... 代碼塊 ... return 返回值
然后舉一個(gè)例子,假設(shè)我們要判定一個(gè)學(xué)生的成績(jī)是及格還是不及格:
def judge_score(score): judge = None if score >= 60: judge = True print('Pass') else: #雖然成績(jī)應(yīng)該在0-100之間,但是我懶,就不限定范圍了。 judge = False print('Fail') return judge
然后試一下:
judge_score(59) Fail j = judge_score(61) Pass print(j) True #說(shuō)明j接收到的值為T(mén)rue
這段代碼很簡(jiǎn)單,但是也不能每次要判斷一次成績(jī),就敲出這么一大串,所以為了便捷,就給這段代碼用def(define,定義)封裝成一個(gè)函數(shù)。
給一個(gè)函數(shù)名judge_score,后面調(diào)用就可以用judge_score()
。
score是我們要輸入的自變量,也就是要在這個(gè)judge_score()
中進(jìn)行運(yùn)算的值。
接下來(lái)要注意:
這個(gè)'Pass'和'Fail' 不是函數(shù)的返回值!
這個(gè)'Pass'和'Fail' 不是函數(shù)的返回值!
這個(gè)'Pass'和'Fail' 不是函數(shù)的返回值!
‘Pass'和'Fail' 是函數(shù)內(nèi)部的print()打印出來(lái)的內(nèi)容,函數(shù)的返回值是寫(xiě)在return后面的內(nèi)容。
return后面可以做運(yùn)算,也可以直接寫(xiě)變量。如果這個(gè)函數(shù)的語(yǔ)句塊內(nèi)沒(méi)有寫(xiě)return語(yǔ)句,那么說(shuō)明沒(méi)有定義返回值,也就是說(shuō)調(diào)用函數(shù)什么都沒(méi)有返回,如果拿一個(gè)標(biāo)識(shí)符來(lái)接受這個(gè)函數(shù)的返回值,只能接受到None。
return后面的返回值可以是多個(gè),多個(gè)的話,就用,隔開(kāi),然后封裝成一個(gè)元組再返回。
函數(shù)名也是標(biāo)識(shí)符,有的時(shí)候可以用callable()來(lái)判斷這個(gè)標(biāo)識(shí)符是不是一個(gè)可以調(diào)用的。
callable(judge_score) #返回值為T(mén)rue
注意別再檢查的函數(shù)名標(biāo)識(shí)符后面加()。
在定義函數(shù)時(shí),函數(shù)名后面的()里面叫做參數(shù)列表,這個(gè)參數(shù)列表里的參數(shù),都是要在下面的代碼塊中要使用的。
形式參數(shù),就是指在定義函數(shù)時(shí),參數(shù)列表里寫(xiě)出的參數(shù),他們都還只是標(biāo)識(shí)符,都會(huì)在下面的代碼中出現(xiàn)。此時(shí)此刻他們沒(méi)有具體的值,只是一個(gè)“外殼”,所以叫形式參數(shù)。
比如y = 3x 里面這個(gè)x,只是一個(gè)形式,沒(méi)有具體的值。
實(shí)際參數(shù),是指在調(diào)用參數(shù)時(shí),函數(shù)名后面的括號(hào)內(nèi)給出具體的值,每個(gè)值都會(huì)與某一個(gè)形式參數(shù)對(duì)應(yīng)。這些值是存在的,不止有個(gè)外殼。也可以算作是數(shù)學(xué)函數(shù)中的自變量,用它們來(lái)計(jì)算出因變量。比
如y = 3x ,假設(shè)x=3,這個(gè)3就是實(shí)際參數(shù)。
注意,每一個(gè)形式參數(shù)都必須獲得一個(gè)值(如果沒(méi)有默認(rèn)值),才能進(jìn)行計(jì)算,否則會(huì)報(bào)錯(cuò)!
3.2.1 傳遞參數(shù)類型
傳遞參數(shù),我的理解就是把實(shí)際參數(shù)和形式參數(shù)連接起來(lái)。
定義一個(gè)函數(shù)BMI,用來(lái)計(jì)算人的body mass index:
def BMI(height, weight): index = "%.2f" % (weight / (height**2)) #留兩位小數(shù) return index
位置傳參
調(diào)用它:
BMI(1.71, 65) #返回值為22.23
可以看到我們的形式參數(shù)有height和weight,實(shí)際參數(shù)是1.71和65。在調(diào)用函數(shù)時(shí),這兩個(gè)實(shí)際參數(shù)沒(méi)有指定誰(shuí)是height,誰(shuí)是weight,是按定義函數(shù)時(shí),參數(shù)列表里面的形式參數(shù)的位置一一對(duì)應(yīng)起來(lái)。這樣就叫做位置傳參。
關(guān)鍵詞傳參
調(diào)用它:
BMI(weight=65, height=1.71) #返回值22.23
如果函數(shù)的調(diào)用者知道形式參數(shù)的標(biāo)識(shí)符,也可以直接用 形式參數(shù)名=值 這樣的方式來(lái)傳遞參數(shù),可以理解為賦值。因?yàn)槭怯藐P(guān)鍵字傳遞參數(shù),所以位置可以和定義函數(shù)時(shí)參數(shù)列表內(nèi)的形式參數(shù)順序不同。
混合傳參
有的時(shí)候,我們可能有一些參數(shù)要用位置傳參,有一些要用關(guān)鍵字傳參。萬(wàn)幸的是他們可以混合使用。但是要遵守一定的規(guī)則:
BMI(1.71, weight=65) #返回值為22.23
前面的1.71用的是位置傳參,后面65用的是關(guān)鍵詞傳參。
注意,關(guān)鍵字傳參一定要寫(xiě)在位置傳參之后!不然就會(huì)報(bào)錯(cuò)。
3.2.2 參數(shù)類型
位置參數(shù)的可變參數(shù)
比如有這樣一個(gè)需求,要輸入若干個(gè)數(shù)字,然后求出這若干個(gè)數(shù)字中的大值和最小值。分析一下,若干個(gè)數(shù)字,也就是不知道數(shù)字的個(gè)數(shù),這樣也就不知道設(shè)置多少個(gè)形式參數(shù)。這樣就可以用,*args,可變參數(shù)。
如果在定義函數(shù)時(shí)*args的左側(cè)有參數(shù),那么在調(diào)用時(shí),實(shí)際參數(shù)依次給予*arg左邊的用位置傳參的普通形式參數(shù)之后,剩下的實(shí)際參數(shù)無(wú)論多少都會(huì)被*args接收,和切片有一定的相似之處,但是差別也不小。
舉個(gè)例子
def maxmin(x,*nums): #這個(gè)x是為了顯示*args的收取范圍 length = len(nums) for i in range(1,length): #這是選擇排序的思路,走一趟,練練手 maxindex = 0 minindex = 0 if nums[i] > nums[maxindex]: maxindex = i if nums[i] < nums[minindex]: minindex = i print('x={};{};max:{};min:{}'.format(x,nums,nums[maxindex],nums[minindex]))
調(diào)用這個(gè)函數(shù):
maxmin(100,12,23,34,45,67,9) x=100;(12, 23, 34, 45, 67, 9);max:12;min:9
可以看出,在給出的實(shí)際參數(shù)中,除了第一個(gè)100,被x接收了,剩下的實(shí)際參數(shù)都被*nums接收了,然后封裝成了一個(gè)元組(講道理,用逗號(hào)隔開(kāi)了又被一個(gè)標(biāo)識(shí)符接收,確實(shí)應(yīng)該封裝成元組)。
注意,和切片不同的是,定義函數(shù)時(shí)*args會(huì)無(wú)限接收實(shí)際參數(shù),不會(huì)給后面的形式參數(shù)留值,所以在定義函數(shù)時(shí)的參數(shù)列表里,位置參數(shù)的可變參數(shù)一定要在普通位置參數(shù)之后!
關(guān)鍵字參數(shù)的可變參數(shù)
關(guān)鍵字參數(shù)的可變參數(shù),不是收集多個(gè)用關(guān)鍵字傳參的實(shí)際參數(shù),而是收集關(guān)鍵字傳參的關(guān)鍵字和值,并把他們當(dāng)做一個(gè)鍵值對(duì),收集在一個(gè)字典內(nèi),在代碼塊中使用。
def test(x=1,**nums): print(x,nums) test(c=3,x=2,a=1,b=2) 2 {'c': 3, 'a': 1, 'b': 2}
可以看出,用關(guān)鍵字傳參,可以不按位置順序來(lái),先把
關(guān)鍵字參數(shù)的可變參數(shù),和位置參數(shù)的可變參數(shù)一樣,會(huì)無(wú)限接收實(shí)際參數(shù),不過(guò)是接收實(shí)際參數(shù)的關(guān)鍵字和值,組成鍵值對(duì)。
有的時(shí)候,一些參數(shù)變化不頻繁,以上面定義的這個(gè)BMI函數(shù)來(lái)看,假設(shè)一個(gè)班里面的99%的同學(xué),身高變化很大,體重都是75kg,每次用這個(gè)函數(shù)算同學(xué)的BMI,都要重新輸入一遍身高體重,比較麻煩,所以可以在定義函數(shù)時(shí),給形式參數(shù)設(shè)置一個(gè)默認(rèn)值:
def BMI(height=1.80, weight=75): index = "%.2f" % (weight / (height**2)) #留兩位小數(shù) return index
注意,這是在定義函數(shù)時(shí),就設(shè)置好默認(rèn)值,然后調(diào)用:
BMI(1.80) #此時(shí)按height=1.80,weight=75來(lái)計(jì)算 BMI(1.71) #此時(shí)按height=1.71,weight=75來(lái)計(jì)算 BMI(65) #此時(shí)按height=65,weight=75來(lái)計(jì)算,注意和上面的區(qū)別 BMI(1.71, 65) #此時(shí)按height=1.71,weight=65來(lái)計(jì)算 BMI(height=1.90) #此時(shí)按height=1.90,weight=75來(lái)計(jì)算 BMI(1.80, weight=80) #此時(shí)按height=1.80,weight=80來(lái)計(jì)算
也就是說(shuō),如果沒(méi)有傳參,那么形式參數(shù)就會(huì)按定義參數(shù)時(shí)設(shè)置的默認(rèn)值來(lái)計(jì)算。
如果用位置傳參,就按形式參數(shù)的位置順序,依次往后覆蓋,如果實(shí)際參數(shù)沒(méi)給夠,那么沒(méi)有接收到位置傳參的形式參數(shù)就會(huì)用默認(rèn)值。(也就是說(shuō),在定義函數(shù)時(shí),雖然設(shè)置了默認(rèn)值,看起來(lái)和關(guān)鍵字參數(shù)似的,但是它也是有前后位置順序的。)
如果用關(guān)鍵字傳參,理解就比較簡(jiǎn)單了,如果給了,就用關(guān)鍵字傳參給的實(shí)際參數(shù),如果沒(méi)給,就用默認(rèn)值。
總之,如果普通的形式參數(shù)沒(méi)有設(shè)置默認(rèn)值,就必須要接收一個(gè)實(shí)際參數(shù)來(lái)使用。如果形式參數(shù)在定義時(shí)設(shè)置了默認(rèn)值,調(diào)用函數(shù)時(shí),沒(méi)有給出實(shí)際參數(shù),就用默認(rèn)值,如果給了一個(gè)實(shí)際參數(shù),就用這個(gè)新的實(shí)際參數(shù)把默認(rèn)值覆蓋。
這個(gè)參數(shù)可以理解為,必須用關(guān)鍵字傳參才能獲得的參數(shù)。
這種參數(shù)為了區(qū)別于普通的形式參數(shù),位置有所改變,在定義參數(shù)時(shí),放在*args(位置參數(shù)的可變參數(shù))和**kwargs(關(guān)鍵字參數(shù)的可變參數(shù))之間,就代表這個(gè)參數(shù)是keyword-only參數(shù)。
def test(*words, x): print(words, x)
調(diào)用這個(gè)函數(shù):
test(1, 2, 3, 4) #這樣會(huì)報(bào)錯(cuò),因?yàn)檫@四個(gè)都是位置傳參,都被*word截獲了,x沒(méi)有值 test(1, 2, 3, x=4) #這個(gè)沒(méi)毛病 (1, 2, 3) 4 test(x=4) #這個(gè)也沒(méi)毛病,*word一個(gè)值都有截獲 () 4
注意,在定義函數(shù)時(shí),參數(shù)列表里,keyword-only參數(shù)不能放在關(guān)鍵字參數(shù)的可變參數(shù)之后,因?yàn)閗eyword-only參數(shù)要用關(guān)鍵字傳參,**kwargs要截獲所有的關(guān)鍵字傳參的實(shí)際參數(shù),keyword-only參數(shù)永遠(yuǎn)也收不到值。
keyword-only參數(shù)還有另一種定義形式,比如,我們想要這個(gè)函數(shù)都用關(guān)鍵字傳參,并且不想接收任何位置傳參的實(shí)際參數(shù)(就是一用位置傳參就報(bào)錯(cuò)),可以用一下這種形式:
def test(*, x, y): print(x,y)
調(diào)用它:
test(1, x=100, y=99) #會(huì)報(bào)錯(cuò),因?yàn)?沒(méi)有形參來(lái)接收它 test(x=100, y=99) #沒(méi)毛病 100 99
重中之重:
定義函數(shù)時(shí):(位置參數(shù),帶缺省值的位置參數(shù),位置參數(shù)的可變參數(shù),keyword-only參數(shù),關(guān)鍵字參數(shù)的可變參數(shù))
調(diào)用函數(shù)時(shí):(用位置傳參的實(shí)際參數(shù),用關(guān)鍵字傳參的實(shí)際參數(shù))
但是一定要確保,定義函數(shù)時(shí),參數(shù)列表里的每一個(gè)參數(shù)(可變參數(shù)除外,因?yàn)榭勺儏?shù)可以收集到0個(gè)實(shí)際參數(shù))都要有值可以使用。
例:
def test(a,b,c,d=5,*nums,x='X',y='Y',**ddict): print(a,b,c,d) print(nums) print(x,y) print(ddict)
調(diào)用它:
test(97,98,99,100,101,102,x='XX',y='YY',name='Tom',age=10) 97 98 99 100 (101, 102) XX YY {'name': 'Tom', 'age': 10}
可以看到,有六個(gè)用位置傳參的實(shí)際參數(shù),雖然d有默認(rèn)值,但是按位置來(lái)看,d也會(huì)接收到一個(gè)新的值100,因此nums只截獲了兩個(gè)位置參數(shù)。
然后是keyword-only參數(shù),x和y,都得用關(guān)鍵字傳參。如果x和y不指定的話,就會(huì)使用默認(rèn)值x='X',y='Y'。
然后是關(guān)鍵字參數(shù)的可變參數(shù),不能說(shuō)是截獲,得說(shuō)keyword-only參數(shù)只給它剩下了兩個(gè),然后他們組成了一個(gè)字典
還要注意,在傳參的時(shí)候,位置傳參要放在最前面。然后是關(guān)鍵字傳參,從關(guān)鍵字傳參中把keyword-only參數(shù)挑走,然后剩下的給**kwargs。
有時(shí)候,定義參數(shù)的時(shí)候,有很多形參。但是實(shí)際參數(shù),都存在一個(gè)list或者說(shuō)別的數(shù)據(jù)結(jié)構(gòu)中,一個(gè)個(gè)拿出來(lái)很麻煩,所以可以種參數(shù)解構(gòu)來(lái)完成:
def maxmin(x,*nums): length = len(nums) for i in range(1,length): maxindex = 0 minindex = 0 if nums[i] > nums[maxindex]: maxindex = i if nums[i] < nums[minindex]: minindex = i print('x={};{};max:{};min:{}'.format(x,nums,nums[maxindex],nums[minindex]))
這個(gè)函數(shù),是找若干個(gè)數(shù)中的大值還有最小值的,它可以接受若干個(gè)值(因?yàn)槎x函數(shù)時(shí)用的是*args)。但是如果給我們的是一個(gè)列表,那還真把每個(gè)元素都提取出來(lái),輸入一遍嗎,太蠢了,可以直接在調(diào)用函數(shù)的時(shí)候解構(gòu)。
用*解構(gòu):
lst = [100, 12, 23, 34, 45, 67, 9] maxmin(*lst) 100 9
再看一個(gè)例子,用**解構(gòu):
def test(*nums): print(nums) test({'a':1,'b':2,'c':3}) ({'a': 1, 'b': 2, 'c': 3},) #打印出來(lái)一個(gè)元組,說(shuō)明傳入的字典沒(méi)有解構(gòu) test(*{'a':1,'b':2,'c':3}) ('a', 'b', 'c') #打印的是key,字典解構(gòu)是key的集合,沒(méi)毛病
def test(a,b,c): #因?yàn)橐幼值浣鈽?gòu)出來(lái)的 print(a,b,c) test(**{'a':1,'b':2,'c':3}) #**這樣是把字典里的鍵值對(duì)解構(gòu)出來(lái) 1 2 3
在一些已經(jīng)知道函數(shù)中,有的有返回值,比如input()
、sorted()
等,它們都可以用一個(gè)標(biāo)識(shí)符來(lái)接收,形成一個(gè)變量。有的沒(méi)有返回值,比如print()
、list.append()
等方法,都是沒(méi)有返回值的。
在自定義函數(shù)中,也能做到這一點(diǎn),用的是return語(yǔ)句:
def test(x=5): a = x ** 2 print(a) b = test() 25 #這個(gè)25是print(a)的效果 b None
這說(shuō)明,我們定義的函數(shù)test()
,它是沒(méi)有返回值的,雖然出現(xiàn)了結(jié)果,但那時(shí)print()
語(yǔ)句的打印輸出,用標(biāo)識(shí)符來(lái)接收test()
的輸出,什么都接收不到(什么都沒(méi)有就是None)。
如果用return語(yǔ)句:
def test(x=5): a = x ** 2 print(a) return a b = test() 25 #這個(gè)還是print(a)的打印輸出 b 25 #這個(gè)是b剛剛接收函數(shù)的返回值
也就是說(shuō)return語(yǔ)句后面的值,可以作為這個(gè)函數(shù)的返回值,這個(gè)值可以是一個(gè)變量,也可以是一個(gè)表達(dá)式,還可以寫(xiě)多個(gè)用逗號(hào)隔開(kāi)的值,不過(guò)最后會(huì)被封裝成一個(gè)元組返回。
def test(): .... return 1, 2, 3, 4 #返回值其實(shí)是(1, 2, 3, 4) a, b, c, d = test() #這里用解構(gòu)來(lái)接收返回值,會(huì)比較方便
如果寫(xiě)了return語(yǔ)句,return后面的值會(huì)作為返回值輸出,但是如果不寫(xiě)return語(yǔ)句,或者只寫(xiě)了一個(gè)return,后面沒(méi)有值,就說(shuō)明這個(gè)函數(shù)沒(méi)有返回值(其實(shí)是隱式調(diào)用了return = None)
自定義函數(shù)中,可以有多條return語(yǔ)句,在定義時(shí)候不會(huì)報(bào)錯(cuò),但是這些return語(yǔ)句只有一條會(huì)被執(zhí)行,執(zhí)行完這個(gè)return語(yǔ)句,函數(shù)就結(jié)束,其不會(huì)理會(huì)其他的return語(yǔ)句。
def compareThree(n): if n > 3: return 3 else: print('Less than three') return False print('nothing') return 'nothing'
看著個(gè)自定義函數(shù),如果實(shí)參大于3,返回值就是3,如果小于等于3,返回值就是False。
因?yàn)樗玫氖欠种дZ(yǔ)句,只會(huì)進(jìn)入其中一個(gè)分支,所以只有一個(gè)return會(huì)被執(zhí)行。這個(gè)分支結(jié)構(gòu)完成后,還有一個(gè)print()和return,這兩個(gè)是不會(huì)被執(zhí)行的,因?yàn)榉种дZ(yǔ)句中都有return,怎么著都會(huì)執(zhí)行其中的一個(gè),執(zhí)行完就結(jié)束這個(gè)函數(shù),所以后面的'nothing'和返回值字符串'nothing'都不會(huì)執(zhí)行。
所以還能看出,return語(yǔ)句不一定是定義函數(shù)時(shí)寫(xiě)的最后一條語(yǔ)句,但最后一條執(zhí)行的代碼,一定是return(這么說(shuō)是因?yàn)椋绻麤](méi)有寫(xiě)return,會(huì)隱式調(diào)用return = None,它默認(rèn)寫(xiě)在定義函數(shù)時(shí)的最后一句)。
在自定義函數(shù)時(shí),就涉及到一個(gè)問(wèn)題,在函數(shù)內(nèi)部定義的變量,在函數(shù)之外能不能使用。這個(gè)問(wèn)題要分類討論。
先介紹一個(gè)定義,作用域,一個(gè)標(biāo)識(shí)符的可見(jiàn)范圍,就是這個(gè)標(biāo)識(shí)符的作用域,也叫做變量的作用域(個(gè)人覺(jué)得叫變量的作用范圍比較好理解)。
a = 5 def test(): b = 10 print(a, b) return b test(10) 5 10 print(a) 5 print(b) #這里會(huì)報(bào)錯(cuò)
這里a的作用域是整個(gè)程序,也可以說(shuō)在整個(gè)程序的運(yùn)行環(huán)境中都可見(jiàn)。因?yàn)槎xa是在最外部,所以函數(shù)的內(nèi)部,不管多少層嵌套函數(shù),都可以使用a。
這種a叫做全局變量,它的作用域是全局,全局作用域就是在整個(gè)程序的運(yùn)行環(huán)境中都可見(jiàn)。
再看b,在函數(shù)內(nèi)部定義的變量,在全局中是不可見(jiàn)的,它只能在這個(gè)函數(shù)的內(nèi)部使用,所以最后在全局中print(b)的話會(huì)報(bào)錯(cuò),并且異常是NameError,說(shuō)這個(gè)b沒(méi)有被定義過(guò)。
實(shí)際參數(shù)的作用域也是只在函數(shù)的內(nèi)部,因?yàn)樾螀⑹窃诤瘮?shù)的內(nèi)部,給形參傳參之后,它還是在函數(shù)的內(nèi)部。
這種b就叫做局部變量,它只作用于當(dāng)前的函數(shù)或者類,局部變量的作用范圍不能超過(guò)當(dāng)前的局部作用域。
再看一個(gè)例子:
x = 1 def t(): x = 100 print(x) t() print(x)
x在全局下定義過(guò),但是函數(shù)內(nèi)部對(duì)這個(gè)x又重新定義了。但是在全局中,x仍然是原來(lái)的值,這說(shuō)明x=100這個(gè)標(biāo)量是局部變量,作用域只在這個(gè)函數(shù)內(nèi)部,對(duì)外部不可見(jiàn)。
有的時(shí)候,定義一個(gè)函數(shù),內(nèi)部可能還要再定義一個(gè)函數(shù):
a = 5 def test(): b = a + 5 print(b) def test2(): c = b + 5 print(c) return c d = test2() return a, b, d
分析代碼可以按這種順序,碰到def定義函數(shù),跳過(guò),等出現(xiàn)了調(diào)用,再回頭看這個(gè)函數(shù)的定義。
看這個(gè)函數(shù),有雙層嵌套,調(diào)用它:
test() #注意test2()在全局中是調(diào)用不出來(lái)的
全局中有個(gè)a = 5 ,它的作用域是全局,所以在函數(shù)內(nèi)部也可以用它,得到b = 10,打印一下,然后看見(jiàn)def test2(),跳過(guò),然后下面就是調(diào)用test2(),再返回來(lái)看test2()的定義,b的作用域是局部的,包括當(dāng)前這層函數(shù)和內(nèi)層函數(shù),所以得到c = 15,作為return返回,用標(biāo)識(shí)符d接收,最后test()這一層的return返回a, b, d。
執(zhí)行結(jié)果如下:
test() 10 15 (5, 10, 15) #這個(gè)是return的值,是output
test()這層的函數(shù)定義時(shí),為啥不return c呢,是因?yàn)閏是test2()這一層函數(shù)中定義的變量,是局部變量,對(duì)于外部是不可見(jiàn)的,如果test()中使用了c,且沒(méi)有定義過(guò),就會(huì)報(bào)錯(cuò)。
這樣會(huì)多多少少出現(xiàn)一些麻煩,所以根據(jù)需要,python給出了兩種聲明global和nonlocal。
global,它能把一個(gè)變量變成全局變量。
a = 10 def t(): global x global a x = 1 a = 100 return x print(x) #x是在函數(shù)內(nèi)部定義的,但是在全局的環(huán)境下也可以使用 1 print(a) #在內(nèi)部重新定義的a也會(huì)改變 100
但是定義函數(shù),其中有一個(gè)目的就是封裝,和外部環(huán)境隔絕,通過(guò)傳參來(lái)計(jì)算,但是這樣用global定義為全局變量,有悖于這個(gè)理念,所以盡量別用global。
nonlocal,它能把一個(gè)變量變成局部變量,就是這個(gè)變量的可見(jiàn)范圍是最外層的函數(shù),但是全局中不可見(jiàn)。
這里還要引入兩個(gè)概念,
自由變量,就是沒(méi)有在本地作用域中定義的變量
閉包,指在嵌套函數(shù)中,內(nèi)層函數(shù)用到了外層函數(shù)定義的變量(注意是外層函數(shù),不是全局中定義的)這一種現(xiàn)象。
a = 1 def t(): a = 10 print(a) def t1(): nonlocal a a = 100 print(a) t1() print(a)
調(diào)用t():
print(a) t() 1 #這是在全局環(huán)境中的a,值沒(méi)變是a 10 #這是最外層函數(shù)的a 100 #這是聲明a是nonlocal之后,給a改變值后print的a 100 #這是在最外層函數(shù)中print的a,已經(jīng)發(fā)生了改變,和內(nèi)層函數(shù)的a同步
參數(shù)可以作文局部變量理解,當(dāng)一個(gè)有默認(rèn)值的形參,沒(méi)有接收到實(shí)參時(shí),就會(huì)調(diào)用默認(rèn)值:
def t(a=[1,2,3,4]): a.append(5) print(a) t() #連續(xù)調(diào)用3次 t() t() [1, 2, 3, 4, 5] [1, 2, 3, 4, 5, 5] [1, 2, 3, 4, 5, 5, 5]
三次都使用的是形參的默認(rèn)值,但是print的結(jié)果不一樣,那么就是說(shuō)明默認(rèn)值改變了。
這是因?yàn)椋螀⒌哪J(rèn)值都會(huì)保存下來(lái),可以查看,用__defaults__:
t().__defaults__ ([1, 2, 3, 4, 5, 5, 5],) #這是三次執(zhí)行之后的默認(rèn)值
可以看到,默認(rèn)值是保存在一個(gè)元組中,如果元組中的元素是引用類型(比如list),那么儲(chǔ)存的是一個(gè)引用地址,后面通過(guò)引用地址對(duì)這個(gè)默認(rèn)值修改的話,修改的是他們共用的數(shù)據(jù)。默認(rèn)值通過(guò)這個(gè)引用地址去找數(shù)據(jù)時(shí),找到的是改過(guò)的數(shù)據(jù)。
有時(shí)候我們需要這種改變,但是有時(shí)候不需要,所以有兩種解決辦法:
def t(a=[1,2,3,4]): a = a[:] #影子拷貝,拷貝一個(gè)a,修改就是在這個(gè)新的a中 a.append(5) print(a)
但是這個(gè)有弊端,如果默認(rèn)值內(nèi)部還有引用類型,影子拷貝并不能拷貝引用類型。
還有一種方法:
def t(a=None): if a is None: a = [1,2,3,4] a.append(5) print(a)
如果不指定a,那么a就用默認(rèn)值None,就給a重新賦值一個(gè)[1,2,3,4],這時(shí)候引用地址已經(jīng)換了,不是默認(rèn)值的引用地址,但是這個(gè)新的引用地址就是我們想要的a的默認(rèn)值,然后進(jìn)行操作。
下次如果還是不指定a的話,a還是None,還會(huì)重新賦值成[1,2,3,4]。相當(dāng)于雖然想要的a默認(rèn)值是引用類型,但是對(duì)a操作不改變我們想要的a的默認(rèn)值。
在定義函數(shù)時(shí)出現(xiàn)了函數(shù)嵌套,對(duì)于每個(gè)變量的作用域就要了解清楚。
這個(gè)變量被用到時(shí),就要在這個(gè)解釋器中搜尋這個(gè)變量是在哪一層賦值的,找個(gè)搜尋順序就是L local E enclosing G global B build-in,也就是說(shuō),
先在本地(這一層作用域)查找,
沒(méi)找到就在這個(gè)函數(shù)的外部函數(shù)中查找,
如果還是沒(méi)找到,就在全局中查找,
如果仍舊沒(méi)找到,就在build-in庫(kù)中查找。
比如一個(gè)典型的錯(cuò)誤:
x = 5 def t(): print(x) x += 5
函數(shù)內(nèi)print要用到x,首先要在local中查找,查到了x,但是注意,此時(shí)的x是作用域?yàn)楹瘮?shù)內(nèi)部,不能使用外部的x=5,這是給x定義,先計(jì)算右邊,右邊的x依舊沒(méi)有被定義,所以以一個(gè)x它自己(沒(méi)有值)來(lái)定義它,就會(huì)報(bào)錯(cuò)。
x = 5 def t(): print(x)
如果這樣的話,函數(shù)內(nèi)部local沒(méi)有,嵌套函數(shù)enclosing也沒(méi)有,那么就去global全局中找,找到了x=5,使用它。
至于build-in 是指我們用到的一些內(nèi)建函數(shù),print(),input()等,都是定義在build-in庫(kù)中的,要使用時(shí),也是一層一層往上找,顯然L E G中都沒(méi)有,然后去B中找,找到了,使用它。
兩種方法:
第一個(gè)是重新定義同樣函數(shù)名的函數(shù)。
第二個(gè)是用del:
del t
看完上述內(nèi)容是否對(duì)您有幫助呢?如果還想對(duì)相關(guān)知識(shí)有進(jìn)一步的了解或閱讀更多相關(guān)文章,請(qǐng)關(guān)注創(chuàng)新互聯(lián)成都網(wǎng)站設(shè)計(jì)公司行業(yè)資訊頻道,感謝您對(duì)創(chuàng)新互聯(lián)成都網(wǎng)站設(shè)計(jì)公司的支持。
另外有需要云服務(wù)器可以了解下創(chuàng)新互聯(lián)scvps.cn,海內(nèi)外云服務(wù)器15元起步,三天無(wú)理由+7*72小時(shí)售后在線,公司持有idc許可證,提供“云服務(wù)器、裸金屬服務(wù)器、網(wǎng)站設(shè)計(jì)器、香港服務(wù)器、美國(guó)服務(wù)器、虛擬主機(jī)、免備案服務(wù)器”等云主機(jī)租用服務(wù)以及企業(yè)上云的綜合解決方案,具有“安全穩(wěn)定、簡(jiǎn)單易用、服務(wù)可用性高、性價(jià)比高”等特點(diǎn)與優(yōu)勢(shì),專為企業(yè)上云打造定制,能夠滿足用戶豐富、多元化的應(yīng)用場(chǎng)景需求。
當(dāng)前標(biāo)題:怎么在Python中定義函數(shù)-創(chuàng)新互聯(lián)
網(wǎng)頁(yè)地址:http://m.2m8n56k.cn/article38/dsippp.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供自適應(yīng)網(wǎng)站、軟件開(kāi)發(fā)、服務(wù)器托管、搜索引擎優(yōu)化、App設(shè)計(jì)、全網(wǎng)營(yíng)銷推廣
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:[email protected]。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)
猜你還喜歡下面的內(nèi)容