1. 程式人生 > >Python學習筆記__4.4章 裝飾器(添加額外功能)

Python學習筆記__4.4章 裝飾器(添加額外功能)

編程語言 Python

# 這是學習廖雪峰老師python教程的學習筆記

1、概覽

裝飾器可以幫助我們為已經存在的對象添加額外的功能

裝飾器本質上是一個Python函數,它可以讓其他函數在不需要做任何代碼變動的前提下增加額外功能,裝飾器的返回值也是一個函數對象。

裝飾器經常用於有切面需求的場景,比如:插入日誌、性能測試、事物處理、緩存、權限校驗等場景。

1.1、為now函數 加一行日誌

# 定義now函數

def now():

print('2018-5-8')

# 編輯decorator

def log(func): # 接受函數名

def wrapper(*args,**kw): #

接收傳入函數的參數

print('the function name is %s():' % func.__name__) # 打印函數的__name__屬性,註意__name__ 是兩條_(下劃線)

return func(*args,**kw) # 返回這個函數

return wrapper

# 將decorator 加到 now函數

@log

def now():

print('2018-5-8')

把@log放到now()函數的定義處,相當於執行了 now=log(now)。過程為【wapper=log(now),weapper返回now()】

由於log()是一個decorator,返回一個函數,所以,原來的now()函數仍然存在,只是現在同名的now變量指向了新的函數,於是調用now()將執行新函數,即在log()函數中返回的wrapper()函數。

1.2、三層嵌套的decorator

如果decorator本身需要傳入參數,那就需要編寫一個返回decorator的高階函數。比如,要自定義log的文本

def log(text):

def decorator(func):

def wrapper(*args, **kw):

print('%s %s():' % (text, func.__name__))

return func(*args, **kw)

return wrapper

return decorator

# 添加到now() 函數

@log('execute')

def now():

print('2015-3-25')

# 效果

>>> now()

execute now():

2015-3-25

三層嵌套的執行效果:now = log('execute')(now)

1.3、原始函數的屬性復制

從上面的例子可以看出,now()函數 最終是由wrapper 函數返回的。 此時now() 函數的__name__屬性已經變為了 'wrapper'

>>> now.__name__

'wrapper'

但我們的本意只是,給now() 函數加額外功能,並不想改變它本身的屬性,此時,就需要把now() 函數的屬性復制給 wrapper() 函數

不需要編寫wrapper.__name__ = func.__name__這樣的代碼,Python內置的functools.wraps就是幹這個事的

functools.wrapswraps 本身也是一個裝飾器,它能把原函數的元信息拷貝到裝飾器函數中。這使得裝飾器函數也有和原函數一樣的元信息

# 順便復制屬性的decorator

import functools

def log(func):

@functools.wraps(func) #這個裝飾器始終在 wrapper 函數上面

def wrapper(*args, **kw):

print('call %s():' % func.__name__)

return func(*args, **kw)

return wrapper

2、例子

1、請設計一個decorator,它可作用於任何函數上,並打印該函數的執行時間:

# -*- coding: utf-8 -*-
import time, functools

def metric(fn):

@functools.wraps(fn) # 復制原函數信息 wrapper

def wrapper(*args, **kw):

begin=time.time()

res=fn(*args, **kw) # fn 函數的返回值 賦給 res

end=time.time()

print("%s executed in %s ms" % (fn.__name__,(end-start)*1000)) # 打印執行時間 ms

return res

return wrapper

2、在函數的執行前後打印標記

def log(func):

def call(*args,**kw):

print('begin call')

out=func(*args,**kw) # 要點在於 將函數的返回值 賦值給一個變量,最後return 變量

print('end call')

return out

return call


Python學習筆記__4.4章 裝飾器(添加額外功能)