Python學習之函數式編程

Python學習目錄python

  1. 在Mac下使用Python3
  2. Python學習之數據類型
  3. Python學習之函數
  4. Python學習之高級特性
  5. Python學習之函數式編程
  6. Python學習之模塊
  7. Python學習之面向對象編程
  8. Python學習之面向對象高級編程
  9. Python學習之錯誤調試和測試
  10. Python學習之IO編程
  11. Python學習之進程和線程
  12. Python學習之正則
  13. Python學習之經常使用模塊
  14. Python學習之網絡編程

練習代碼git

​函數式編程就是一種抽象程度很高的編程範式,純粹的函數式編程語言編寫的函數沒有變量,所以,任意一個函數,只要輸入是肯定的,輸出就是肯定的,這種純函數咱們稱之爲沒有反作用。而容許使用變量的程序設計語言,因爲函數內部的變量狀態不肯定,一樣的輸入,可能獲得不一樣的輸出,所以,這種函數是有反作用的。函數式編程的一個特色就是,容許把函數自己做爲參數傳入另外一個函數,還容許返回一個函數!github

Python對函數式編程提供部分支持。因爲Python容許使用變量,所以,Python不是純函數式編程語言。編程

函數是Python內建支持的一種封裝,咱們經過把大段代碼拆成函數,經過一層一層的函數調用,就能夠把複雜任務分解成簡單的任務,這種分解能夠稱之爲面向過程的程序設計。函數就是面向過程的程序設計的基本單元。網絡

​而函數式編程(請注意多了一個「式」字)——Functional Programming,雖然也能夠歸結到面向過程的程序設計,但其思想更接近數學計算。閉包

咱們首先要搞明白計算機(Computer)和計算(Compute)的概念。在計算機的層次上,CPU執行的是加減乘除的指令代碼,以及各類條件判斷和跳轉指令,因此,彙編語言是最貼近計算機的語言。而計算則指數學意義上的計算,越是抽象的計算,離計算機硬件越遠。對應到編程語言,就是越低級的語言,越貼近計算機,抽象程度低,執行效率高,好比C語言;越高級的語言,越貼近計算,抽象程度高,執行效率低,好比Lisp語言。app

高階函數

在python中函數名是指向函數的變量,當函數的參數也是函數的時候,這種函數咱們稱之爲高階函數。編程語言

def add(x, y, f):
    return f(x) + f(y)
add(1, -6, abs)
複製代碼

map/reduce

map()函數接收兩個參數,一個是函數,一個是Iterable,map將傳入的函數依次做用到序列的每一個元素,並把結果做爲新的Iterator返回。函數式編程

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

>>> [1, 4, 9, 16, 25, 36, 49, 64, 81]
複製代碼

reduce把一個函數做用在一個序列[x1, x2, x3, ...]上,這個函數必須接收兩個參數,reduce把結果繼續和序列的下一個元素作累積計算,效果以下:函數

reduce(f, [x1, x2, x3, x4]) = f(f(f(x1, x2), x3), x4)
複製代碼

結合map和reduce能夠整理出一個str2int的函數:

from functools import reduce

DIGITS = {'0': 0, '1': 1, '2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7, '8': 8, '9': 9}

def char2num(s):
    return DIGITS[s]

def str2int(s):
    return reduce(lambda x, y: x * 10 + y, map(char2num, s))
複製代碼

filter

Python內建的filter()函數用於過濾序列。

map()相似,filter()也接收一個函數和一個序列。和map()不一樣的是,filter()把傳入的函數依次做用於每一個元素,而後根據返回值是True仍是False決定保留仍是丟棄該元素。

例如,在一個list中,刪掉偶數,只保留奇數,能夠這麼寫:

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

list(filter(is_odd, [1, 2, 4, 5, 6, 9, 10, 15]))
# 結果: [1, 5, 9, 15]
複製代碼

sorted

對list進行排序:

>>> sorted([36, 5, -12, 9, -21])
[-21, -12, 5, 9, 36]
複製代碼

此外,sorted()函數也是一個高階函數,它還能夠接收一個key函數來實現自定義的排序,例如按絕對值大小排序:

>>> sorted([36, 5, -12, 9, -21], key=abs)
[5, 9, -12, -21, 36]
複製代碼

字符串排序:

>>> sorted(['bob', 'about', 'Zoo', 'Credit'])
['Credit', 'Zoo', 'about', 'bob']
複製代碼

忽略大小寫:

>>> sorted(['bob', 'about', 'Zoo', 'Credit'], key=str.lower)
['about', 'bob', 'Credit', 'Zoo']
複製代碼

要進行反向排序,沒必要改動key函數,能夠傳入第三個參數reverse=True

>>> sorted(['bob', 'about', 'Zoo', 'Credit'], key=str.lower, reverse=True)
['Zoo', 'Credit', 'bob', 'about']
複製代碼

返回函數

定義

高階函數除了能夠接受函數做爲參數外,還能夠把函數做爲結果值返回。

咱們來實現一個可變參數的求和。一般狀況下,求和的函數是這樣定義的:

def calc_sum(*args):
    ax = 0
    for n in args:
        ax = ax + n
    return ax
複製代碼

可是,若是不須要馬上求和,而是在後面的代碼中,根據須要再計算怎麼辦?能夠不返回求和的結果,而是返回求和的函數:

def lazy_sum(*args):
    def sum():
        ax = 0
        for n in args:
            ax = ax + n
        return ax
    return sum
複製代碼

當咱們調用lazy_sum()時,返回的並非求和結果,而是求和函數:

>>> f = lazy_sum(1, 3, 5, 7, 9)
>>> f
<function lazy_sum.<locals>.sum at 0x101c6ed90>
複製代碼

調用函數f時,才真正計算求和的結果

>>> f()
25
複製代碼

閉包

如上所示的例子中,咱們在函數lazy_sum中又定義了函數sum,而且,內部函數sum能夠引用外部函數lazy_sum的參數和局部變量,當lazy_sum返回函數sum時,相關參數和變量都保存在返回的函數中,這種稱爲「閉包(Closure)」。

注意點:返回函數不要引用任何循環變量,或者後續會發生變化的變量。

以下所示:

def count():
    fs = []
    for i in range(1, 4):
        def f():
             return i*i
        fs.append(f)
    return fs

f1, f2, f3 = count()
複製代碼

返回結果以下:

>>> f1()
9
>>> f2()
9
>>> f3()
9
複製代碼

匿名函數

關鍵字lambda表示匿名函數,冒號前面的x表示函數參數。匿名函數有個限制,就是隻能有一個表達式,不用寫return,返回值就是該表達式的結果。

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

裝飾器

代碼運行期間動態增長功能的方式,稱之爲「裝飾器」(Decorator)。本質上,decorator就是一個返回函數的高階函數。

定義一個能打印日誌的decorator,能夠定義以下:

def log(func):
    def wrapper(*args, **kw):
        print('call %s():' % func.__name__)
        return func(*args, **kw)
    return wrapper
複製代碼

觀察上面的log,由於它是一個decorator,因此接受一個函數做爲參數,並返回一個函數。咱們要藉助Python的@語法,把decorator置於函數的定義處:

@log
def now():
    print('2015-3-25')

>>> now()
call now():
2015-3-25
複製代碼

@log放到now()函數的定義處,至關於執行了語句:now = log(now),now變量指向了新的函數。

若是decorator自己須要傳入參數,那就須要編寫一個返回decorator的高階函數,寫出來會更復雜。好比,要自定義log的文本:

def log(text):
    def decorator(func):
        def wrapper(*args, **kw):
            print('%s %s():' % (text, func.__name__))
            return func(*args, **kw)
        return wrapper
    return decorator
複製代碼

這個3層嵌套的decorator用法以下:

@log('execute')
def now():
    print('2015-3-25')
    
>>> now()
execute now():
2015-3-25
複製代碼

和兩層嵌套的decorator相比,3層嵌套的效果是這樣的:>>> now = log('execute')(now)

有個問題,通過decorator裝飾以後的函數,它們的__name__已經從原來的'now'變成了'wrapper'

>>> now.__name__
'wrapper'
複製代碼

Python內置的functools.wraps用來複制函數_name_屬性的:

import functools

def log(func):
 @functools.wraps(func)
    def wrapper(*args, **kw):
        print('call %s():' % func.__name__)
        return func(*args, **kw)
    return wrapper
複製代碼

或者針對帶參數的裝飾器:

import functools

def log(text):
    def decorator(func):
 @functools.wraps(func)
        def wrapper(*args, **kw):
            print('%s %s():' % (text, func.__name__))
            return func(*args, **kw)
        return wrapper
    return decorator
複製代碼

偏函數

Python的functools模塊提供了不少有用的功能,其中一個就是偏函數(Partial function)。把一個函數的某些參數給固定住(也就是設置默認值),返回一個新的函數,調用這個新函數會更簡單。

建立偏函數時,實際上能夠接收函數對象、*args**kw這3個參數。

好比要使用int()函數去轉換二進制字符串,本來的寫法是:int(x, base=2),若是量大的話,很麻煩,因此咱們能夠這麼寫:

>>> import functools
>>> int2 = functools.partial(int, base=2)
>>> int2('1000000')
64
>>> int2('1010101')
85
複製代碼

上面的in2 = functools.partial(int, base=2)至關於:

kw = { 'base': 2 }
int('10010', **kw)
複製代碼

下一篇:Python學習之模塊

相關文章
相關標籤/搜索