python 進階-高階函數

高階函數:能夠接受函數作爲參數的函數,叫做高階函數

math.sqrt() 求平方根

變量可以指向一個函數

例1:

abs(-7)

>>>7

f=abs

f(-7)

>>>7

函數名是指向函數的變量:

abs=len

abs('abc')

>>>3

高階函數示例:

>>> def add(x,y,f):
...     return f(x)+f(y)
...
>>>
>>> add(-5,9,abs)
14

高階函數一:map()

map()是 Python 內置的高階函數,它接收一個函數 f 和一個 list,並通過把函數 f 依次作用在 list 的每個元素上,得到一個新的 list 並返回。

def f(x):
    return x*x
print map(f, [1, 2, 3, 4, 5, 6, 7, 8, 9])

輸出結果:

[1, 4, 9, 10, 25, 36, 49, 64, 81]

注意:map()函數不改變原有的 list,而是返回一個新的 list。

利用map()函數,可以把一個 list 轉換爲另一個 list,只需要傳入轉換函數。

由於list包含的元素可以是任何類型,因此,map() 不僅僅可以處理只包含數值的 list,事實上它可以處理包含任意類型的 list,只要傳入的函數f可以處理這種數據類型。

高階函數二:reduce()

reduce()函數也是Python內置的一個高階函數。reduce()函數接收的參數和 map()類似,一個函數 f,一個list,但行爲和 map()不同,reduce()傳入的函數 f 必須接收兩個參數,reduce()對list的每個元素反覆調用函數f,並返回最終結果值。

例如,編寫一個f函數,接收x和y,返回x和y的和:

def f(x, y):
    return x + y

調用 reduce(f, [1, 3, 5, 7, 9])時,reduce函數將做如下計算:

先計算頭兩個元素:f(1, 3),結果爲4;
再把結果和第3個元素計算:f(4, 5),結果爲9;
再把結果和第4個元素計算:f(9, 7),結果爲16;
再把結果和第5個元素計算:f(16, 9),結果爲25;
由於沒有更多的元素了,計算結束,返回結果25。

上述計算實際上是對 list 的所有元素求和。雖然Python內置了求和函數sum(),但是,利用reduce()求和也很簡單。

reduce()還可以接收第3個可選參數,作爲計算的初始值。如果把初始值設爲100,計算:

reduce(f, [1, 3, 5, 7, 9], 100)

結果將變爲125

高階函數三:filter()

filter()函數是 Python 內置的另一個有用的高階函數,filter()函數接收一個函數 f 和一個list,這個函數 f 的作用是對每個元素進行判斷,返回 True或 False,filter()根據判斷結果自動過濾掉不符合條件的元素,返回由符合條件元素組成的新list。

例如,要從一個list [1, 4, 6, 7, 9, 12, 17]中刪除偶數,保留奇數,首先,要編寫一個判斷奇數的函數:

def is_odd(x):
    return x % 2 == 1

然後,利用filter()過濾掉偶數:

filter(is_odd, [1, 4, 6, 7, 9, 12, 17])

結果:[1, 7, 9, 17]

利用filter(),可以完成很多有用的功能,例如,刪除 None 或者空字符串:

def is_not_empty(s):
    return s and len(s.strip()) > 0
filter(is_not_empty, ['test', None, '', 'str', '  ', 'END'])

結果:['test', 'str', 'END']

注意: s.strip(rm) 刪除 s 字符串中開頭、結尾處的 rm 序列的字符。

當rm爲空時,默認刪除空白符(包括'\n', '\r', '\t', ' '),如下:

a = '     123'
a.strip()

結果: '123'

a='\t\t123\r\n'
a.strip()

結果:'123'


高階函數四:sorted()

Python內置的 sorted()函數可對list進行排序:

>>>sorted([36, 5, 12, 9, 21])
[5, 9, 12, 21, 36]

但 sorted()也是一個高階函數,它可以接收一個比較函數來實現自定義排序,比較函數的定義是,傳入兩個待比較的元素 x, y,如果 x 應該排在 y 的前面,返回 -1,如果 x 應該排在 y 的後面,返回 1。如果 x 和 y 相等,返回 0。

因此,如果我們要實現倒序排序,只需要編寫一個reversed_cmp函數:

def reversed_cmp(x, y):
    if x > y:
        return -1
    if x < y:
        return 1
    return 0

這樣,調用 sorted() 並傳入 reversed_cmp 就可以實現倒序排序:

>>> sorted([36, 5, 12, 9, 21], reversed_cmp)
[36, 21, 12, 9, 5]

sorted()也可以對字符串進行排序,字符串默認按照ASCII大小來比較:

>>> sorted(['bob', 'about', 'Zoo', 'Credit'])
['Credit', 'Zoo', 'about', 'bob']

'Zoo'排在'about'之前是因爲'Z'的ASCII碼比'a'小。


python中返回函數

Python的函數不但可以返回int、str、list、dict等數據類型,還可以返回函數!

例如,定義一個函數 f(),我們讓它返回一個函數 g,可以這樣寫:

def f():
    print 'call f()...'
    # 定義函數g:
    def g():
        print 'call g()...'
    # 返回函數g:
    return g

仔細觀察上面的函數定義,我們在函數 f 內部又定義了一個函數 g。由於函數 g 也是一個對象,函數名 g 就是指向函數 g 的變量,所以,最外層函數 f 可以返回變量 g,也就是函數 g 本身。

調用函數 f,我們會得到 f 返回的一個函數:

>>> x = f()   # 調用f()
call f()...
>>> x   # 變量x是f()返回的函數:
<function g at 0x1037bf320>
>>> x()   # x指向函數,因此可以調用
call g()...   # 調用x()就是執行g()函數定義的代碼

請注意區分返回函數和返回值:

def myabs():
    return abs   # 返回函數
def myabs2(x):
    return abs(x)   # 返回函數調用的結果,返回值是一個數值

返回函數可以把一些計算延遲執行。例如,如果定義一個普通的求和函數:

def calc_sum(lst):
    return sum(lst)

調用calc_sum()函數時,將立刻計算並得到結果:

>>> calc_sum([1, 2, 3, 4])
10

但是,如果返回一個函數,就可以“延遲計算”:

def calc_sum(lst):
    def lazy_sum():
        return sum(lst)
    return lazy_sum

# 調用calc_sum()並沒有計算出結果,而是返回函數:

>>> f = calc_sum([1, 2, 3, 4])
>>> f
<function lazy_sum at 0x1037bfaa0>

# 對返回的函數進行調用時,才計算出結果:

>>> f()
10

由於可以返回函數,我們在後續代碼裏就可以決定到底要不要調用該函數。


Python中的閉包

這種內層函數引用了外層函數的變量(參數也算變量),然後返回內層函數的情況,稱爲閉包(Closure)

閉包的特點是返回的函數還引用了外層函數的局部變量,所以,要正確使用閉包,就要確保引用的局部變量在函數返回後不能變。

def calc_sum(lst):
    def lazy_sum():
        return sum(lst)
    return lazy_sum

注意: 發現沒法把 lazy_sum 移到 calc_sum 的外部,因爲它引用了 calc_sum 的參數 lst

python中匿名函數

高階函數可以接收函數做參數,有些時候,我們不需要顯式地定義函數,直接傳入匿名函數更方便。

在Python中,對匿名函數提供了有限支持。還是以map()函數爲例,計算 f(x)=x時,除了定義一個f(x)的函數外,還可以直接傳入匿名函數:

>>> map(lambda x: x * x, [1, 2, 3, 4, 5, 6, 7, 8, 9])
[1, 4, 9, 16, 25, 36, 49, 64, 81]

通過對比可以看出,匿名函數 lambda x: x * x 實際上就是:

def f(x):
    return x * x

關鍵字lambda 表示匿名函數,冒號前面的 x 表示函數參數。

匿名函數有個限制,就是只能有一個表達式不寫return,返回值就是該表達式的結果。

使用匿名函數,可以不必定義函數名,直接創建一個函數對象,很多時候可以簡化代碼:

>>> sorted([1, 3, 9, 5, 0], lambda x,y: -cmp(x,y))
[9, 5, 3, 1, 0]

返回函數的時候,也可以返回匿名函數:

>>> myabs = lambda x: -x if x < 0 else x 
>>> myabs(-1)
1
>>> myabs(1)
1


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章