python裝飾器介紹

xie仗劍天涯發表於2017-08-07
"""
裝飾器
定義:本質是函式(器:就是函式的意思),功能:裝飾其他函式,就是為其他函式新增附加功能

原則:
1. 不能修改被裝飾的函式的原始碼
2. 不能修改被裝飾的函式的呼叫方式

實現裝飾器知識儲備:
1. 函式即"變數"
2. 高階函式
a: 把一個函式名當做實參傳給另一個函式(在不修改被裝飾函式原始碼的情況下為其新增功能)
b: 返回值中包含函式名 (不修改函式的呼叫方式)
3. 巢狀函式

高階函式 + 巢狀函式 --> 裝飾器

參考連結:http://egon09.blog.51cto.com/9161406/1836763
"""


# 將test1 和 test2 函式新增logging 功能 (未使用使用函式呼叫)
'''
def logger():
    print ("logging")

def test1():
    pass
    logger()

def test2():
    pass
    logger()

test1()
test2()
'''




# 裝飾器示範_1
'''
import time

def timmer(func):
    def warpper(*args,**kwargs):
        start_time = time.time()
        func()
        stop_time = time.time()
        print ("the func run time is %s"%(stop_time - start_time))
    return warpper

@timmer
def test1():
    time.sleep(3)
    print ("this is test1")

test1()
# this is test1
# the func run time is 3.019197702407837

'''



# 函式即"變數"
# 新概念:函式在記憶體中以變數形式存放
# 匿名函式沒有函式名,當執行後記憶體會被立馬回收(def 定義的函式,不會被記憶體回收,當程式結束後記憶體地址才會釋放)
'''
# eg_v1  
def bar():  # 正常執行
    print ("in the bar")
def foo():
    print ("in the foo")
    bar()
foo()
# in the foo
# in the bar


# eg_v2   # 正常執行
def foo():
    print ("in the foo")
    bar()
def bar():
    print ("in the bar")
foo()
# in the foo
# in the bar


# eg_v3  # 丟擲異常
def foo():
    print ("in the foo")
    bar()
foo()
def bar():
    print ("in the bar")
# Traceback (most recent call last):
# in the foo
#   File "E:/pycharm-project/PYTHON自動化課程/第二模組/課堂筆記/裝飾器_v1.py", line 92, in <module>
#     foo()
#   File "E:/pycharm-project/PYTHON自動化課程/第二模組/課堂筆記/裝飾器_v1.py", line 91, in foo
#     bar()
# NameError: name 'bar' is not defined
'''


# 高階函式
# eg_v1
'''
def bar():
    print ("in the bar")

def test1(func):
    print (func)
# test1(bar)
# # <function bar at 0x0000024DE92E3E18> bar函式的記憶體地址
'''

# eg_v2
'''
def bar():
    print ("in the bar")

def test1(func):
    print (func)
    func()     # 2 呼叫func函式時,也執行了bar函式
test1(bar)     # 1 將bar函式傳遞給test1
# <function bar at 0x00000216F49D3E18>
# in the bar
'''


# eg_v3  # 裝飾器第一步(沒有修改原始碼,但改變了呼叫方式)(test1為裝飾函式,bar函式為被裝飾函式)
'''
import time
def bar():
    time.sleep(2)
    print ("in the bar")

def test1(func):
    start_time = time.time()
    func()
    stop_time = time.time()
    print ("the func run time is %s"%(stop_time - start_time))

test1(bar)
# in the bar
# the func run time is 2.000016689300537
'''

# eg_v4

'''
import time
def bar():
    time.sleep(2)
    print("in the bar")

def test2(func):
    print (func)
    return func
# t = test2(bar)  # 將test2(bar) 賦值給t,t() == 呼叫test2函式,改變了呼叫方式
# t()
# # <function bar at 0x00000172B2CF3E18>
# # in the bar
bar = test2(bar)    # # 將test2(bar) 賦值給bar,bar() == 呼叫test2函式,未改變了呼叫方式
bar()
# <function bar at 0x0000016C6E663E18>
# in the bar
'''

  



# 巢狀函式
# eg_v1
'''
def foo():
    print ("in the foo")
    def bar():
        print ("in the bar")

    bar()
foo()
'''

# eg_v2 # 區域性作用域和全域性作用域的訪問順序
'''
x = 0
def grandpa():
    x = 1
    def dad():
        x = 2
        def son():
            x = 3
            print (x)
        son()
    dad()
grandpa()
# 3
'''

  





# 寫一個簡單的裝飾器
# eg_v1 不使用語法糖
'''
import time

def timer(func):   # timer(test1) --> func= test1
    def deco():
        start_time = time.time()
        func()   # run test1()
        stop_time = time.time()
        print ("the func run time %s:"%(stop_time - start_time))
    return deco

def test1():
    time.sleep(3)
    print ("in the test1")

def test2():
    time.sleep(3)
    print ("in the test2")
test1 = (timer(test1))  # 賦值必須與被裝飾的函式同名
test1()
# in the test1
# the func run time 3.0068588256835938:
'''


# eg_v2 使用語法糖,不帶引數
'''
import time

def timer(func):   # timer(test1) --> func= test1
    def deco():
        start_time = time.time()
        func()   # run test1()
        stop_time = time.time()
        print ("the func run time %s:"%(stop_time - start_time))
    return deco

@timer
def test1():
    time.sleep(3)
    print ("in the test1")
@timer
def test2():
    time.sleep(3)
    print ("in the test2")

test1()
test2()
# in the test1
# the func run time 3.000098705291748:
# in the test2
# the func run time 3.0001633167266846:
'''



# eg_v2 使用語法糖,被裝飾函式帶引數
'''
import time

def timer(func):   # timer(test1) --> func= test1
    def deco(*args,**kwargs):
        start_time = time.time()
        func(*args,**kwargs)   # run test1()
        stop_time = time.time()
        print ("the func run time %s:"%(stop_time - start_time))
    return deco

@timer
def test1():
    time.sleep(3)
    print ("in the test1")
@timer
def test2(name):   # test2 = timer(test2) == deco test2(name) == deco(name)
    time.sleep(3)
    print ("in the test2",name)

test1()
test2("xieshengsen")
# in the test1
# the func run time 3.0004942417144775:
# in the test2 xieshengsen
# the func run time 3.0003535747528076:
'''



# eg_v3
# 一個簡單的登陸頁面認證過程(裝飾器)(裝飾函式帶引數)
user = "aaaaaa"
pawd = "123456"


def auth(auth_type):
    print ("auth func:",auth_type)
    
    def outer_wrapper(func):
        def wrapper(*args,**kwargs):
            print ("wrapper func args:",*args,**kwargs)

            if auth_type == "local":
                username = input("username:").strip()
                password = input("password:").strip()

                if username == user and password == pawd:
                    print ("login....")
                    res = func(*args,**kwargs)  # from home
                    print ("-*-*-*-*-after auth-*-*-*-*-")
                    return res
                else:
                    print ("invalid...")
            elif auth_type == "ldap":
                input("ldap")
                print ("use ladp auth...")
        return wrapper
    return outer_wrapper


def index():
    print ("welcome to index page.")
    

@auth(auth_type="local")
def home():
    print("welcome to home age.")


@auth(auth_type="ldap")
def bbs():
    print("welcome to bbs age.")

index()
home()
bbs()

  

另 一些關於裝飾器介紹的例項

來自:http://blog.csdn.net/yhy1271927580/article/details/72758577

"""
被裝飾的物件為函式,且不帶引數
簡要說明: @timeit裝飾器對sleep函式進行了裝飾,這是一個裝飾器不帶引數的裝飾器,當sleep函式呼叫的時候,呼叫的並不是我們看到的原始的sleep函式,而是裝飾過後的sleep函式。這個裝飾的過程會發生在呼叫sleep函式之前發生。
裝飾的過程:原生的sleep函式作為引數傳遞到timeit裝飾器函式的fn引數,通過@wraps這個裝飾器將fn的屬性賦值給底下需要返回的wrap函式,最後返回wrap函式,由此可間,wrap就是裝飾過後的sleep函式了。那麼在呼叫新sleep函式的時候,就是呼叫wrap函式,sleep函式的引數2,被wrap函式的*args、**kwargs這兩個可變引數接收。
整個裝飾的目的就是將原生的sleep函式,擴充了一個print(nowTime() - start)的過程。
"""
from time import sleep as sleeping
from time import time as nowtime
from functools import wraps


def timeit(fn):
    @wraps(fn)
    def wrap(*args,**kwargs):
        start = nowtime()
        ret = fn(*args,**kwargs)
        print (nowtime() - start)
        return ret
    return wrap

@timeit
def sleep(n):
    sleeping(n)
    print("sleep time {}s".format(n))
    return n

sleep(2)
print("%s func" % sleep.__name__)
# sleep time 2s
# 2.0028135776519775
# sleep func

  

"""
被裝飾的物件為函式,且帶引數
簡要說明: @timeit裝飾器對sleep函式進行了裝飾,這是一個裝飾器是帶引數的裝飾器.
這個裝飾器由於需要傳遞引數,因此需要兩層裝飾,第一層是接受傳遞的參宿,第二層是接收傳遞進來的需要裝飾的函式當sleep函式呼叫的時候,呼叫的並不是我們看到的原始的sleep函式,而是裝飾過後的sleep函式。
裝飾的過程:裝飾器第一次接收cpu_time引數,然後返回一個dec裝飾器,而這個dec裝飾器會被再次呼叫,傳入引數是原生的sleep函式,原生的sleep函式作為引數傳遞到dec裝飾器函式的fn引數,通過@wraps這個裝飾器將fn的屬性賦值給底下需要返回的wrap函式,最後返回wrap函式
在呼叫新sleep函式的時候,就是呼叫wrap函式,sleep函式的引數2,被wrap函式的*args、**kwargs這兩個可變引數接收。整個裝飾的目的就是將原生的sleep函式,擴充了一個time_func = time.clock if cpu_time else time.time和print(nowTime() - start)的過程
"""
from functools import wraps
import time


def timeit(cpu_time=False):
    print("timeit")

    def dec(fn):
        @wraps(fn)
        def wrap(*args,**kwargs):
            start = time.time()
            ret = fn (*args,**kwargs)
            print(time.time() - start)
            return ret
        return wrap
    return dec


@timeit(False)
def sleep(n):
    time.sleep(n)
    print ("time sleep")
    return n

sleep(2)
print("%s func" % sleep.__name__)
# timeit
# time sleep
# 2.009625196456909
# sleep func

  

 



 

 

 



相關文章