寫在篇前
函數能提高應用的子產品性,和代碼的重複使用率,是程式設計必須具備的基本抽象能力。python函數更是奇妙靈活,與很多特性值得探讨,本篇文章就來詳細看看python 函數那些巧妙之處。首先,在篇前簡單說說python函數的形式。比如說我們要實作一個函數:計算一個數m的n次方,那麼函數可以定義如下:
def exponentiation(m, n):
return m^n
# def 是函數定義關鍵字
# exponentiation 是函數名
# (m,n)是函數參數,後面冒号之後是函數體
# return 表示傳回值,一個函數也可以沒有函數體,此時傳回值為None
函數參數
-
必選參數
必選參數,又稱為位置參數,我們上面定義的函數
中exponentiation
就是必選參數。當調用該函數時,每一個必選參數都必須傳入合适的值。m, n
-
預設參數
修改函數
為:exponentiation
這樣,def exponentiation(m, n=2): return m^n
便是一個預設參數,當計算一個數的二次方時,隻需要傳入參數n
即可:m
需要注意的是,必選參數必須在前,預設參數在後,否則Python的解釋器會報錯;為了避免不必要的坑,預設參數必須指向不變對象。 另外,在函數定義的時候就會确定預設參數的值,當我們改變x的值的時候對預設參數值并沒有影響,即:>>> exponentiation(3) 9
>>> x = 42 >>> def spam(a, b=x): ... print(a, b) ... >>> spam(1) 1 42 >>> x = 23 >>> spam(1) 1 42 >>>
-
可變參數
可變參數是python函數靈活性的表現之一,如果有這樣一個需求,編寫一個函數計算若幹個數的和,這時可變參數便是一種好的選擇:
def calc_sum(*args): sum = 0 for arg in args: sum += arg return sum print(calc_sum(1, 2, 3)) # 可變參數可傳入0個或任意個參數,在函數調用時自動組裝為一個tuple nums = [1, 2, 3] print(calc_sum(*nums)) # *nums意思是将nums中的所有元素以可變參數的形式傳入函數
-
關鍵字參數
關鍵字參數允許你傳入0個或任意個含參數名的參數,在函數内部會自動組裝為一個dict。
def print_info(pc_id, pc_name, **kwargs): print(pc_id, pc_name, kwargs, sep='\n') other_info = {'city': 'NanJing', 'company': 'APPLE'} print_info('0001', 'IMac-2018', city='NanJing', company='APPLE') print_info('0001', 'IMac-2018', **other_info) 0001 IMac-2018 {'city': 'NanJing', 'company': 'APPLE'} 0001 IMac-2018 {'city': 'NanJing', 'company': 'APPLE'}
關鍵字參數可以傳入任意參數名,那我們可不可以限制他傳入指定的參數名呢?答案是當然可以,采用命名關鍵字可以實作該需求。但是這裡需要搞清楚一個邏輯上的問題。這裡的指定的參數名要和前面的必選參數區分開來。命名關鍵字參數如果有預設值可以不傳入,但是如果傳入就隻能是已經限定的關鍵字參數。
def print_info(pc_id, pc_name, *, city, company):
# 用一個 *,區分必選參數和關鍵字參數
print("pc_id: %s\tpc_name: %s" % (pc_id, pc_name), end='\t')
if city:
print('city:%s' % city, end='\t')
if company:
print('company:%s' % company)
other_info = {'city': 'NanJing', 'company': 'APPLE'}
print_info('0001', 'IMac-2018', city='NanJing', company='APPLE')
print_info('0001', 'IMac-2018', **other_info)
# 但是如果函數定義中已經有了一個可變參數,命名關鍵字參數就不再需要分隔符*了
# def print_info(pc_id, pc_name, *args, city, company):
-
小結
以上四種參數可以靈活的進行組合應用,需要注意的是,參數定義的順序必須是:必選參數、預設參數、可變參數、(命名關鍵字參數)關鍵字參數。
def f(a, b, c=0, *args, **kw): print('a =', a, 'b =', b, 'c =', c, 'args =', args, 'kw =', kw)
特殊函數
匿名函數
在python中可以通過
lambda
來定義匿名函數,其傳回一個函數表達式,類似于def,但是比def更輕巧,可以沒有名字。
# 關鍵字`lambda`表示匿名函數,冒号前面的`x`,`y`表示函數參數
# 匿名函數有個限制:隻能有一個表達式,不用寫return,傳回值就是該表達式的結果。
>>>add = lambda x,y: x+y
>>>add(2,3)
5
需要注意的是,lambda表達式中的參數
x、y
等是自由變量, 在運作時綁定值,而不是定義時就綁定,這跟函數的預設值參數定義是不同的。 是以在下面例子中,調用這個lambda表達式的時候,x的值是執行時的值。
>>> x = 10
>>> a = lambda y: x + y
>>> x = 20
>>> b = lambda y: x + y
>>> a(10)
30
>>> b(10)
30
高階函數
一個函數接收另一個函數作為參數,這種函數稱之為高階函數(Higher-order Functions)。python中有幾個常用的内置高階函數:
-
filter
函數接收兩個參數,一個函數和一個序列,其中傳入的函數會依次作用于序列的每一個元素,根據傳回值是True或則False決定是否保留該元素。filter()
filter(lambda x: x>10, [1, 56, 3, 36, 9]) >>> g = filter(lambda x: x>10, [1, 56, 3, 36, 9]) >>> g <filter object at 0x10207b470> >>> type(g) <class 'filter'> >>> list(g) [56, 36]
-
map/reduce
函數接收兩個參數,一個是函數,一個是map()
,Iterable
将傳入的函數依次作用到序列的每個元素,并把結果作為新的map
傳回;Iterator
把一個函數作用在一個序列[x1, x2, x3, …]上,這個函數必須接收兩個參數,reduce把結果繼續和序列的下一個元素做累積計算。reduce()
>>> r = map(lambda x:x*x, [1, 2, 3, 4, 5, 6, 7, 8, 9]) >>> type(r) <class 'map'> >>> list(r) [1, 4, 9, 16, 25, 36, 49, 64, 81] from functools import reduce >>>reduce(lambda x,y: x+y, [1,2,3,4]) 10
-
sorted
用于可疊代對象的排序,如:
# 執行個體1 >>> sorted([36, 5, -12, 9, -21], key=abs) [5, 9, -12, -21, 36]
-
傳回函數
高階函數除了可以接受函數作為參數,也可以将函數作為傳回值,實作“延遲計算”。
def calc_sum(lst): def lazy_sum(): return sum(lst) return lazy_sum >>> f = calc_sum([1, 2, 3, 4]) >>>f <function calc_sum.<locals>.lazy_sum at 0x10e9bd158> >>>f() 10
偏函數
偏函數的作用是為函數某些參數設定預設值,使調用更加友善,以下會是一個好的例子:
points = [ (1, 2), (3, 4), (5, 6), (7, 8) ]
import math
def distance(p1, p2):
x1, y1 = p1
x2, y2 = p2
return math.hypot(x2 - x1, y2 - y1)
>>> pt = (4, 3)
>>> points.sort(key=partial(distance,p2=pt)) # 注意這裡必須是p2=pt
>>> points
[(3, 4), (1, 2), (5, 6), (7, 8)]
回調函數
在計算機程式設計中,回調函數,是指通過函數參數傳遞到其它代碼的,某一塊可執行代碼的引用。這一設計允許了底層代碼調用在高層定義的子程式。關于了解可以參考回調函數是什麼。這裡我們也給出一個例子輔助了解:
def greeting(name):
print('hello %s!' % name)
def someone_coming(callback):
name = input()
callback(name)
def main():
someone_coming(greeting)
if __name__ == '__main__':
main()
内置函數
這裡确切的應該說是其他内置函數,因為上面也涉及很多内置函數,如
sorted
、
filter
、
map
等。另外,python内置函數非常之多,如果想了解更全面的内部函數,可以參考内置函數1,内置函數2
-
zip()
用于将可疊代的對象作為參數,将對象中對應的元素打包成一個個元組,然後傳回由這些元組組成的清單。需要注意的是,當多個可疊代資料length不同時,取最小長度,其他忽略。
>>>a = [1,2,3] >>> b = [4,5,6] >>> c = [4,5,6,7,8] >>> zipped = zip(a,b) # 打包為元組的清單 [(1, 4), (2, 5), (3, 6)] >>> zip(a,c) # 元素個數與最短的清單一緻 [(1, 4), (2, 5), (3, 6)] >>> zip(*zipped) # 與 zip 相反,*zipped 可了解為解壓,傳回二維矩陣式 [(1, 2, 3), (4, 5, 6)]
-
reversed()
用于反轉序列,生成新的可疊代對象
>>> a = reversed(range(10)) # 傳入range對象 >>> a # 類型變成疊代器 <range_iterator object at 0x035634E8> >>> list(a) [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
-
enumerate()
enumerate() 函數用于将一個可周遊的資料對象(如清單、元組或字元串)組合為一個索引序列,同時列出資料和資料下标,一般用在 for 循環當中
>>>seq = ['one', 'two', 'three'] >>> for i, element in enumerate(seq): ... print i, element 0 one 1 two 2 three