裝飾器

軟件開發原則之一

開放封閉原則:不改變調用方式與源代碼的基礎上增長新功能
  封閉:不能修改被裝飾對象(函數)的源代碼、調用方式
  開放:增長、擴展新的功能效果

裝飾器:就是閉包的一種應用
    把要被裝飾的函數做爲外層函數的參數經過閉包操做後返回一個替代版函數
    被裝飾的函數:fn
    外層函數:outer(func)  outer(fn) => func = fn
    替代版函數: return inner: 原功能+新功能
def fn():
    print("原有功能")

# 裝飾器
def outer(tag):
    def inner():
        tag()
        print(新增功能")
    return inner
fn = outer(fn)              
              
fn()

開放封閉原則的實現
def vase():
    print('插花')    #原功能:花瓶的做用
vase()



def vase():
    print('插花')
    print('繪畫:進行觀賞')   # 1)增長觀賞功能:不知足開放封閉原則,修改了源代碼
vase()



def wrap(fn):
    vase()
    print('繪畫:進行觀賞')  # 2)增長觀賞功能:不知足開放封閉原則,修改了調用方式
wrap(vase)


def fn():
    vase()
    print('繪畫:進行觀賞')  # 3)知足了開放封閉原則,可是出現了函數調用的死循環
vase = fn
vase()


# 4)下方的函數嵌套結構就是裝飾器
def wrap(tag):
    def fn():
        tag()      # 原有的vase
        print('繪畫:進行觀賞')
    return fn  # 拓展功能後的vase
vase = wrap(vase)  # 將拓展功能後的功能函數從新賦值給vase


vase()  # 功能拓展了,且調用方式不變


-----------------------------------------------------------------------
# 瞭解:知足開放封閉原則,且能夠達到裝飾器的做用:拓展功能
def vase():
    print('插花')
tag = vase  # 暴露在全局:很容易被修改掉
def fn():
    tag()
    print('繪畫:進行觀賞')
vase = fn
vase()


def wrap(tag):
    def fn():
        tag()
        print('繪畫:進行觀賞')
    return fn
vase = wrap(vase)
vase()



---------------------------------------------------------------------
# 1.0版本
def fn():
    print('fn run')
fn()

# 2.0版本
def fn():
    print('fn run0')
    print('fn run1')
    print('fn run2')
fn()

# 修改了源代碼,沒有更改調用方式,對外調用方式仍是原來的,但功能要有所增長(開放)
def fn():
    print('fn run0')
    print('fn run')
    print('fn run2')
fn()

# 更改了調用方式,沒有修改原功能代碼(封閉)
def wrap(fn):
    print('fn run0')
    fn()
    print('fn run2')
wrap(fn)
View Code
 

@語法糖: @外層函數

一個函數能夠被任意一個相關裝飾器裝飾,也能夠被任意幾個裝飾器裝飾
注意:裝飾的順序會影響新增功能的執行順序
mysql

def outer(f):
    def inner():
        f()
        print("新增功能1")
    return inner
              
def wrap(f):
    def inner():
        f()
        print("新增功能2")
    return inner              

@wrap  # 被裝飾的順序決定了新增功能的執行順序
@outer  # <==> fn = outer(fn): inner      
def fn():
    print("原有功能")
示例:花瓶新增功能
def outer(fn):
    def inner():
        fn()
        print("繪畫:進行觀賞")
    return inner


def wrap(fn):
    def inner():
        fn()
        print('擺放功能')
    return inner


# 語法糖 | 笑笑語法
@wrap
@outer  # <==> vase = outer(vase)
def vase():
    print('插花')
# vase = outer(vase)

vase()
View Code

 

疊加多個裝飾器web

import time

def timmer(func):  #func=wrapper2
    def wrapper1(*args,**kwargs):
        start=time.time()
        res=func(*args,**kwargs)  #res=wrapper2(*args,**kwargs)
        stop=time.time()
        print('run time is %s' %(stop - start))
        return res
    return wrapper1

def auth(func): #func=最原始的那個index的內存地址
    def wrapper2(*args,**kwargs):
        inp_user = input('please input your username: ').strip()
        inp_pwd = input('please input your password: ').strip()
        if inp_user == 'egon' and inp_pwd == '123':
            print('login successfull')
            res=func(*args,**kwargs) # 調用最原始的那個/也就是被裝飾的那個函數
            return res
        else:
            print('username or password error')
    return wrapper2

# 解釋@語法的時候是自下而上運行
# 而執行裝飾器內的那個wrapper函數時的是自上而下
@timmer # index=timmer(wrapper2) #index=wrapper1
@auth # index=auth(最原始的那個index的內存地址) #index=wrapper2
def index():
    print('welcome to index page')
    time.sleep(3)

index() #wrapper1()
#--------------------------------1------------------------------------
import time

def timmer(func):
    print('timmer')
    def wrapper1(*args,**kwargs):
        start=time.time()
        res=func(*args,**kwargs)  #res=wrapper2(*args,**kwargs)
        stop=time.time()
        print('run time is %s' %(stop - start))
        return res
    return wrapper1

def auth(func):
    print('auth')
    def wrapper2(*args,**kwargs):
        inp_user = input('please input your username: ').strip()
        inp_pwd = input('please input your password: ').strip()
        if inp_user == 'egon' and inp_pwd == '123':
            print('login successfull')
            res=func(*args,**kwargs) # 調用最原始的那個/也就是被裝飾的那個函數
            return res
        else:
            print('username or password error')
    return wrapper2


@auth   # index=auth(wrapper1) #index=wrapper2
@timmer #timmer(最原始的index)返回wrapper1
def index():
    print('welcome to index page')
    time.sleep(3)

index() #wrapper2()




#------------------------------------2--------------------------------------------
import time



def outter1(func1): #func1=wrapper2
    print('outter1')
    def wrapper1(*args,**kwargs):
        print('wrapper1')
        res1=func1(*args,**kwargs) #res1=wrapper2(*args,**kwargs)
        return res1
    return wrapper1

def outter2(func2): #func2=最原始的那個index的內存地址
    print('outter2')
    def wrapper2(*args,**kwargs):
        print('wrapper2')
        res2=func2(*args,**kwargs)
        return res2
    return wrapper2


@outter1 # index=outter1(wrapper2) #index=wrapper1
@outter2 #outter2(最原始的那個index的內存地址) ===> wrapper2
def index():
    print('welcome to index page')
    time.sleep(3)

index()  #wrapper1()
View Code

 

有參有返的函數被裝飾

def outer(func):
    def inner(*args, **kwargs):
        print("新增功能1")
        result = func(*args, **kwargs)
        print("新增功能2")
        return result
    return inner

@outer
def func(*args, **kwargs):
    print("原有功能")
    return "原有結果"

有參裝飾器

def wrap(arg):
    def outer(func):
        def inner(*args, **kwargs):
            print("新增功能1")
            result = func(*args, **kwargs)
            print("新增功能2")
            return result
        return inner

@wrap("裝飾器參數")
def func(*args, **kwargs):
    print("原有功能")
    return "原有結果"

 示例:sql

def check_usr(fn):  # fn, login, inner:不一樣狀態下的login,因此參數是統一的
    def inner(usr, pwd):
        # 在原功能上添加新功能
        if not (len(usr) >= 3 and usr.isalpha()):
            print('帳號驗證失敗')
            return False
        
        # 原有功能
        result = fn(usr, pwd)
        
        # 在原功能下添加新功能
        # ...
        
        return result
       return inner


@check_usr
def login(usr, pwd):
    if usr == 'abc' and pwd =='123qwe':
        print('登陸成功')
        return True
    print('登陸失敗')
    return False

總結:
1.login有參數,因此inner與fn都有相同參數
2.login有返回值,因此inner與fn都有返回值
inner(usr, pwd):
    res = fn(usr, pwd)  # 原login的返回值 return res login = check_usr(login) = inner res = login('abc', '123qwe') # inner的返回值
import time

current_user={'username':None}

# 補充:全部的數據類型的值自帶布爾值,能夠直接看成條件去用,只須要記住布爾值爲假的那一些值便可(0,空,None)


def login(engine='file'): #engine='mysql'
    def auth(func): #func=最原始那個index的內存地址
        def wrapper(*args,**kwargs):
            if current_user['username']:
                print('已經登陸過了,無需再次登錄')
                res=func(*args,**kwargs)
                return res

            if engine == 'file':
                inp_user = input('please input your username: ').strip()
                inp_pwd = input('please input your password: ').strip()
                if inp_user == 'egon' and inp_pwd == '123':
                    print('login successfull')
                    current_user['username']=inp_user # 在登錄成功以後馬上記錄登陸狀態
                    res=func(*args,**kwargs) # res=最原始那個index的內存地址(*args,**kwargs)
                    return res
                else:
                    print('username or password error')
            elif engine == 'mysql':
                print('基於mysql的認證機制')
            elif engine == 'ldap':
                print('基於ldap的認證機制')
            else:
                print('沒法識別的認證源')
        return wrapper
    return auth

@login('file')  #@auth # index=auth(最原始那個index的內存地址) #index=wrapper
def index():
    print('welcome to index page')
    time.sleep(3)

@login('file')
def home(name):
    print('welcome %s to home page' %name)
    time.sleep(2)
    return 123


index() #wrapper()
res=home('egon')
print(res)
View Code
# 有參裝飾器的模板
def outter1(x,y,z):
    def outter2(func):
        def wrapper(*args,**kwargs):
            res=func(*args,**kwargs)
            return res
        return wrapper
    return outter2

# 無參裝飾器的模板
def outter(func):
    def wrapper(*args,**kwargs):
        res=func(*args,**kwargs)
        return res
    return wrapper

 

裝飾器的最終寫法閉包

def wrap(fn):
    def inner(*args, **kwargs):
        print('前增功能')
        result = fn(*args, **kwargs)
        print('後增功能')
        return result
    return inner

@wrap
def fn1():
    print('fn1的原有功能')
@wrap
def fn2(a, b):
    print('fn2的原有功能')
@wrap   
def fn3():
    print('fn3的原有功能')
    return True
@wrap
def fn4(a, *, x):
    print('fn4的原有功能')
    return True

fn1()
fn2(10, 20)
fn3()
fn4(10, x=20)
def outer(input_color):
    def wrap(fn):
        if input_color == 'red':
            info = '\033[36;41mnew action\33[0m'
        else:
            info = 'yellow:new action'

        def inner(*args, **kwargs):
            pass
            result = fn(*args, **kwargs)
            print(info)
            return result
        return inner
    return wrap  # outer(color) => wrap


color = input('color: ')
@outer(color)  # @outer(color) ==> @wrap  # func => inner
def func():
    print('func run')

func()
帶參裝飾器(瞭解)

 

登陸認證功能app

is_login = False  # 登陸狀態

def login():
    usr = input('usr: ')
    if not (len(usr) >= 3 and usr.isalpha()):
        print('帳號驗證失敗')
        return False
    pwd = input('pwd: ')
    if usr == 'abc' and pwd =='123qwe':
        print('登陸成功')
        is_login = True
    else:
        print('登陸失敗')
        is_login = False


# 完成一個登陸狀態校驗的裝飾器
def check_login(fn):
    def inner(*args, **kwargs):
        # 查看我的主頁或銷售功能前:若是沒有登陸先登陸,反之能夠進入其功能
        if is_login != True:
            print('你未登陸')
            login()
        # 查看我的主頁或銷售
        result = fn(*args, **kwargs)
        return result
    return inner

# 查看我的主頁功能
@check_login
def home():
    print('我的主頁')

# 銷售功能
@check_login
def sell():
    print('能夠銷售')

home()
from functools import wraps
def outer(func):
    @wraps(func)
    def inner(*args, **kwargs):
        '''裝飾器文檔註釋'''
        func(*args, **kwargs)
    return inner

@outer
def func(*args, **kwargs):
    '''原有文檔註釋'''
    print("原有功能")
wraps修改函數文檔註釋
相關文章
相關標籤/搜索