装饰器函数

阅读目录:

  • 开闭原则

  • 装饰器的概述

  • 同一个函数被多个装饰器装饰

  • 带参数的装饰器

  • 装饰器的应用

开闭原则 设计模式六大原则之一

又被称为开放封闭原则,主要包括以下两点:

  1.对功能的扩展开放(即可以加功能)

  2.对(源)代码的修改是封闭的(即原来的代码不能修改,为了程序的稳定性)

总结下来就是 不改变源函数(原来的调用方式,函数名,参数,返回值),只改变函数体(仅限于对函数体增加代码,不能更改原有的代码)),就能实现新功能的扩展.

装饰器的概述

  装饰器的本质:一个闭包函数

  装饰器的功能:在不修改原函数及其调用方式的情况下对原函数功能进行扩展(在函数的前、后添加功能)

def wrapper(fn):
    def inner():
        print('开外挂')   # 在原有函数前面加新功能
        fn()
        print('关闭外挂')  # 在原有函数后面加新功能

    return inner


def play():  # 对原有函数完全没有修改
    print('双击LOL')
    print('选择狂战士')
    print('进草丛')
    print('狮子看')
    print('崩山击')


play = wrapper(play)  # 这个需要的时候写上,不需要的时候注释掉
play()  # 对原有的调用方式完全没有变
装饰器--最初级版
def wrapper(fn):
    def inner(*args, **kwargs):  # 无敌传参-->形参,聚合
        print('开外挂')  # 在原有函数前面加新功能
        f = fn(*args, **kwargs)  # 这里是fn函数的调用(-->实参,打散),所以fn函数的返回值被这里接收
        print('关闭外挂')  # 在原有函数后面加新功能
        return f
    return inner


def play(username, password):  # 对原有函数完全没有修改
    print('登录:', username, password)
    print('双击LOL')
    print('选择狂战士')
    print('进草丛')
    print('狮子看')
    print('崩山击')
    return '十字斩刀者'


def xiaoxiaole(qq):
    print('登录QQ账号:', qq)
    print('消消乐')
    return '奖励10000分'


play = wrapper(play)  # 这个需要的时候写上,不需要的时候注释掉
ret = play('alex', '123')  # 对原有的调用
print(ret)
xiaoxiaole = wrapper(xiaoxiaole)
re = xiaoxiaole(11010)
print(re)
装饰器--初级版带参数和返回值
# 通用装饰器写法 --> Python里面的动态代理
# # 装饰器存在的意义:在不破坏原有函数和原有函数调用的基础上,给函数添加新的功能(与不使用装饰器之前相比,使用装饰器后,原有目标函数的定义完全不改动,调用完全不改动,执行之后的效果除了增加新的功能也完全不改动)
def wrapper(fn):  # fn是目标函数
    def inner(*args, **kwargs):  # 无敌形参-->为了目标函数的传参
        '''在执行目标函数之前...'''
        ret = fn(*args, **kwargs)  # 调用目标函数,ret是目标函数的返回值
        '''在执行目标函数之后...'''
        return ret  # 把目标函数返回值返回,保证函数正常的结束(不改变目标函数原有的调用方式还能实现想要的功能)

    return inner


@wrapper  # 加在目标函数正上方,相当于 target_func = wrapper(target_func)
def target_func():
    pass


# target_func = wrapper(target_func)  # 此时target_func就是fn  写了@wrapper就不需要这句话了
target_func()  # 此时执行的是inner
装饰器--通用写法(语法糖)
from functools import wraps # 引入函数模块
# 装饰器: 对传递进来的函数进⾏包装. 可以在目标函数之前和之后添加任意的功能. def wrapper(func):
    @wraps(func) # 加在最内层函数最上方,使用函数原来的名字 
    def inner(*args, **kwargs):
        '''在执⾏目标函数之前要执⾏的内容''' 
        ret = func(*args, **kwargs) 
        '''在执⾏目标函数之后要执⾏的内容''' 
        return ret
    return inner


# @wrapper 相当于 target_func = wrapper(target_func) 语法糖 
@wrapper  # 加在目标函数最上方
def target_func():    
     print("我是目标函数")


# 调⽤⽬标函数
target_func() 
print(target_func.__name__)  # 不再是inner,而是target_func了
装饰器--通用写法(wraps版)
def wrapper1(fn):
    def inner(*args, **kwargs):
        print("我是wrapper1前")
        ret = fn(*args, **kwargs)
        print("我是wrapper1后")
        return ret
    return inner

def wrapper2(fn):
    def inner(*args, **kwargs):
        print("我是wrapper2前")
        ret = fn(*args, **kwargs)
        print("我是wrapper2后")
        return ret
    return inner

def wrapper3(fn):
    def inner(*args, **kwargs):
        print("我是wrapper3前")
        ret = fn(*args, **kwargs)
        print("我是wrapper3后")
        return ret
    return inner


# 就近原则
@wrapper1
@wrapper2
@wrapper3
def func():
    print("我是可怜的func")

func()
# 就近原则: 1 2 3  func 3 2 1
>>>
我是wrapper1前
我是wrapper2前
我是wrapper3前
我是可怜的func
我是wrapper3后
我是wrapper2后
我是wrapper1后

解析:
执⾏顺序: 首先@wrapper3装饰起来. 然后获取到一个新函数是wrapper3中的inner. 然后执行@wrapper2.这个时候,wrapper2装饰的就是wrapper3中的inner了...以此类推. 所以. 执⾏顺序就像: 第一层装饰器前(第二层装饰器前(第三层装饰器前(目标)第三层装饰器后)第二层装饰器后)第一层装饰器后. 程序从左到右执⾏起来,就是我们看到的结果.
同一个函数被多个装饰器装饰
# 函数名+():代表函数的执行  --> 这里返回的结果是一个函数名
# 装饰器:结构是 @+函数名--->接收  函数名+() 的返回结果 一个函数名
# @wrapper_out(True)  执行顺序为先计算函数调用的结果,拿结果作为装饰器 -->等价于后面函数的调用加了一个小括号 @(wrapper_out(True)) 这里之所以传进去实参也是因为这里是函数wrapper_out的调用
def wrapper_out(flag):  # 装饰器本身的函数
    def wrapper(fn):  # 目标函数
        def inner(*args, **kwargs):  # 无敌形参-->为了目标函数的传参
            if flag == True:
                '''在执行目标函数之前...'''
                ret = fn(*args, **kwargs)  # 调用目标函数,ret是目标函数的返回值
                '''在执行目标函数之后...'''
                return ret  # 把目标函数返回值返回,保证函数正常的结束(不改变目标函数原有的调用方式还能实现想要的功能)
            else:
                ret = fn(*args, **kwargs)
                return ret
        return inner
    return wrapper

# 语法糖 @
@wrapper_out(True)  #加在目标函数正上方,相当于 target_func = wrapper(target_func)
def target_func():
    pass


# 解析:
注意: 咱们之前的写法是@wrapper 其中wrapper是一个函数(名). 那么也就是说,如果我能让wrapper这⾥换成个函数(名)就⾏了. wrapper_out(True)返回的结果是wrapper也是一个函数啊. 刚刚好和前面的@组合成一个@wrapper. 依然还是原来那个装饰器. 只不过这⾥里套了3层. 但你要能看懂. 其实还是原来那个装饰器@wrappe.
执⾏步骤: 先执⾏wrapper(True),然后再@返回值,返回值恰好是wrapper,结果就是@wrapper.
带参数的装饰器

  装饰器的应用:

'''
员工星系表框架:
要求先登录,之后才能进行增删改查操作.(在增,删,改,查的每个函数上方添加登录验证的装饰器)
'''
menu = ("查看", "添加", "修改", "删除", "退出")

flag = False  # 没登录


def login():
    global flag
    username = input("请输入用户名:")
    password = input("请输入密码:")
    if username == "alex" and password == "123":
        flag = True
        print("登录")
    else:
        flag = False
        print("用户名密码错误")


# 登录验证装饰器
def login_verify(fn):
    def inner(*args, **kwargs):
        # 登录校验
        while 1:
            if flag == True:
                ret = fn(*args, **kwargs)
                return ret
            else:
                print('对不起, 您还没有登录')
                login()

    return inner


def chakan():
    print("==============================查看")


@login_verify
def tianjia():
    print("============================添加")


@login_verify
def xiugai():
    print("=======================修改")


@login_verify
def shanchu():
    print("=========================删除")


while 1:
    for i in range(len(menu)):
        print(i + 1, menu[i])

    num = input("请输入你要执行的菜单:")
    if num == "1":
        chakan()
    elif num == "2":
        tianjia()
    elif num == "3":
        xiugai()
    elif num == "4":
        shanchu()
    elif num == "5":
        print("程序退出中..........")
        exit()
    else:
        print("输入有误. 请重新选择!")

小结:

1.开闭原则:
  对功能的扩展是开放的
  对代码的修改是封闭的

2.通用装饰器语法(必须记住):

# 装饰器: 对传递进来的函数进⾏包装. 可以在目标函数之前和之后添加任意的功能.
def wrapper(fn): #  fn是目标函数.
    def inner(*args, **kwargs): # 为了目标函数的传参(聚合)
        '''在执行目标函数之前要执行的内容'''
        ret = fn(*args, **kwargs) # 调用目标函数(打散), ret是目标函数的返回值
        '''在执行目标函数之后要执行的内容'''
        return ret  # 把目标函数返回值返回. 保证函数正常的结束
    return inner

@wrapper  # target_func = wrapper(target_func)
def target_func():
    pass

# 调用目标函数
target_func() # 此时执行的是inner

3.同一个函数被多个装饰器装饰(就近原则)

 @wrapper1
@wrapper2
@wrapper3
def func():
pass
执行的结果:1 2 3 func 3 2 1(目标函数被离得近的装饰器括起来)

4.带参数的装饰器

def wrapper_out(参数):  # 装饰器本身的参数
      def wrapper(fn):  # fn是目标函数
             def inner(*args, **kwargs): # 目标函数执行需要的参数-->聚合
                    '''在目标函数之前需要执行的内容'''
                    ret = fn(*arg, **kwargs) # 调用目标函数,ret是目标函数的返回值-->打散
                    '''在目标函数之后需要执行的内容'''
                    return ret  # 把目标函数返回值返回,保证函数以原有的调用方式正常的结束
             return inner
      return wrapper

@wrapper_out(实参)  # 执行的时候. 先执行函数的调用然后使用返回值和前面的@组合成装饰器语法糖
def func():  # 目标函数的定义
      pass

func()  # 函数的调用

原文地址:https://www.cnblogs.com/lyfstorm/p/10119891.html