【python學習筆記】裝飾器、functools.wraps裝飾器和functools.partial偏函數

一、裝飾器python

      不修改被裝飾函數的定義,可是能夠在代碼運行期間動態增長功能的方式,稱之爲「裝飾器「,
      本質上,裝飾器decorator就是一個返回函數的高階函數設計模式

#!/usr/bin/env python
#-*- coding:utf-8 -*-
import math# 導入數學公式模塊
from collections import Iterable# 判斷是否爲可迭代對象
import os# 導入os模塊
import functools# 導入functools.wraps



# Python將一切視爲object的子類,即一切都是對象,固然函數也是一個對象,能夠像變量同樣被傳遞和指向
def foo():
    print "I`m foo"
f = foo
f()
# 函數對象有一個__name__的屬性,能夠獲得函數的名字
print foo.__name__
print f.__name__

# ★不修改被裝飾函數的定義,可是能夠在代碼運行期間動態增長功能的方式,稱之爲「裝飾器「,
# 例子:在函數調用先後自動打印日誌
def logging1(func):# 裝飾器函數logging1
    def wrapper(*args, **kw):# 可變參數和關鍵字參數
        print '%s is running...' % func.__name__
        return func(*args, **kw)
    return wrapper
# logging1是一個裝飾器,它能接收一個函數作參數,並返回一個函數
@logging1
def foo():
    print "I`m foo"
# @符號是裝飾器的語法糖,在定義函數的時候使用,避免再一次賦值操做
foo()# 調用foo()函數
# @logging1放到foo()函數的定義處,調用被裝飾函數foo,不只會執行被裝飾函數foo,還會執行logging1裝飾器
# @logging1至關於執行了foo = logging1(foo)語句

# 分析:因爲logging1() 是一個decorator,返回一個函數,原來的foo()函數仍然存在,只是如今同名的foo變量指向了新的函數,即在logging1()函數中返回的wrapper()函數;因而調用foo()將執行新wrapper()函數
#       wrapper()函數的參數定義是(*args, **kw),所以,wrapper() 函數能夠接受任意參數的調用。在wrapper()函數內,首先打印日誌,再緊接着調用原始函數foo()

# ★有參數的裝飾器,須要編寫一個返回decorator的高階函數
# 裝飾器
def logging2(info):
    def decorator(func):
        def wrapper(*args, **kw):
            print '%s,%s is running...' % (info, func.__name__)
            return func(*args, **kw)
        return wrapper
    return decorator
# 用法
@logging2('Waiting')
def foo():
    print "I`m foo"
# 調用foo()函數
foo()
# 這種三層嵌套結構的裝飾器,執行過程是foo = logging2('Waiting')(foo)
# 分析:首先執行logging2('Waiting'),返回的是decorator函數,再調用返回的decorator函數,參數是now函數,最終返回值是wrapper函數

運行效果:
二、內置的functools.wraps裝飾器app

設計模式之裝飾模式decorator函數

使用Python內置的functools.wraps,在實現以前加上functools的wraps,它能保留原有函數的名稱和docstring
Python除了能支持OOP的decorator外,直接從語法層次支持decorator。Python的decorator能夠用函數實現,也能夠用類實現。設計

# ★函數也是對象,它有__name__ 等屬性,但上面兩個例子通過decorator裝飾以後的函數,它們的__name__ 已經從原來的'now' 變成了'wrapper'
print u'裝飾後函數的名字:',foo.__name__
# 由於返回的那個函數是wrapper() ,因此,結果就是'wapper',如今須要把原始函數的__name__等屬性複製到wrapper() 函數中,不然,有些依賴函數簽名的代碼執行就會出錯。
# 可使用wrapper.__name__ = func.__name__
def logging3(info):
    def decorator(func):
        def wrapper(*args, **kw):
            wrapper.__name__ = func.__name__
            print '%s,%s is running...' % (info, func.__name__)
            return func(*args, **kw)
        return wrapper
    return decorator

@logging3('Waiting')
def foo():
    print "I`m foo"

foo()
print u'裝飾後函數的名字:',foo.__name__

# 無參裝飾器使用functools.wraps
def logging4(func):# 裝飾器函數logging4
    @functools.wraps(func)# 使用functools.wraps保留原來函數的信息
    def wrapper(*args, **kw):# 可變參數和關鍵字參數
        print '%s is running...' % func.__name__
        return func(*args, **kw)
    return wrapper
    
@logging4
def foo():
    print "I`m foo"
    
foo()
print u'無參裝飾器使用functools.wraps:',foo.__name__
# 有參裝飾器使用functools.wraps
def logging5(info):
    def decorator(func):
        @functools.wraps(func)# 使用functools.wraps保留原來函數的信息
        def wrapper(*args, **kw):
            print '%s,%s is running...' % (info, func.__name__)
            return func(*args, **kw)
        return wrapper
    return decorator

@logging5('Waiting')
def foo():
    print "I`m foo"

foo()
print u'有參裝飾器使用functools.wraps:',foo.__name__


# ★編寫一個decorator,能在函數調用的先後打印出'begin call' 和'end call' 的日誌
def logging6(info):
    def decorator(func):
        @functools.wraps(func)# 使用functools.wraps保留原來函數的信息
        def wrapper(*args, **kw):
            print '%s...,begin call %s()' % (info, func.__name__)# 調用前
            result = func(*args, **kw)# 由於不能直接返回,因此須要暫存結果
            print 'end call %s()' % (func.__name__)# 調用後
            return result
        return wrapper
    return decorator
# 組建裝飾器
@logging6('Waiting')
def foo():
    print "I`m foo"

foo()

# ★寫出一個@log的decorator。使它既支持:@log,又支持:@log('execute')
def logging7(info):
    if isinstance(info,str):   # 有參裝飾器
        def decorator(func):
            @functools.wraps(func)
            def wrapper(*args,**kw):
                print u'%s,有參%s is running...' %(info,func.__name__)
                func(*args,**kw)
                print u'有參%s is enddning...' %(func.__name__)
            return wrapper
        return decorator
    else:                      # 無參裝飾器
        @functools.wraps(info) # 此時info就是被裝飾函數
        def wrapper(*args,**kw):
            print u'請等待,無參%s is running...' %(info.__name__)
            info(*args,**kw)
            print u'無參%s is enddning...' %(info.__name__)
        return wrapper
# 組建無參裝飾器
@logging7
def foo():
    print "I`m foo"
    
foo()
# 組建有參裝飾器
@logging7('Waiting')
def foo():
    print "I`m foo"
    
foo()

運行效果:日誌

三、functools.partial偏函數code

偏函數:經過設定參數的默認值,下降函數調用的難度對象

functools模塊除了wraps裝飾器外,還有好多其餘函數,例如偏函數functools.partial,區別於數學意義上的偏函數
 utf-8

#!/usr/bin/env python
#-*- coding:utf-8 -*-
import math# 導入數學公式模塊
from collections import Iterable# 判斷是否爲可迭代對象
import os# 導入os模塊
import functools# 導入functools.wraps

 

# int():默認參數base=10
print u'默認十進制', int('12')      # 必選參數 # 結果:123456
print u'八進制數:', int ('12',base = 8)# 默認參數 # 結果:10
# base表示該字符串轉換爲整數時,採用的進制。而輸出的是十進制
# 而若是轉換大量的八進制字符串,每次都傳入int (x,base = 8)是比較麻煩的,爲此咱們能夠定義int8(x,base = 8)
def int8(x,base = 8):
    return int(x,base)
print u'八進制數:', int8('12')
# 也可使用functools.partial,這樣就不用從新def定義新函數int8()了
int8 = functools.partial(int ,base = 8)
print u'使用functools.partial的八進制數:',int8('15')

# int8函數把參數從新設定默認值爲8 ,但也能夠在函數調用時傳入其餘值。例如:int8('15',base = 16)
# 建立偏函數時,實際上能夠接收函數對象、*args 和**kw 這3個參數
# int8 = functools.partial(int ,base = 8)其實是固定了關鍵字參數的值爲8,等效於kw = {'base' : 8}  int('15', **kw)
# max_ex = functools.partial(max ,10,12)# 會把10,12做爲可變參數自動添加到右邊,max_ex(1,2,3)等效於max(1,2,3,10,12)

運行效果:字符串

★當函數的參數個數太多,使用functools.partial 能夠建立一個新的函數,這個新函數能夠把原函數的某些參數給固定住(也就是設置默認值),返回一個新的函數,從而在調用時更簡單

相關文章
相關標籤/搜索