Python decorator裝飾器

問題:python

  • 定義了一個新函數
  • 想在運行時動態增長功能
  • 又不想改動函數自己的代碼

經過高階段函數返回一個新函數數據庫

def f1(x):
    return x*2

def new_fn(f): #裝飾器函數
    def fn(x):
        print ('call ' + f.__name__ + '()')
        return f(x)
    return fn
#方法1
g1 = new_fn(f1)
print (g1(5))
#方法2
f1 = new_fn(f1) #f1的原始定義函數完全被隱藏了
print (f1(5))

#輸出:
#call f1()
#10

 

裝飾器

python內置的@語法就是爲了簡化裝飾器app

使用 decorator 用Python提供的 @ 語法,這樣能夠避免手動編寫 f = decorate(f) 這樣的代碼。函數

相似上述的方法2post

 

 

裝飾器的做用性能

能夠極大的簡化代碼,避免每一個函數編寫重複性代碼spa

  • 打印日誌:@log
  • 檢測性能:@performance
  • 數據庫事務:@transaction
  • URL路由:@post('/register')

 

python中編寫無參數decorator

舉例:日誌

def log(f):
    def fn(x):
        print 'call ' + f.__name__ + '()...'
        return f(x)
    return fn

@log
def factorial(n):
    return reduce(lambda x,y: x*y, range(1, n+1))
print factorial(10) #結果 call factorial()... 3628800

 

對於不是一個參數的函數,須要修改:code

要讓 @log 自適應任何參數定義的函數,能夠利用Python的 *args 和 **kw,保證任意個數的參數老是能正常調用:orm

def log(f):
    def fn(*args, **kw):
        print 'call ' + f.__name__ + '()...'
        return f(*args, **kw)
    return fn
@log
def add(x, y):
    return x + y
print add(1, 2)

 

 

python中編寫帶參數decorator

帶參數的log函數首先返回一個decorator函數,再讓這個decorator函數接收my_func並返回新函數:

def log(prefix):
    def log_decorator(f):#標準decorator
        def wrapper(*args, **kw):
            print '[%s] %s()...' % (prefix, f.__name__)
            return f(*args, **kw)
        return wrapper
    return log_decorator #返回log

@log('DEBUG')
def test():
    pass
print test()

 

#執行結果
[DEBUG] test()...
None

 

 

python中完善decorator

@decorator能夠動態實現函數功能的增長,可是,通過@decorator「改造」後的函數,和原函數相比,有所不一樣:

def f1(x):
    pass
print f1.__name__

#輸出
f1

 

def log(f):
    def wrapper(*args, **kw):
        print 'call...'
        return f(*args, **kw)
    return wrapper
@log
def f2(x):
    pass
print f2.__name__

#輸出
wrapper

 

因爲decorator返回的新函數函數名已經不是'f2',而是@log內部定義的'wrapper'。這對於那些依賴函數名的代碼就會失效。decorator還改變了函數的__doc__等其它屬性。若是要讓調用者看不出一個函數通過了@decorator的「改造」,就須要把原函數的一些屬性複製到新函數中:

方法1:

def log(f):
    def wrapper(*args, **kw):
        print 'call...'
        return f(*args, **kw)
    wrapper.__name__ = f.__name__
    wrapper.__doc__ = f.__doc__
    return wrapper

 

方法2:Python內置的functools能夠用來自動化完成這個「複製」的任務(推薦使用)

import functools
def log(f):
    @functools.wraps(f) #
    def wrapper(*args, **kw):
        print 'call...'
        return f(*args, **kw)
    return wrapper

最後須要指出,因爲咱們把原函數簽名改爲了(*args, **kw),所以,沒法得到原函數的原始參數信息。

 

python中偏函數

當一個函數有不少參數時,調用者就須要提供多個參數。若是減小參數個數,就能夠簡化調用者的負擔。

functools.partial就是幫助咱們建立一個偏函數的,不須要咱們本身定義int2(),能夠直接使用下面的代碼建立一個新的函數int2:

>>> import functools
>>> int2 = functools.partial(int, base=2)
>>> int2('1000000')
64
>>> int2('1010101')
85

 

舉例:在sorted這個高階函數中傳入自定義排序函數就能夠實現忽略大小寫排序。

import functools
sorted_ignore_case
= functools.partial(sorted,key=str.lower) print sorted_ignore_case(['bob', 'about', 'Zoo', 'Credit'])
相關文章
相關標籤/搜索