1. 程式人生 > >python學習筆記,返回函式,匿名函式,裝飾器

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)