python學習筆記,返回函式,匿名函式,裝飾器
宣告:
想學習的朋友可以直接看http://www.liaoxuefeng.com/wiki/001374738125095c955c1e6d8bb493182103fac9270762a000
廖雪峰老師的網站學習,這些只是個人筆記和整理。
返回函式:
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 sum at 0x10452f668>
呼叫函式f
時,才真正計算求和的結果:
>>> f()
25
在函式lazy_sum
中又定義了函式sum
,並且,內部函式sum
可以引用外部函式lazy_sum
的引數和區域性變數,當lazy_sum
返回函式sum
時,相關引數和變數都儲存在返回的函式中,這種稱為“閉包(Closure)”的程式結構擁有極大的威力。
注意:當我們呼叫lazy_sum()
時,每次呼叫都會返回一個新的函式,即使傳入相同的引數:
>>> f1 = lazy_sum(1, 3 , 5, 7, 9)
>>> f2 = lazy_sum(1, 3, 5, 7, 9)
>>> f1==f2
False
f1()
和f2()
的呼叫結果互不影響。
返回閉包時牢記的一點就是:返回函式不要引用任何迴圈變數,或者後續會發生變化的變數。
如果一定要引用迴圈變數怎麼辦?方法是再建立一個函式,用該函式的引數繫結迴圈變數當前的值,無論該迴圈變數後續如何更改,已繫結到函式引數的值不變:
>>> def count():
... fs = []
... for i in range(1, 4):
... def f(j) :
... def g():
... return j*j
... return g
... fs.append(f(i))
... return fs
...
>>> f1, f2, f3 = count()
>>> f1()
1
>>> f2()
4
>>> f3()
9
缺點是程式碼較長,可利用lambda函式縮短程式碼。
匿名函式:
以map()
函式為例,計算f(x)=x2時,除了定義一個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
,返回值就是該表示式的結果。用匿名函式有個好處,因為函式沒有名字,不必擔心函式名衝突。此外,匿名函式也是一個函式物件,也可以把匿名函式賦值給一個變數,再利用變數來呼叫該函式:
>>> f = lambda x: x * x
>>> f
<function <lambda> at 0x10453d7d0>
>>> f(5)
25
同樣,也可以把匿名函式作為返回值返回,比如:
def build(x, y):
return lambda: x * x + y * y
裝飾器:
函式物件有一個__name__
屬性,可以拿到函式的名字:
>>> now.__name__
'now'
>>> f.__name__
'now'
我們要增強now()
函式的功能,比如,在函式呼叫前後自動列印日誌,但又不希望修改now()
函式的定義,這種在程式碼執行期間動態增加功能的方式,稱之為“裝飾器”(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 '2013-12-25'
呼叫now()
函式,不僅會執行now()
函式本身,還會在執行now()
函式前列印一行日誌:
>>> now()
call now():
2013-12-25
把@log
放到now()
函式的定義處,相當於執行了語句:
now = log(now)
不需要編寫wrapper.__name__ = func.__name__
這樣的程式碼,Python內建的functools.wraps
就是幹這個事的,所以,一個完整的decorator的寫法如下:
import functools
def log(func):
@functools.wraps(func)
def wrapper(*args, **kw):
print 'call %s():' % func.__name__
return func(*args, **kw)
return wrapper
或者針對帶引數的decorator:
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
import functools
是匯入functools
模組。記住在定義wrapper()
的前面加上@functools.wraps(func)
。