python (七)装饰器

一、装饰器的形成

  1、装饰器的形成(装饰不带参数的函数)  

需求1:  用一段代码测试一下函数的执行效率

import time
print(time.time())  # 输出当前时间

def func1():
    time.sleep(1)  # 等待1秒
    print(111)

def func2():
    time.sleep(1)
    print(222)

start_time = time.time()  # 开始时间
func1()  # 执行此函数
end_time = time.time()  # 结束时间
print('此函数的执行时间是%s' % (end_time-start_time))  # 输出执行时间 =结束时间-开始时间


start_time = time.time()
func2()
end_time = time.time()
print('此函数的执行时间是%s' % (end_time-start_time))
装饰器--简单版

但是如果有多个函数,都想测试他们的执行时间,是不是每次都得func1 = timer(func1)?

这样还是有点麻烦,因为这些函数的函数名可能是不相同,有func1,func2,graph,等等

第一版本不好,重复造轮子,测试函数的效率 必须得复制代码测试。

改版:使用函数

# 将代码封装到函数中,基本完成
import time

def func1():
    time.sleep(1)
    print(111)

def func2():
    time.sleep(1)
    print(222)

def timmer(f):
    start_time = time.time()
    f()
    end_time = time.time()
    print('此函数的执行时间是%s' % (end_time-start_time))
timmer(func1)
timmer(func2)
装饰器--使用函数

需求2: 生产环境测试函数的真正的执行效率:线上500个函数

  线上问题:
    线上的函数你不改变
    函数的原本的调用方式不能改变

  原本线上的调用方式:
    func1

    func2

  但是用了测试效率的函数之后
    def timmer(f):
    start_time = time.time()
    f()
    end_time = time.time()
    print('此函数的执行时间是%s' % (end_time-start_time))

  此时已经改变了函数的调用方式:

    func1()

    timmer(func1)

没有改变函数里面的函数体,但是改变了函数的调用方法,所以要解决:用最少的代码,解决调用方式一致性的问题。
最终完成的需求 func1() 这个指令既要执行func1函数,又要测试效率

# 利用闭包,在不改变函数体的情况下
import time
def func1():
    time.sleep(1)
    print(111)

def func2():
    time.sleep(1)
    print(222)

def timmer(f): # f = func1 函数的内存地址
    def inner():
        start_time = time.time()
        f()
        end_time = time.time()
        print('此函数的执行时间是%s' % (end_time-start_time))
    return inner
func1 = timmer(func1) # = inner
func1()  # inner()

def func1():
    print(func1)
func1 = 777
print(func1)
装饰器--利用闭包

 功能实现了,但是有点复杂,还可以继续优化

import time
def timmer(f): # f = func1 函数的内存地址
    def inner():
        start_time = time.time()
        f()
        end_time = time.time()
        print('此函数的执行时间是%s' % (end_time-start_time))
    return inner


def func1():
    time.sleep(1)
    print(111)


def func2():
    time.sleep(1)
    print(222)


def func3():
    time.sleep(1)
    print(333)

func1 = timmer(func1)
func1()  # inner()

func2 = timmer(func2)
func2()

func3 = timmer(func3)
func3()
装饰器--闭包优化

上面还是不够优化,python 提供了更简洁的方法--语法糖

import time
def timmer(f):  # f = func1 函数的内存地址
    def inner():
        start_time = time.time()
        f()
        end_time = time.time()
        print('此函数的执行时间是%s' % (end_time - start_time))

    return inner

@timmer # func1 = timmer(func1)
def func1():
    time.sleep(1)
    print(111)

func1()  # inner()
@timmer
def func2():
    time.sleep(1)
    print(222)
func2()


def func3():  # 这个不会执行,没有加语法糖
    time.sleep(1)
    print(333)
装饰器--语法糖

  2、装饰带参数的函数  

import time

def timmer(f):  # f = func1 函数的内存地址
    def inner(*args, **kwargs):
        start_time = time.time()
        f(*args, **kwargs)
        end_time = time.time()
        print('此函数的执行时间是%s' % (end_time - start_time))

    return inner

@timmer  # func1 = timmer(func1)
def func1(a):
    time.sleep(0.6)
    print('来了,%s' % (a))
func1('大锤')

  3、装饰有返回值的函数  

import time

def timmer(f):  # f = func1 函数的内存地址
    def inner(*args, **kwargs):
        start_time = time.time()
        ret = f(*args, **kwargs)
        end_time = time.time()
        print('此函数的执行时间是%s' % (end_time - start_time))
        return ret

    return inner

@timmer  # func1 = timmer(func1)
def func1(a):
    time.sleep(1)
    print('来了,%s' % (a))
    return 666
print(func1('大锤'))  # print(inner('alex'))

小总结:  

  装饰器本质其实闭包,
  装饰器在不改变原函数的内部函数体以及调用方式的前提下,给函数增加了额外的功能:登录,注册,打印日志,函数效率等等。

  4、装饰器的基本结构  

def wrapper(f):
    def inner(*args, **kwargs):
        ''' 执行被装饰的函数之前的操作'''
        print('执行之前', 555)
        ret = f(*args, **kwargs)
        print('执行之后', 666)
        ''' 执行被装饰的函数之后的操作'''
        return ret
    return inner

@wrapper
def func():
    pass

func()

二、装饰器的应用

  1、装饰器的固定格式  

# 装饰器的固定格式:
def wrapper(f):
    def inner(*args, **kwargs):
        ret = f(*args, **kwargs)
        return ret
    return inner

  2、装饰器的应用  

# 登录认证

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


def login(f):
    def inner(*args, **kwargs):
        if login_status['status']:
            ret = f(*args, **kwargs)
            return ret
        else:
            print('请先登录')
            username = input('请输入用户名').strip()
            password = input('请输入密码').strip()
            if username == '大锤' and password == '123':
                login_status['username'] = username
                login_status['status'] = True
                ret = f(*args, **kwargs)
                return ret
    
    return inner


@login
def article():
    print('欢迎访问文章页面')


@login
def dariy():
    print('欢迎访问日记页面')


@login
def comment():
    print('欢迎访问评论页面')


dic = {
    1: login,
    2: article,
    3: dariy,
    4: comment,
}

while 1:
    print('''
    欢迎来到博客园首页:
    1,登录
    2,文章页面
    3,日记页面
    4,评论页面
    ''')
    num = input('请输入数字:').strip()
    dic[int(num)]()
装饰器的应用-登录认证

三、带参数的装饰器

需求: 500个函数加上装饰器,再去掉,在加 ,再去....

# 基本格式
def login(a, b):
    print(a, b)
    def wrapper(f):
        def inner(*args, **kwargs):
            ret = f(*args, **kwargs)
            return ret
        return inner
    return wrapper

@login(1, 2)
def func():
    print(111)

func()

举例

# 带参数的装饰器
# 登录认证

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

def login(a):
    def wrapper(f):
        def inner(*args, **kwargs):
            if a:
                if login_status['status']:
                    ret = f(*args, **kwargs)
                    return ret
                else:
                    print('请先登录')
                    username = input('请输入用户名').strip()
                    password = input('请输入密码').strip()
                    if username == '大锤' and password == '123':
                        login_status['username'] = username
                        login_status['status'] = True
                        ret = f(*args, **kwargs)
                        return ret
            else:
                ret = f(*args, **kwargs)
                return ret
        return inner
    return wrapper

flag = False

@login(flag)
def func1():
    print(111)
@login(flag)
def func2():
    print(12)
@login(flag)
def func3():
    print(131)

func1()
带参数的装饰器--登录认证基本结构
login_status = {
    'username': None,
    'status': False,
}


def login(a):
    def wrapper(f):
        def inner(*args, **kwargs):
            if login_status['status']:
                ret = f(*args, **kwargs)
                return ret
            else:
                print('请先登录')
                '''根据a 京东,天猫 去验证不同密码'''
                username = input('请输入用户名').strip()
                password = input('请输入密码').strip()
                if username == '二狗' and password == '123':
                    login_status['username'] = username
                    login_status['status'] = True
                    ret = f(*args, **kwargs)
                    return ret

        return inner
    return wrapper


@login('京东')
def jd():
    print('欢迎访问文章页面')


@login('京东')
def jdmarkte():
    print('欢迎访问日记页面')


@login('天猫')
def TM():
    print('欢迎访问评论页面')

@login('天猫')
def TMmarke():
    print('欢迎访问评论页面')
带参数的装饰器--根据不同页面验证不同密码

 进阶:多个装饰器装饰一个函数,注意顺序

# 多个装饰器装饰一个函数

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

def wrapper2(func):  # func = inner1
    def inner2():
        print('wrapper2 ,before func')  # 1
        func()  # inner1()
        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()
原文地址:https://www.cnblogs.com/caoyinshan/p/10110506.html