1. 程式人生 > >Python--裝飾器

Python--裝飾器

sign 註意 time 裝飾 app ron light wrap 操作

裝飾器的應用場景,較為經典的有插入日誌、性能測試、事務處理等。 上代碼:

def foo():
    print(‘foo‘)

此段代碼,如果我們想在不改變原有功能的基礎上添加新功能,可以這樣做:

def foo():
    print(‘foo‘)

def wrapper(func):
    def inner():
        print(‘before‘)
        func()
        print(‘after‘)
    return inner


foo=wrapper(foo)
foo()

打印結果:
before
foo
after

Python提供了一個語法糖來降低字符輸入量。

def wrapper(func):
    def inner():
        print(‘before‘)
        func()
        print(‘after‘)
    return inner

@wrapper    #wrapper=wrapper(foo)
def foo():
    print(‘foo‘)

foo()

打印結果:
before
foo
after

讓我們來關註一下@wrapper的寫法,在foo函數定義上加上這一行與另外寫foo = wrapper(foo)完全等價,@並沒有另外的魔力。

除了字符輸入少了一些,還有一個額外的好處:這樣看上去更有裝飾器的感覺。

這樣還不能滿足我們的需求,我們想給foo函數傳參數的話,如何處理?

上代碼:

def wrapper(func):
    def inner(sth):
        print(‘before‘)
        func(sth)
        print(‘after‘)
    return inner

@wrapper    #wrapper=wrapper(foo)
def foo(sth):
    print(sth)

foo(‘apple‘)

打印結果:
before
apple
after

這個是傳一個參數的情況,如果我們想傳的參數很多,除了多個位置參數之外,還想要傳關鍵字參數,這個時候怎麽辦呢?

func函數通過*args,**kwargs接收實參即可完美實現!

def wrapper(func):
    def inner(*args,**kwargs):
        print(‘before‘)
        func(*args,**kwargs)
        print(‘after‘)
    return inner

@wrapper    #wrapper=wrapper(foo)
def foo(sth1,sth2,sth3,sth4):
    print(sth1,sth2,sth3,sth4)

foo(‘apple‘,‘orange‘,‘banana‘,‘lemon‘)

打印結果:
before
apple orange banana lemon
after

進一步,我們的foo函數有時候需要使用到返回值,然後如何處理呢?

import time

def wrapper(func):
    def inner(*args,**kwargs):
        print(‘before‘)
        ret = func(*args,**kwargs)
        print(‘after‘)
        print(ret)
        return ret
    return inner


@wrapper    #foo=wrapper(foo)
def foo(arg):
    print(arg)
    return 111

res=foo(‘apple‘)
time.sleep(1)
print(res)

打印結果:
before
apple
after
111
111(1秒後打印)

此外,functools模塊提供有裝飾器:wraps(wrappedassignedupdated)。

函數是有幾個特殊屬性比如函數名,在被裝飾後,上例中的函數名foo會變成包裝函數的名字inner,我們可以看下例:

def wrapper(func):

    def inner(*args, **kwargs):
        print("before...")
        ret = func(*args, **kwargs)
        print("after/...")
        return ret
    return inner


@wrapper   #  foo = wrapper(foo) -->inner
def foo(sth):
    print(sth)
    return "aaa"

print(foo.__name__)

打印結果:
inner

這時候wraps就體現了它的作用:它能將裝飾過的函數的特殊屬性保留。

import functools

def wrapper(func):

    @functools.wraps(func)
    def inner(*args, **kwargs):
        print("before...")
        ret = func(*args, **kwargs)
        print("after/...")
        return ret
    return inner


@wrapper   #  foo = wrapper(foo) -->inner
def foo(sth):
    print(sth)
    return "aaa"

print(foo.__name__)

打印結果:
foo

關於裝飾器,一些有意思的小練習可以拿來做。

一:編寫函數,(函數執行的時間是隨機的)
二:編寫裝飾器,為函數加上統計時間的功能
三:編寫裝飾器,為函數加上認證的功能

四:編寫裝飾器,為多個函數加上認證的功能(用戶的賬號密碼來源於文件),要求登錄成功一次,後續的函數都無需再輸入用戶名和密碼
	註意:從文件中讀出字符串形式的字典,可以用eval(‘{"name":"egon","password":"123"}‘)轉成字典格式

五:編寫下載網頁內容的函數,要求功能是:用戶傳入一個url,函數返回下載頁面的結果

六:為題目五編寫裝飾器,實現緩存網頁內容的功能:
	具體:實現下載的頁面存放於文件中,如果文件內有值(文件大小不為0),就優先從文件中讀取網頁內容,否則,就去下載,然後存到文件中

七:還記得我們用函數對象的概念,制作一個函數字典的操作嗎,來來來,我們有更高大上的做法,在文件開頭聲明一個空字典,然後在每個函數前加上裝飾器,完成自動添加到字典的操作

Python--裝飾器