這一次主要是學習了一下Python3函數式編程思想,介紹了3個代表性高階函數:map(), reduce(), filter()。像 sorted() 其實也是高階函數,可以接受函數作為參數。這篇學習筆記中編寫了大量高階函數,同時介紹了Python中的閉包,裝飾器。這些思想和方法很美妙,我受益匪淺。當然這些都需要進一步運用和學習。
運行環境:Python3.6 + Jupyter notebook
函數式編程¶
函數作為參數¶
def add(x, y, f):
return f(x) + f(y)
add(-1, 1, abs)
map¶
map()作為高階函數,它將運算規則抽象了。
r = map(lambda x: x*x, [1, 2, 3, 4])
list(r)
reduce¶
from functools import reduce
def prod(List,f):
return reduce(f, List)
def f(x, y):
return x * y
prod([1,2,3], f)
str2int¶
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]
str2int('123')
type(str2int('123'))
str2float¶
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]))
str2float('1.234')
type(str2float('1.234'))
filter¶
利用filter() 求素數(埃氏篩法)
def odd_iter():
n = 1
while True:
n += 2
yield n
def prime():
yield 2
it = odd_iter()
while True:
n = next(it)
yield n
it = filter(lambda x: x % n > 0, it)
for i in prime():
if i < 20:
print(i, end=' ')
else:
break
返回函數¶
def lazy_sum(*args):
def sum():
ax = 0
for arg in args:
ax += arg
return ax
return sum
f = lazy_sum(1, 2, 3)
f
f()
我們在函數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(), f2(), f3()
注:閉包返回的函數並沒有立刻執行,而是直到調用了f()才執行。
<font color = red>返回函數不要包含任何循環變量,或者后續會發生變化的變量。</font>
如果一定要引入循環變量呢?方法是在 count() 中調用 f(),從而綁定循環變量當前值。
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
f1, f2, f3 = count()
f1(), f2(), f3()
example¶
def creat_counter():
def g():
n = 1
while True:
yield n
n += 1
it = g()
def counter():
return next(it)
return counter
counterA = creat_counter()
counterA()
counterA()
注:creat_counter() 作為一個計數器函數。
轉換簡單的類為閉包¶
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
test = sample(1)
test.func()
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
test = sample(1)
test()
test.get_value()
test.set_value(5)
test()
注:將 get_value() 和 set_value() 作為函數 func()的屬性,然后利用閉包返回 func() 函數
參考:訪問閉包中定義的變量,里面有進一步內容。
裝飾器¶
def now():
print('2018/2/20')
now.__name__
現在希望增加 now() 函數的功能,但不改變他的定義,這種在代碼運行期間動態增加功能的方式,稱之為裝飾器(Decorator)。
本質上,decorator 就是一個返回函數的高階函數。下面,我們定義一個打印日志的 decorator。
def log(func):
print('call {}'.format(func.__name__))
log(now)
但是這樣就顯式調用了 log(),說白了是兩個函數
def log(func):
def wrapper(*args, **kw):
print('call {}'.format(func.__name__))
return func(*args, **kw)
return wrapper
now = log(now)
now()
以上就實現了打印日志的 now() 函數,同時會調用 now() 函數實現功能。
@log
def now():
print('2018/2/20')
now()
注:@log 相當於執行語句:
now = log(now)
log()就是一個 decorator,它返回 wrapper() 函數,執行 @log 后,wrapper 賦值給 now,再次執行 now() 后,我們會執行 wrapper() 函數,打印日志,然后執行原來的 now() 函數。
下面進一步。自定義日志內容
大致思想是下面兩條語句:
now = log('text')(now)
now()
寫一個3層嵌套的 decorator:log('text') 返回一個函數,返回相當於上一個例子的 log() 函數。
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
def now():
print('2018/2/20')
now = log('execute')(now)
now()
用法如下:
@log('execute')
def now():
print('2018/2/20')
now()
但是有一個問題:
now.__name__
由於最后返回的是 wrapper 函數,因此 now 的 __name__ 屬性就是 'wrapper'。
完整代碼:
例一:
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')
now()
now.__name__
例二:
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')
now()
now.__name__
最后,寫一個打印函數執行時間的decorator
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
@meric
def test(x, y):
time.sleep(0.1)
return x + y
test(1,2)