1. 程式人生 > >【24】Python裝飾器筆記

【24】Python裝飾器筆記

put 訪問 img elif 分享 sse pass 源代碼 local

裝飾器
定義:本職是函數,(裝飾其他函數)就是為其他函數添加附加功能。
原則:
1.不能修改被裝飾的函數的源代碼
2.不能修改被裝飾函數的調用的方式

先來一個直觀感受
import time
def timmer(func): ##
    def warpper(*args,**kwargs):
        start_time=time.time()
        func()  ##run  test1()
        stop_time=time.time()
        print("the func time is %s"%(start_time-stop_time))
    return warpper

@timmer  #@加函數名,即可調用函數
def test1():  ###源代碼
    time.sleep(3)
    print("in the test1")

test1()

實現裝飾器的知識儲備:
1.函數即"變量"
2.高階函數
3.嵌套函數(函數裏面def聲明一個函數叫嵌套函數,調用函數不叫嵌套)

下面進行一波實驗:如下三種結果(驗證1.函數即“變量”)
A.輸出報錯沒有bar函數
def foo():
    print("in the foo")
    bar()
foo()

B.成功
def bar():
    print("in the bar")
def foo():
    print("in the foo")
    bar()
foo()

C.輸出報錯,沒找到定義的bar函數
def foo():
    print("in the foo")
    bar()
foo()
def bar():
    print("in the bar")

在將函數即“變量”的時候,先講下python內存存儲變量的機制。
當設定一個變量x=1時,內存看成一個大house,這時候大house會騰出一個房間將變量值1放入這個房間,並貼上x的門牌號。
如果是x=1,y=x,那就是變量值1這個房間將會有兩個門牌號,即x&y。如圖所示:
而在python內存的回收機制中,當你的房間沒有門牌號時,它就會默認回收。這樣可以節約空間。而當兩個或多個變量值都一樣時,它不會給你創建N個房間,而是同一個房間內貼上N個門牌號。
技術分享圖片
凡是也有例外,有人就會說了,那就不會有沒有變量名的變量了嗎?這個在python中,還真有即匿名函數lambda。當lambda x:x*3,結果是占用的內存地址。

這裏的
print(“in the poo”)
bar()
就是函數體相當於變量存放在內存中。poo()就是門牌號,當執行A時之所以會報錯,就是因為沒有找到bar的函數體。而C雖然定義了bar()但是定義的位置不對,函數都是從上往下讀取,當執行poo()之前並沒有定義,所以會報錯找不到bar
技術分享圖片

2.高階函數(又分以下兩種)

a:把一個函數名當做實參傳遞給另外一個函數
import time
def bar():
    time.sleep(3)
    print("in the bar")

def test1(func):  ##根據test1(bar)的調用可以看出,這裏的func = bar。這就是把bar函數名傳遞給func當test1的實參
    start_time=time.time()
    func() #run bar()
    stop_time=time.time()
    print("the func run time is %s"%(stop_time-start_time))

test1(bar)

b:返回值中包含函數名
import time
def bar():
    time.sleep(3)
    print("in the bar")
def test2(func):
    print(func)
    return func  #f返回bar的內存值

print(test2(bar))
t=test2(bar) #test2執行結果賦值給了t,相當於把bar(賦值給了t,當使用t()時,就是在執行bar函數
t()  #run bar()

3.嵌套函數 (在一個函數體內用def聲明一個函數叫嵌套。調用不叫嵌套)

#局部即變量
def foo():
    def bar():
        print("in the bar")
    bar() ##想要輸出print結果就需要一層層的調用
foo()

#局部作用和全局作用域的訪問順序
x=0
def boo():
    def daa():
        x=2
        def son():
            x=3
            print(x)
        son()
    daa()
boo()
猜測下這裏的輸出結果是:

下面寫個簡單的裝飾器。(高階函數+嵌套函數=》裝飾器)
不設定函數實參

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

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

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

當設定test實參時,可以這樣寫。deco() func()裏面都直接*args,***kwargs不限量。如果具體某一個參數,那就可以修改為具體的。

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

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

@timer #非固定參數name
def test2(name):
    time.sleep(3)
    print("in the test2",name)

test1=timer(test1)
test1() ##-->deco
test2("alex")

模擬遠端登錄與本地登錄試驗:

user,passwd="alex","abc"
def auth(auth_type):
    print("auth>>>",auth_type)
    def outer_wrapper(func):
        def wrapper(*args,**kwargs):
            print("*args,**kwargs",*args,**kwargs)
            if auth_type=="local":
                username=input("Input user: ").strip()
                password=input("Input password: ").strip()
                if username==user and password==passwd:
                    print("\033[32;1mUser has passed authentication\033[0m")
                    res=func(*args,**kwargs)
                    return res
                else:
                    exit("\033[31;1mIminvalid username or password\033[0m")
            elif auth_type=="ldap":
                print("不會")
        return wrapper
    return outer_wrapper
def index():
    print("welcome to index page")
@auth(auth_type="local")  ##本地驗證登錄
def home():
    print("welcome to home page")
    return  "from home"
@auth(auth_type="ldap")  ##遠端驗證登錄
def dds():
    print("welcome to dds page")

index()
print(home())
dds()

【24】Python裝飾器筆記