装饰器函数

装饰器的概念应用场景

概念:装饰器本质上就是一个函数, 它可以让其他函数在不需要做任何代码变动的前提下, 增加额外的功能, 装饰器的返回值也是一个函数对象.

应用场景: 比如插入日志, 性能测试, 事物处理, 缓存等等场景.

装饰器的形成过程

现在我有一个需求: 需要测试一个函数的执行时间, 在不改变这个函数代码的情况下:

import time


def timmer(fn):
    def inner():
        s_time = time.time()
        fn()
        e_time = time.time()
        print('函数运行时间为:', e_time-s_time)
    return inner


def func():
    time.sleep(1)
    print('10000行代码运行结束')


func = timmer(func)
func()
装饰器_简单版

 但是如果有多个函数, 我都想测试他们的执行时间, 那岂不是每次都得func = timmer(func)? 这样感觉还是有点麻烦, 因为这些函数的函数名都不一样.

所以python给我们提供了更简单的方法, 那就是语法糖.

import time


def timmer(fn):
    def inner():
        s_time = time.time()
        fn()
        e_time = time.time()
        print('函数运行时间为:', e_time-s_time)
    return inner

@timmer     #相当于  func = timmer(func)
def func():
    time.sleep(1)
    print('10000行代码运行结束')


func()
装饰器_语法糖

上面的装饰器装饰的都是不带参数的函数, 那如果要装饰一个带参数的函数该怎么办?

import time


def timmer(fn):
    def inner(*args, **kwargs):
        s_time = time.time()
        ret = fn(*args, **kwargs)
        e_time = time.time()
        print('函数运行时间为:', e_time-s_time)
        return ret
    return inner

@timmer     #相当于  func1 = timmer(func1)
def func1(a,b):
    time.sleep(1)
    print('10000行代码运行结束')
    return a+b


@timmer     #相当于  func2 = timmer(func2)
def func2(x,y):
    time.sleep(1)
    print('10000行代码运行结束')
    return x*y


print(func1(2,3))
print(func2(4,5))
装饰带参数带返回值的函数

上面的装饰器已经非常完美了, 但是有我们正常情况下查看函数信息的方法在此都会失效:

@timmer
def func1(a,b):
    """我是func1"""
    time.sleep(1)
    print('10000行代码运行结束')
    return a+b

print(func1.__doc__)
print(func1.__name__)

# 结果:
# None
# inner

解决方案:

from functools import wraps
import time


def timmer(fn):
    @wraps(fn)
    def inner(*args, **kwargs):
        s_time = time.time()
        ret = fn(*args, **kwargs)
        e_time = time.time()
        print('函数运行时间为:', e_time-s_time)
        return ret
    return inner

@timmer
def func1(a,b):
    """我是func1"""
    time.sleep(1)
    print('10000行代码运行结束')
    return a+b

print(func1.__doc__)
print(func1.__name__)

# 结果:
# 我是func1
# # func1

开放封闭原则

1.对扩展是开放的

为什么要对扩展开放呢?

我们说,任何一个程序,不可能在设计之初就已经想好了所有的功能并且未来不做任何更新和修改。所以我们必须允许代码扩展、添加新功能。

2.对修改是封闭的

为什么要对修改封闭呢?

因为我们写的一个函数, 很有可能已经交付给其他人使用了, 如果我们这个时候对其进行修改, 很有可能影响其他已经在使用该函数的用户.

装饰器完美的遵循了这个开放封闭原则

装饰器的主要功能和固定架构

def wrapper(fn):
    def inner(*args, **kwargs):
        """执行函数前要做的"""
        ret=fn(*args, **kwargs)
        """执行函数后要做的"""
        return ret
    return inner
装饰器的固定格式
from functools import wraps

def wrapper(fn):
    @wraps(fn)
    def inner(*args, **kwargs):
        """执行函数前要做的"""
        ret=fn(*args, **kwargs)
        """执行函数后要做的"""
        return ret
    return inner
装饰器的固定格式_wraps版

带参数的装饰器

假如你有成千上万个函数使用了一个装饰器, 现在你又想把这些装饰器全部取消掉, 那该怎么办?

总不可能一个一个去删掉吧, 万一过两天你又想加上去呢?

import time

def outer(flag):
    def timmer(fn):
        def inner(*args, **kwargs):
            if flag:
                print("执行函数前要做的")
            ret = fn(*args, **kwargs)
            if flag:
                print("执行函数后要做的")
            return ret
        return inner
    return timmer

flag = 0

@outer(flag)
def func1(a,b):
    """我是func1"""
    time.sleep(1)
    print('10000行代码运行结束')
    return a+b

print(func1(2,3))
带参数的装饰器

多个装饰器装饰一个函数

def wrapper1(func):  # func == f 函数名
    def inner1():
        print('wrapper1 ,before func')  # 2
        func()
        print('wrapper1 ,after func')  # 4
    return inner1

def wrapper2(func):  # func == inner1
    def inner2():
        print('wrapper2 ,before func')  # 1
        func()
        print('wrapper2 ,after func')  # 5
    return inner2

# 就近原则
@wrapper2  # f = wrapper2(f) 里面f == inner1  外面的f == inner2
@wrapper1  # f = wrapper1(f) 里面的f == 函数名f   外面的f == inner1
def f():
    print('in f')  # 3
f()  # inner2()

# 结果:
# wrapper2 ,before func
# wrapper1 ,before func
# in f
# wrapper1 ,after func
# wrapper2 ,after func
多个装饰器装饰一个函数

装饰器的简单应用举例

需求:在第一次进入博客园的页面之前需要先登录账户.

dic_status = {
    'username': None,
    'status': False,
}

def login(fn):
    def inner(*args, **kwargs):
        if dic_status['status'] == True:
            fn(*args, **kwargs)
            return
        else:
            username=input('用户名:')
            password=input('密码:')
            with open('userinfo',mode='r',encoding='utf-8') as f:
                for line in f:
                    lis = line.strip().split("|")
                    if username == lis[0] and password == lis[1]:
                        dic_status['username']=username
                        dic_status['status']=True
                        break
                else:
                    print("登录失败")
            inner(*args, **kwargs)
    return inner





@login
def article():
    print('欢迎登录文章页面')
@login
def diary():
    print('欢迎登录日记页面')
@login
def comment():
    print('欢迎登录评论页面')
    
    
article()
comment()
简单应用示例
原文地址:https://www.cnblogs.com/af1y/p/10159200.html