Python编程核心内容 ---- Function(函数)
什么“大事”呢?下面將要介紹Python編程的核心內容之一——函數。
對于Python編程,函數的重要性不言而喻。重要的事情講三遍:函數實在是太重要,太關鍵了。
引入函數
之前,我們編寫程序遵循的原則:根據業務邏輯從上到下實現功能,其往往用一長段代碼來實現指定功能,開發過程中最常見的操作就是粘貼復制,也就是將之前實現的代碼塊復制到現需功能處。這種編程方式雖然可以應付一般性問題,但是不能對付大多數問題。這不,下面就來個例子。
r1 = 12.3r2 = 9.1r3 = 64.21s1 = 2 * 3.14 * r15 s2 = 2 * 3.14 * r26 s3 = 2 * 3.14 * r3圓是個神奇的圖形。特別是π,它讓人類陷入無限的遐想。OK,回歸正題。為了求圓的周長,我們需要引入公式:周長 = 2 * π * r(半徑)。看到這兒,某些讀者可能會有疑惑:這跟函數有什么關系,之前的方式依然適用。是的,這的確是可以的,但這很麻煩,太重復啦。那如果現在需要把 π 更改為3.1415926535,那該怎么辦呢?難道我們要一個一個地去改???Oh,my god!!!這時,我嗅到了函數的味道。
有了函數,我們就不再每次寫c = 2 * 3.14 * x,而是寫成更有意義的函數調用c = perimeter_of_circle(x),而函數perimeter_of_circle本身只需要寫一次,就可以多次調用。
Python不但能非常靈活地定義函數,而且本身內置了很多有用的函數,可以直接調用。
是的,函數最大的優點:增強代碼的重用性和可讀性。Python中,函數就是最基本的一種代碼抽象的方式。.
函數定義
在Python中,函數有五大要點,分別是def、函數名、函數體、參數、返回值,以及兩個英文版符號,分別是括號(括號內為參數)和冒號(:)。
-
def:函數的關鍵字,沒它可不行。
-
函數名:函數的名稱,根據函數名調用函數。
-
函數體:函數中進行一系列的具體操作。
-
參數:為函數體提供數據。
-
返回值:當函數執行完畢后,可以給調用者返回數據。
上述函數的要點中,最重要的是參數和返回值。
1.返回值
函數是一個功能塊,該功能到底執行成功與否,需要通過返回值來告知調用者。
2.參數
定義函數時,參數是一定需要考慮的。Python的函數定義非常簡單,但靈活度卻非常大。
對于函數的調用者來說,只需要知道如何傳遞正確的參數,以及函數將返回什么樣的值就夠了,函數內部的復雜邏輯被封裝起來,調用者無需了解。
Python中,參數類型有:必選參數、默認參數、可變參數、關鍵字參數和命名關鍵字參數。函數中,參數定義的順序必須是:必選參數、默認參數、可變參數、命名關鍵字參數和關鍵字參數。
3.空函數
空函數:什么事也不做,可以用pass語句。既然“一事不做”,那空函數還有什么用處?實際上pass可以用來作為占位符,比如現在還沒想好怎么寫函數的代碼,就可以先放一個pass,讓代碼能運行起來。如此,運行代碼程序就不會出現錯誤了。
#空函數 def nop():pass函數參數
Python中,參數是非常靈活的。掌握參數就能領悟函數的真諦了。這是真的。參數是比較難理解的,特別是參數組合。
1.位置參數
既然說函數,就需要展示函數:
''' 遇到問題沒人解答?小編創建了一個Python學習交流QQ群:857662006 尋找有志同道合的小伙伴,互幫互助,群里還有不錯的視頻學習教程和PDF電子書! ''' #位置參數(必選參數) def involution(x):return x * x >>>involution(3) 9 >>>involution(5) 25如代碼所示,參數x就是一個位置參數。
2.默認參數
Python函數支持默認參數,即可以給函數的參數指定默認值。當該參數沒有傳入相應的值時,該參數就使用默認值。
#默認參數 def involution(x,n = 2):s = 1while n > 0:n = n - 1s = s * xreturn s >>>involution(6) 36 >>>involution(5,3) 125如代碼所示,當我們調用involution(5),就相當于調用involution(5,2)。
注:
1.設置默認參數時,必選參數在前,默認參數在后,否則Python的解釋器會報錯;
2.定義默認參數要牢記:默認參數必須指向不可變對象!
''' 遇到問題沒人解答?小編創建了一個Python學習交流QQ群:857662006 尋找有志同道合的小伙伴,互幫互助,群里還有不錯的視頻學習教程和PDF電子書! ''' >>>def add_end(L=[]): ... L.append('END') ... return L ... >>>add_end() ['END'] >>>add_end() ['END','END'] >>>add_end() ['END','END','END']上述代碼展示的是默認參數不為不可變對象的現象。因此,默認參數必須指向不可變對象【字符串、None……】。
3.可變參數
在Python函數中,還可以定義可變參數。顧名思義,可變參數就是傳入的參數個數是可變的,可以是1個、2個到任意個,還可以是0個。
我們以數學題為例子,給定一組數字a,b,c……,請計算a2 + b2 + c2 + ……。
要定義出這個函數,我們必須確定輸入的參數。由于參數個數不確定,我們首先想到可以把a,b,c……作為一個list或tuple傳進來,這樣,函數可以定義如下:
#一般性函數 def calc(numbers):sum = 0for n in numbers:sum = sum + n * nreturn sum如何調用calc()函數呢?需要調用時,需要為參數引入list或者tuple。
''' 遇到問題沒人解答?小編創建了一個Python學習交流QQ群:857662006 尋找有志同道合的小伙伴,互幫互助,群里還有不錯的視頻學習教程和PDF電子書! ''' #函數調用 >>> calc([1, 2, 3]) 14 >>> calc((1, 3, 5, 7)) 84然而,如果我們使用可變參數,我們可以進行簡化,方法如下:
#可變參數 def calc(*numbers):sum = 0for n in numbers:sum = sum + n * nreturn sum咋調用呢?這個可簡單啦,再也不用list或者tuple了。參數調用只需如下所示:
#可變參數的魅力 >>> calc(1, 2, 3) 14 >>> calc(1, 3, 5, 7) 84#參數調用不用calc([1,2,3]),括號內還用寫中括號,好麻煩~~~定義可變參數和定義一個list或tuple參數相比,僅僅在參數前面加了一個*號。在函數內部,參數numbers接收到的是一個tuple,因此,函數代碼完全不變。但是,調用該函數時,可以傳入任意個參數,包括0個參數:
>>> calc(1, 2) 5 >>> calc() 0如果已經有一個list或者tuple,要調用一個可變參數怎么辦?可以這樣做:
>>> nums = [1, 2, 3]>>> calc(nums[0], nums[1], nums[2])14這種寫法當然是可行的,問題是太繁瑣,所以Python允許你在list或tuple前面加一個*號,把list或tuple的元素變成可變參數傳進去:
>>> nums = [1, 2, 3]>>> calc(*nums)144.關鍵字參數
可變參數允許你傳入0個或任意個參數,這些可變參數在函數調用時自動組裝為一個tuple。而關鍵字參數允許你傳入0個或任意個含參數名的參數,這些關鍵字參數在函數內部自動組裝為一個dict。dict就是字典,它是鍵值對組合,益處多多~~~
#引入關鍵字參數,默認為**kwdef person(name, age, **kw):print('name:', name, 'age:', age, 'other:', kw)函數person除了必選參數name和age外,還接受關鍵字參數kw。在調用該函數時,可以只傳入必選參數(必選參數必須全部傳入,否則會出錯),也可以傳入關鍵字參數。注:關鍵字參數可是任意個的。
''' 遇到問題沒人解答?小編創建了一個Python學習交流QQ群:857662006 尋找有志同道合的小伙伴,互幫互助,群里還有不錯的視頻學習教程和PDF電子書! ''' #調用關鍵字參數 >>>def person(name,age,**kw): ... print('name:',name,'age:',age,'other:',kw) ... >>>person('Jack') Traceback (most recent call last):File "<stdin>", line 1, in <module> TypeError: person() missing 1 required positional argument: 'age' >>>person('Jack',36) name:Jack age:36 other:{} >>>person('Jack',36,city='Hangzhou') name:Jack age:36 other:{'city':'Hangzhou'} >>>person('Jack',36,city='Hangzhou',job='Engineer') name:Jack age:36 other:{'city':'Hangzhou','job':'Engineer'}關鍵字參數有什么用呢?其實,既然存在就有它的強大之處。就像自然界中的萬物,物競天擇,適者生存。如果它能夠在自然界中生存下來,那么它就有獨特的生存本領。因此,關鍵字參數還是有用武之地的。
它可以擴展函數的功能。比如,在person函數里,我們保證能接收到name和age這兩個參數,但是,如果調用者愿意提供更多的參數,我們也能收到。試想你正在做一個用戶注冊的功能,除了用戶名和年齡是必填項外,其他都是可選項,利用關鍵字參數來定義這個函數就能滿足注冊的需求。
如何操作呢?我們可以先組裝出一個dict,然后,把該dict轉換為關鍵字參數傳進去:
>>> extra = {'city': 'Hangzhou', 'job': 'Engineer'}>>> person('Jack', 36, city=extra['city'], job=extra['job'])name: Jack age: 36 other: {'city': 'Hangzhou', 'job': 'Engineer'}當然了,上面代碼調用方式有點煩,通過dict鍵來查找值。我們可以通過關鍵字簡化一下:
>>> extra = {'city': 'Hangzhou', 'job': 'Engineer'}>>> person('Jack', 36, **extra)name: Jack age: 36 other: {'city': 'Hangzhou', 'job': 'Engineer'}extra表示把extra這個dict的所有key-value用關鍵字參數傳入到函數的kw參數,kw將獲得一個dict。
5.命名關鍵字參數
對于關鍵字參數,函數的調用者可以傳入任意不受限制的關鍵字參數。至于到底傳入了哪些,就需要在函數內部通過kw檢查。
仍以person()函數為例,我們希望檢查是否有city和job參數:
''' 遇到問題沒人解答?小編創建了一個Python學習交流QQ群:857662006 尋找有志同道合的小伙伴,互幫互助,群里還有不錯的視頻學習教程和PDF電子書! ''' def person(name, age, **kw):if 'city' in kw:# 有city參數passif 'job' in kw:# 有job參數passprint('name:', name, 'age:', age, 'other:', kw)如果要限制關鍵字參數的名字,就可以用命名關鍵字參數,例如,只接收city和job作為關鍵字參數。這種方式定義的函數如下:
def person(name, age, *, city, job):print(name, age, city, job)和關鍵字參數*kw不同,命名關鍵字參數需要一個特殊分隔符,*后面的參數被視為命名關鍵字參數。
調用命名關鍵字參數方式如下:
那如果參數中有可變參數,那該怎么辦呢?
若可變參數后面跟著命名關鍵字參數,后面跟著的命名關鍵字參數就不再需要一個特殊分隔符*了。
def person(name, age, *args, city, job):print(name, age, args, city, job)命名關鍵字參數必須傳入參數名,這和位置參數不同。如果沒有傳入參數名,調用將報錯。而命名關鍵字參數可以有缺省值,從而簡化調用:
def person(name, age, *, city='Hangzhou', job):print(name, age, city, job)由于命名關鍵字參數city具有默認值,調用時,可不傳入city參數:
>>> person('Jack', 36, job='Engineer')Jack 36 Hangzhou Engineer6.參數組合
目前,函數中共有5種常用的參數類型。若只傳入一種類型的參數,這太簡單了。難點在哪?難點就在參數組合使用,那是相當惡心。不過,平時最好不要混合使用參數,不然容易搞得“烏煙瘴氣”。
OK!言歸正傳,不然跑題啦。
Python中,定義一個函數,我們可以用必選參數、默認參數、可變參數、關鍵字參數和命名關鍵字參數,這5種參數都可以組合使用。但是請注意,參數定義的順序必須是:必選參數、默認參數、可變參數、命名關鍵字參數和關鍵字參數。
下面來定義一個函數,該函數參數包含一種或幾種參數。
def f1(a, b, c=0, *args, **kw):print('a =', a, 'b =', b, 'c =', c, 'args =', args, 'kw =', kw)def f2(a, b, c=0, *, d, **kw):print('a =', a, 'b =', b, 'c =', c, 'd =', d, 'kw =', kw)在函數調用的時候,Python解釋器自動按照參數位置和參數名把對應的參數傳進去。
>>> f1(1, 2) a = 1 b = 2 c = 0 args = () kw = {} >>> f1(1, 2, c=3) a = 1 b = 2 c = 3 args = () kw = {} >>> f1(1, 2, 3, 'a', 'b') a = 1 b = 2 c = 3 args = ('a', 'b') kw = {} >>> f1(1, 2, 3, 'a', 'b', x=99) a = 1 b = 2 c = 3 args = ('a', 'b') kw = {'x': 99} >>> f2(1, 2, d=99, ext=None) a = 1 b = 2 c = 0 d = 99 kw = {'ext': None}最神奇的是通過一個tuple和dict,你也可以調用上述函數:
''' 遇到問題沒人解答?小編創建了一個Python學習交流QQ群:857662006 尋找有志同道合的小伙伴,互幫互助,群里還有不錯的視頻學習教程和PDF電子書! ''' >>> args = (1, 2, 3, 4) >>> kw = {'d': 99, 'x': '#'} >>> f1(*args, **kw) a = 1 b = 2 c = 3 args = (4,) kw = {'d': 99, 'x': '#'} >>> args = (1, 2, 3) >>> kw = {'d': 88, 'x': '#'} >>> f2(*args, **kw) a = 1 b = 2 c = 3 d = 88 kw = {'x': '#'}所以,對于任意函數,都可以通過類似func(*args, **kw)的形式調用它,無論它的參數是如何定義的。
然而,雖然函數參數類型多達5種,但不要同時使用太多的組合,否則函數接口的可理解性很差。哎,簡簡單單才是真啊。
7.函數參數小結
參數,作為函數傳入值的媒介,這里有必要做一個總結。
一、Python的函數具有非常靈活的參數形態,既可以實現簡單的調用,又可以傳入非常復雜的參數;
二、默認參數一定要用不可變對象,如果是可變對象,程序運行時會有邏輯錯誤;
三、*args是可變參數,args接收的是一個tuple;
四、**kw是關鍵字參數,kw接收的是一個dict;
五、可變參數既可以直接傳入:func(1, 2, 3),又可以先組裝list或tuple,再通過args傳入:func((1, 2, 3));
六、關鍵字參數既可以直接傳入:func(a=1, b=2),又可以先組裝dict,再通過kw傳入:func({‘a’: 1, ‘b’: 2});
七、使用*args和**kw是Python的習慣寫法,當然也可以用其他參數名,但最好使用習慣用法;
八、命名的關鍵字參數是為了限制調用者可以傳入的參數名,同時可以提供默認值;
九、定義命名的關鍵字參數在沒有可變參數的情況下不要忘了寫分隔符*,否則定義的將是位置參數。
故而,為了學好Python中的函數部分,參數不容忽視。
函數調用
在學習了函數的定義之后,我們應該需要調用函數,獲取我們想要的數據。
如何調用函數呢?語法:函數名(參數)
Python中,大佬們內置了許多有點用的函數,而我們只需拿來就行(這讓我想起了魯迅的“拿來主義”)。
若要調用Python中的內置函數,我們首先要知道函數名和參數。哈哈,又是參數~~~
比如我想要求某數的絕對值。如果你不知道Python有相關的內置函數,就只能這么做:
#求取絕對值 >>>def abs(num): ... if num >= 0: ... return num ... else: ... return (-num) ... >>>abs(9) 9 >>>abs(0) 0 >>>abs(-8) 8上述代碼雖然可以實現求絕對值的功能,但是太繁瑣,需要敲幾行代碼才能實現該功能。然而,Python中有這個函數可以直接調用并輸出結果。
#Python內置函數:abs() >>>abs(-9) 9 >>>abs(9) 9 #獲取幫助文檔 >>>help(abs) Help on built-in function abs in module builtins:abs(x, /)Return the absolute value of the argument.對于函數參數,通常會遇到以下兩個問題:
1.如果函數傳入參數的數量錯誤,會如何呢?簡單,直接Error唄。比如abs():
#函數傳入參數的數量錯誤>>> abs(-9,89)Traceback (most recent call last):File "<stdin>", line 1, in <module>TypeError: abs() takes exactly one argument (2 given)2.如果傳入的參數數量是對的,但參數類型不能被函數所接受,也會報TypeError的錯誤,并且給出錯誤信息:str是錯誤的參數類型:
#傳入的參數類型錯誤>>> abs('a')Traceback (most recent call last):File "<stdin>", line 1, in <module>TypeError: bad operand type for abs(): 'str'常見內置函數(Built-in Functions)
Python 3.x版本下官方網站:https://docs.python.org/3/library/functions.html。該網址內顯示Python內置函數相關內容(Built-in Functions)。
1.數據結構相關:list()、tuple()、dict()、str()……
2.數字相關:abs()、min()、max()、len()……
3.其他:int()、float()……
好,不一一例舉了,直接上圖吧~~~
數據類型轉換
Python內置的常用函數還包括數據類型轉換函數,比如int()函數可以把其他數據類型轉換為整數:
#Python之數據類型轉換(int、float、str……) >>> int('123') 123 >>> int(12.34) 12 >>> float('12.34') 12.34 >>> str(1.23) '1.23' >>> str(100) '100' >>> bool(1) True >>> bool('') False函數別名
了解Linux的讀者可能知道別名(alias,unalias)這個指令。Python中也有“別名”之說,比如把函數名賦給變量:
#函數“別名” >>>abs(-8) 8 >>>a = abs >>>a(-9) 9 >>>a(0) 0 >>>a(9) 9遞歸函數
講述遞歸函數之前,我想起一個東西:階乘(n!)。舉個例子,我們來計算階乘n! = 1 x 2 x 3 x … x n,用函數fact(n)表示,可以看出:
fact(n) = n! = 1 x 2 x 3 x ... x (n-1) x n = (n-1)! x n = fact(n-1) x n因此,遞歸函數:在函數內部,一個函數在內部調用自身本身。
于是,fact(n)用遞歸的方式寫出來就是:
''' 遇到問題沒人解答?小編創建了一個Python學習交流QQ群:857662006 尋找有志同道合的小伙伴,互幫互助,群里還有不錯的視頻學習教程和PDF電子書! ''' #遞歸函數 >>>def fact(n): ... if n == 1: ... return 1 ... else: ... return fact(n - 1) * n ... >>>fact(1) 1 >>>fact(5) 120 #遞歸函數之棧溢出 >>>fact(1000) Traceback (most recent call last):File "<stdin>", line 1, in <module>File "<stdin>", line 5, in xFile "<stdin>", line 5, in xFile "<stdin>", line 5, in x[Previous line repeated 994 more times]File "<stdin>", line 2, in x RecursionError: maximum recursion depth exceeded in comparison如代碼所示,使用遞歸函數的優點是邏輯簡單清晰,缺點是過深的調用會導致棧溢出。
針對尾遞歸優化的語言可以通過尾遞歸防止棧溢出。尾遞歸事實上和循環是等價的,沒有循環語句的編程語言只能通過尾遞歸實現循環。
Python標準的解釋器沒有針對尾遞歸做優化,任何遞歸函數都存在棧溢出的問題。
什么是尾遞歸?這個請讀者自行查詢唄,這里就不介紹啦,嘿嘿~~~
下面來個斐波拉契數列:
#斐波拉契數列 >>>def fibo(arg1,arg2): ... if arg1 == 0: ... print(arg1,arg2) ... arg3 = arg1 + arg2 ... print(arg3) ... fibo(arg2, arg3) ... >>>fibo(0,1)上述代碼展示的斐波拉契數列會一直計算,直至棧溢出:
#斐波拉契數列導致棧溢出 488272859468887457959087733119242564077850743657661180827326798539177758919828135114407499369796465649524266755391104990099120377 Traceback (most recent call last):File "<stdin>", line 1, in <module>File "<stdin>", line 6, in fiboFile "<stdin>", line 6, in fiboFile "<stdin>", line 6, in fibo[Previous line repeated 992 more times]File "<stdin>", line 5, in fibo RecursionError: maximum recursion depth exceeded while calling a Python object 16602747662452097049541800472897701834948051198384828062358553091918573717701170201065510185595898605104094736918879278462233015981029522997836311232618760539199036765399799926731433239718860373345088375054249如何才能避免棧溢出呢?自己想唄,要不大腦會生銹的。
對于漢羅塔問題,利用遞歸來解決該問題也是相當的簡單,且代碼清晰:
#遞歸解決漢羅塔問題 >>>def hanrota(n,a,b,c): ... if n == 1: ... print(a,'-->',c) ... else: ... hanrota(n - 1,a,c,b) ... hanrota(1,a,b,c) ... hanrota(n - 1,b,a,c) ... >>>hanrota(3,'A','B','C') A --> C A --> B C --> B A --> C B --> A B --> C A --> C匿名函數
定義函數真得不可多得,但有時候不需要顯示地定義函數。因此,函數也需要靈活地運用。使用匿名函數可以更加方便。
匿名函數語法:
lambda x: x * x(關鍵字lambda表示匿名函數,冒號前面的x表示函數參數)
def f(x):return x * x匿名函數的好處:因為函數沒有名字,不必擔心函數名沖突。
def is_odd(n):return n % 2==1L = list(filter(lambda n: n%2==1,range(1,20)))print(L)1.匿名函數也是一個函數對象,也可以把匿名函數賦值給一個變量,再利用變量來調用該函數。
2.Python中,匿名函數可以作為返回值返回并輸出結果。
總結
以上是生活随笔為你收集整理的Python编程核心内容 ---- Function(函数)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 警告!你的Python代码命名太烂了,命
- 下一篇: websocket python爬虫_p