装饰器

软件开发原则之一

开放封闭原则:不改变调用方式与源代码的基础上增加新功能
  封闭:不能修改被装饰对象(函数)的源代码、调用方式
  开放:增加、扩展新的功能效果

装饰器:就是闭包的一种应用
    把要被装饰的函数作为外层函数的参数通过闭包操作后返回一个替代版函数
    被装饰的函数: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
 

@语法糖: @外层函数

一个函数可以被任意一个相关装饰器装饰,也可以被任意几个装饰器装饰
注意:装饰的顺序会影响新增功能的执行顺序

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

@wrap  # 被装饰的顺序决定了新增功能的执行顺序,outer的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

 

叠加多个装饰器

import time


def timer(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 successful')
            res = func(*args, **kwargs)  # 调用最原始的那个/也就是被装饰的那个函数
            return res
        else:
            print('username or password error')

    return wrapper2


# 解释@语法的时候是自下而上运行
# 而执行装饰器内的那个wrapper函数时的是自上而下
@timer  # index=timer(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(arg)
            print("新增功能1")
            result = func(*args, **kwargs)
            print("新增功能2")
            return result
        return inner
    return outer


@wrap("装饰器参数")
def func(*args, **kwargs):
    print("原有功能")
    return "原有结果"

 

示例:

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()
带参装饰器(了解)

 

登录认证功能

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修改函数文档注释

 

posted @ 2019-04-02 15:54  zhoyong  阅读(201)  评论(0)    收藏  举报