python函數裝飾器

裝飾器

1、無參裝飾器

1.什麼是裝飾器

器是指工具,而程序中的函數就是具有某一功能的工具,因此裝飾器指的是爲裝飾器對象額外對象添加額外功能,所以定義裝飾器就是定義一個函數,只不過該函數的功能是用來爲其餘函數添加額外功能的。mysql

須要注意的是:sql

  • 裝飾器自己實際上是能夠任意調用的對象
  • 被裝飾的對象也能夠是任意可調用的對象

2.爲何要用裝飾器

若是咱們已經上線 了一個項目,須要修改某一個方法,可是咱們不想修改方法的使用方法,這個時候可使用裝飾器。由於軟件的維護應該遵循開發封閉原則,即軟件于丹上線運行後,軟件的維護對修改原碼是封閉的,對擴展功能指的是開發的。mongodb

裝飾器的實現必須遵照倆大規則:閉包

  1. 不修改被裝飾對象的源代碼。
  2. 不修改被裝飾對象的調用方式。

裝飾器其實就是在遵照以上倆個原則的前提下被裝飾對象添加新功能。app

怎麼使用裝飾器

改變源代碼函數

import time


def index():
    start = time.time()
    print('welcome to index')
    time.sleep(1)
    end = time.time()
    print('運行時間:', end - start)


index()
#輸出:
welcome to index
運行時間: 1.0003464221954346

編寫重複代碼工具

import time


def index():
    print('welcome to index')
    time.sleep(1)


def f2():
    print('welcome to f2')
    time.sleep(1)


start = time.time()
index()
end = time.time()
print('運行時間:', end - start)

start = time.time()
f2()
end = time.time()
print('運行時間:', end - start)


#輸出:
welcome to index
運行時間: 1.0001671314239502
welcome to f2
運行時間: 1.0003397464752197

第一種傳參方式:改變調用方式code

import time


def index():
    print('welcome to index')
    time.sleep(1)


def time_count(func):
    start = time.time()
    func()
    end = time.time()
    print('運行時間:', end - start)


time_count(index)

#輸出:
welcome to index
運行時間: 1.0005521774291992

第二種傳參方式:包給函數-外包對象

import time


def index():
    print('welcome to index')
    time.sleep(1)


def time_count(func):
    def wrapper():
        start = time.time()
        func()
        end = time.time()
        print('運行時間:', end - start)

    return wrapper


index = time_count(index)
index()
 #輸出:
   welcome to index
運行時間: 1.0000648498535156

4.完善裝飾器

上述的裝飾器,最後調用index()的時候,實際上是在調用wrapper(),所以若是原始的index()有返回值的時候,wrapper()函數的返回值應該和index()的返回值相同,也就是說,咱們須要同步原始的index()和wrapper()方法的返回值。

import time


def index():
    print('welcome to index')
    time.sleep(1)
    return 123


def time_count(func):
    def wrapper():
        start = time.time()
        res = func()
        end = time.time()
        print('運行時間:', end - start)

        return res

    return wrapper


index = time_count(index)
res = index()
print(res)
#輸出:
welcome to index
運行時間: 1.0007765293121338
123

若是原始的index()方法須要傳參,那麼咱們以前的裝飾器是沒法實現該功能的,因爲有wrapper()=index(),因此給wrapper()方法傳參便可。

import time


def home(name):
    print(f'welcome {name} to home page')
    time.sleep(1)
    return name


def time_count(func):
    def wrapper(*args, **kwargs):
        start = time.time()
        res = func(*args, **kwargs)
        end = time.time()
        print(f'{func} time is {end - start}')
        return res

    return wrapper


home = time_count(home)
res = home('chen')
print('res:', res)

#輸出:
welcome chen to home page
<function home at 0x000002085D50B0D8> time is 1.0006473064422607
res: chen

5.裝飾器 語法糖

在被裝飾函數正上方,而且是單獨一行寫上@裝飾器名

import time


def time_count(func):
    def wrapper(*args, **kwargs):
        start = time.time()
        res = func(*args, **kwargs)
        end = time.time()
        print(f'{func} time is {end - start}')
        return res

    return wrapper


@time_count
def home(name):
    print(f'welcome {name} to home page')
    time.sleep(1)
    return name


@time_count
def index():
    print('welcome to index')
    time.sleep(1)
    return 123


res = home('egon')
print(f"res:{res}")

#輸出:
welcome egon to home page
<function home at 0x00000203FABAC948> time is 1.0005333423614502
res:egon

6.裝飾器模板

def func():
    def wrapper(*args,**kwargs):
        res = func(*args,**kwargs)
        return res
    return wrapper

2、有參裝飾器

無參裝飾器只套了兩層,有參裝飾器是套三層。

#登錄註冊的裝飾器
import time

current_user = {'username': None}


def login(func):
    def wrapper(*args, **kwargs):
        if current_user['username']:
            res = func(*args, **kwargs)
            return res
        user = input('username:').strip()
        pwd = input('password:').strip()
        if user == 'chen' and pwd == '123':
            print('login successful')
            current_user['username'] = user
            res = func(*args, **kwargs)
            return res
        else:
            print('user or password error')

    return wrapper


@login
def home(name):
    print(f'welcome {name} to home page')
    time.sleep(1)
    return name


@login
def index():
    print('welcome to index')
    time.sleep(1)
    return 123


res = index()
#輸出:
username:chen
password:123
login successful
welcome to index

對於上面的登錄註冊,咱們把用戶登錄成功的信息寫入內存文檔中,可是在工業在,內存信息能夠存在文本中,mysql中,mongodb中,可是咱們只能讓用戶信息來自於file的用戶能夠認證,所以咱們能夠改寫上述的裝飾器。

import time

current_user = {'username': None}


def login(func):
    def wrapper(*args, **kwargs):
        if current_user['username']:
            res = func(*args, **kwargs)
            return res
        user = input('username:').strip()
        pwd = input('password:').strip()
        engine = 'file'
        if engine == 'file':
            print('base of file')
            if user == 'chen' and pwd == '123':
                print('login successful')
                current_user['username'] = user
                res = func(*args, **kwargs)
                return res
            else:
                print('user or password error')
        elif engine == 'mysql':
            print('base of mysql')
        elif engine == 'mongodb':
            print('base of mongodb')
        else:
            print('default')

    return wrapper


@login
def home(name):
    print(f'welcome {name} to home page')
    time.sleep(1)


@login
def index():
    print('welcome to index')
    time.sleep(1)


res = index()

#輸出:
username:chen
password:123
base of file
login successful
welcome to index

1.三層閉包

三層簡單實例

def f1(y):
    def f2():
        x = 1

        def f3():
            print('x:', x)
            print('y:', y)

        return f3

    return f2


f2 = f1(2)
f3 = f2()
f3()
#輸出:
x: 1
y: 2

如今須要修改原來需求,須要判斷用戶動態的獲取用戶密碼的方式,若是是file類型的咱們則讓用戶進行認證,所以咱們須要使用,有參裝飾器。

import time

current_user = {'username': None}


def auth(engine='file'):
    def login(func):
        def wrapper(*args, **kwargs):
            if current_user['username']:
                res = func(*args, **kwargs)
                return res
            user = input('username:').strip()
            pwd = input('password:').strip()
            if engine == 'file':
                print('base of file')
                if user == 'chen' and pwd == '123':
                    print('登錄成功')
                    current_user['username'] = user
                    res = func(*args, **kwargs)
                    return res
                else:
                    print('用戶密碼或者用戶名錯誤')
            elif engine == 'mysql':
                print('base of mysql')
            elif engine == 'mongodb':
                print('base of mongodb')
            else:
                print('please base of file')

        return wrapper

    return login


@auth(engine='mysql')
def home(name):
    print(f'welcome {name} to home page')
    time.sleep(1)


@auth(engine='file')
def index():
    print('welcome to index')
    time.sleep(1)


res = index()
#輸出:
username:chen
password:123
base of file
登錄成功
welcome to index

因爲倆層的裝飾器,參數必須得固定定位func,可是三層的裝飾器解除了這個限制,咱們不單單可使用上述單個參數的三層裝飾器,多個參數的只須要在三層裝飾器中多家幾個參數便可,也就是說裝飾器三層便可,多加無用。

倆個裝飾器的疊加

def outter1(func):  # func = wrapper2
    def wrapper1(*args, **kwargs):  # wrapper是將來要運行的函數
        print('------------')
        res = func(*args, **kwargs)  # func是被裝飾的函數  # wrapper2
        print('------------')
        return res

    return wrapper1


def outter2(func):  # func = index
    def wrapper2(*args, **kwargs):  # wrapper是將來要運行的函數
        print('11111111111111')
        res = func(*args, **kwargs)  # func是被裝飾的函數  # index()
        print('11111111111111')
        return res

    return wrapper2


# @outter1  # index = outter1(index)
# @outter2  # index = outter2(index)  # 先運行最下面的裝飾器
# # index
def index():
    print('index')


# index從新定義的index = outter2(index 真正的index)
index = outter2(index)  # index = wrapper2
# index再一次從新定義的index = outter1(index從新定義的index,即wrapper2)
index = outter1(index)  # index  = wrapper1
index()  # wrapper1()

#輸出:
------------
11111111111111
index
11111111111111
------------

重要,裝飾器的模板

雙層裝飾器

def outter(func):
    def wrapper(*args,**kwargs):
        #加功能
        res = func(*args,**kwargs)
        return res
    return wrapper

三層裝飾器模板

主要是用來給 倆層裝飾器添加參數

def sanceng(engine):
    def outter(func):
        def wrapper(*args, **kwargs):
            # 加功能
            print(engine)
            res = func(*args, **kwargs)
            return res

        return wrapper

    return outter


@sanceng('file')
def shopping():
    print('shopping')


shopping()
#輸出:file
shopping
相關文章
相關標籤/搜索