Python3 函數式編程(高階函數)

這一次主要是學習了一下Python3函數式編程思想,介紹了3個表明性高階函數:map(), reduce(), filter()。像 sorted() 其實也是高階函數,能夠接受函數做爲參數。這篇學習筆記中編寫了大量高階函數,同時介紹了Python中的閉包,裝飾器。這些思想和方法很美妙,我受益不淺。固然這些都須要進一步運用和學習。javascript

運行環境:Python3.6 + Jupyter notebookcss

 

函數式編程

 

函數做爲參數

In [1]:
def add(x, y, f):
    return f(x) + f(y)
In [2]:
add(-1, 1, abs)
Out[2]:
2
 

map

 

map()做爲高階函數,它將運算規則抽象了。html

In [3]:
r = map(lambda x: x*x, [1, 2, 3, 4])
list(r)
Out[3]:
[1, 4, 9, 16]
 

reduce

In [5]:
from functools import reduce
def prod(List,f):
    return reduce(f, List)
def f(x, y):
    return x * y
prod([1,2,3], f)
Out[5]:
6
 

str2int

In [6]:
from functools import reduce
num = {}
for i in range(10):
    num[str(i)] = i
    
def str2int(string:str) -> int:
    return reduce(lambda x, y: x*10 + y, map(char2num, string))
def char2num(s):
    return num[s]
In [7]:
str2int('123')
Out[7]:
123
In [8]:
type(str2int('123'))
Out[8]:
int
 

str2float

In [13]:
from functools import reduce
num = {}
for i in range(10):
    num[str(i)] = i

def char2num(s):
    return num[s]
def str2float(string):
    s = string.split('.')
    f1 = lambda x, y: x * 10 + y
    f2 = lambda x, y: x * 0.1 + y
    return reduce(f1, map(char2num, s[0])) + 0.1 * reduce(f2, map(char2num, s[-1][::-1]))
In [14]:
str2float('1.234')
Out[14]:
1.234
In [15]:
type(str2float('1.234'))
Out[15]:
float
 

filter

 

利用filter() 求素數(埃氏篩法)html5

In [22]:
def odd_iter():
    n = 1
    while True:
        n += 2
        yield n
In [23]:
def prime():
    yield 2
    it = odd_iter()
    while True:
        n = next(it)
        yield n
        it = filter(lambda x: x % n > 0, it)
In [24]:
for i in prime():
    if i < 20:
        print(i, end=' ')
    else:
        break
 
2 3 5 7 9 11 13 15 17 19 
 

返回函數

In [25]:
def lazy_sum(*args):
    def sum():
        ax = 0
        for arg in args:
            ax += arg
        return ax
    return sum
In [26]:
f = lazy_sum(1, 2, 3)
f
Out[26]:
<function __main__.lazy_sum.<locals>.sum>
In [27]:
f()
Out[27]:
6
 

咱們在函數lazy_sum中又定義了函數sum,而且,內部函數sum能夠引用外部函數lazy_sum的參數和局部變量,當lazy_sum返回函數sum時,相關參數和變量都保存在返回的函數中,這種稱爲「閉包(Closure)」的程序結構擁有極大的威力。java

 

閉包

In [29]:
def count():
    fs = []
    for i in range(1, 4):
        def f():
            return i * i
        fs.append(f)
    return fs
In [30]:
f1, f2, f3 = count()
In [32]:
f1(), f2(), f3()
Out[32]:
(9, 9, 9)
 

注:閉包返回的函數並無馬上執行,而是直到調用了f()才執行。python

 

<font color = red>返回函數不要包含任何循環變量,或者後續會發生變化的變量。</font>jquery

 

若是必定要引入循環變量呢?方法是在 count() 中調用 f(),從而綁定循環變量當前值。linux

In [55]:
def count():
    def f(j):
        def g():
            return j * j
        return g
    fs = []
    for i in range(1, 4):
        fs.append(f(i))
    return fs
In [57]:
f1, f2, f3 = count()
In [58]:
f1(), f2(), f3()
Out[58]:
(1, 4, 9)
 

example

In [88]:
def creat_counter():
    def g():
        n = 1
        while True:
            yield n
            n += 1
    it = g()
    def counter():
        return next(it)
    return counter
In [89]:
counterA = creat_counter()
In [90]:
counterA()
Out[90]:
1
In [91]:
counterA()
Out[91]:
2
 

注:creat_counter() 做爲一個計數器函數。android

 

轉換簡單的類爲閉包

In [92]:
class sample:
    def __init__(self,value):
        self.value = value
    def func(self):
        print('n = ', self.value)
    def get_value(self):
        return self.value
    def set_value(self,new_value):
        self.value = new_value
In [93]:
test = sample(1)
test.func()
 
n =  1
In [94]:
def sample(n):
    value = n
    def func():
        print('n = ', value)
    def get_value():
        return value
    def set_value(new_value):
        nonlocal value
        value = new_value
    
    func.get_value = get_value
    func.set_value = set_value
    return func
In [95]:
test = sample(1)
In [96]:
test()
 
n =  1
In [97]:
test.get_value()
Out[97]:
1
In [98]:
test.set_value(5)
test()
 
n =  5
 

注:將 get_value() 和 set_value() 做爲函數 func()的屬性,而後利用閉包返回 func() 函數css3

 

參考:訪問閉包中定義的變量,裏面有進一步內容。

 

裝飾器

In [99]:
def now():
    print('2018/2/20')
In [100]:
now.__name__
Out[100]:
'now'
 

如今但願增長 now() 函數的功能,但不改變他的定義,這種在代碼運行期間動態增長功能的方式,稱之爲裝飾器(Decorator)。

 

本質上,decorator 就是一個返回函數的高階函數。下面,咱們定義一個打印日誌的 decorator。

In [101]:
def log(func):
    print('call {}'.format(func.__name__))
In [102]:
log(now)
 
call now
 

可是這樣就顯式調用了 log(),說白了是兩個函數

In [104]:
def log(func):
    def wrapper(*args, **kw):
        print('call {}'.format(func.__name__))
        return func(*args, **kw)
    return wrapper
In [105]:
now = log(now)
In [106]:
now()
 
call now
2018/2/20
 

以上就實現了打印日誌的 now() 函數,同時會調用 now() 函數實現功能。

In [107]:
@log
def now():
    print('2018/2/20')
In [108]:
now()
 
call now
2018/2/20
 

注:@log 至關於執行語句:

now = log(now)

 

log()就是一個 decorator,它返回 wrapper() 函數,執行 @log 後,wrapper 賦值給 now,再次執行 now() 後,咱們會執行 wrapper() 函數,打印日誌,而後執行原來的 now() 函數。

 

下面進一步。自定義日誌內容

 

大體思想是下面兩條語句:

now = log('text')(now)

now()

寫一個3層嵌套的 decorator:log('text') 返回一個函數,返回至關於上一個例子的 log() 函數。

In [109]:
def log(text):
    def decorator(func):
        def wrapper(*args, **kw):
            print('{0}{1}():'.format(text, func.__name__))
            return func(*args, **kw)
        return wrapper
    return decorator
In [111]:
def now():
    print('2018/2/20')
now = log('execute')(now)
now()
 
execute now():
2018/2/20
 

用法以下:

In [112]:
@log('execute')
def now():
    print('2018/2/20')
now()
 
execute now():
2018/2/20
 

可是有一個問題:

In [113]:
now.__name__
Out[113]:
'wrapper'
 

因爲最後返回的是 wrapper 函數,所以 now 的 __name__ 屬性就是 'wrapper'。

 

完整代碼:

 

例一:

In [114]:
import functools
def log(func):
    @functools.wraps(func)
    def wrapper(*args, **kw):
        print('call {}'.format(func.__name__))
        return func(*args, **kw)
    return wrapper

@log
def now():
    print('2018/2/20')
In [115]:
now()
 
call now
2018/2/20
In [116]:
now.__name__
Out[116]:
'now'
 

例二:

In [117]:
import functools
def log(text):
    def decorator(func):
        @functools.wraps(func)
        def wrapper(*args, **kw):
            print('{0}{1}():'.format(text, func.__name__))
            return func(*args, **kw)
        return wrapper
    return decorator

@log('execute')
def now():
    print('2018/2/20')
In [118]:
now()
 
execute now():
2018/2/20
In [119]:
now.__name__
Out[119]:
'now'
 

最後,寫一個打印函數執行時間的decorator

In [139]:
import time, functools
def meric(func):
    @functools.wraps(func)
    def wrapper(*args, **kw):
        start = time.time()
        result = func(*args, **kw)
        end = time.time()
        print('{} executed in {} ms'.format(func.__name__, end - start))
        return result
    return wrapper
In [140]:
@meric
def test(x, y):
    time.sleep(0.1)
    return x + y
In [141]:
test(1,2)
 
test executed in 0.10009908676147461 ms
Out[141]:
3
 
相關文章
相關標籤/搜索