你真的会用PYTHON的装饰器了吗?(老铁)

本文结构:

  • 无参装饰器的一般形式

               a、 解决原生函数有参问题
               b、解决原生函数有返回值问题

  • 无参装饰器的模型总结
  • 无参装饰器的具体应用实例
  • 有参装饰器
  • 有参装饰器的具体应用实例

 对于大部分学Python的人来说,装饰器可能是遇到的第一个坎,装饰器到底是什么,到底应该怎么用?本篇博客将进行彻底的讲解。

装饰器的概念:

1、装饰器就是为了在不修改被装饰对象的源代码以及调用方式的前提下,为其添加新的功能;

2、装饰器本身可以是任何可调用的对象,被装饰的对象也可以是任意可调用的对象;

简单来说装饰器就是修改别人的工具,其中修饰指的是添加功能,工具指的是函数。


装饰器的语法:

假设被装饰的函数是index,请写出@timer的含义:

index = timer(index)


场景:假如我现在有3个函数,如下所示:

def index():
    print('欢迎来到python世界')

def home():
    print('欢迎来到scala世界')

def edit():
    print('欢迎来到Java世界')

index()
home()
edit()

现在的需求:求出每一个函数的运行时间:

渣渣aa的做法:

import time

def index():
    start_time = time.time()
    print('欢迎来到python世界')
    end_time = time.time()
    print('run time is %s'%(end_time-start_time))

def home():
    start_time = time.time()
    print('欢迎来到scala世界')
    end_time = time.time()
    print('run time is %s'%(end_time-start_time))

def edit():
    start_time = time.time()
    print('欢迎来到Java世界')
    end_time = time.time()
    print('run time is %s'%(end_time-start_time))


index()
home()
edit()

 渣渣bb的做法:

def decorator(func):
    start_time = time.time()
    func()
    end_time = time.time()
    print('run time is %s'%(end_time-start_time))

def index():
    print('欢迎来到python世界')

def home():
    print('欢迎来到scala世界')

def edit():
    print('欢迎来到Java世界')

decorator(index)
decorator(home)
decorator(edit)

其中渣渣a的问题:修改了源代码;渣渣b的问题:修改了函数正常的调用方式

问题产生了:我们如何在不修改被装饰对象的源代码以及调用方式的前提下为函数增加新的功能呢?这个时候我们的装饰器就上场了。

装饰器的解决方案:

import time

def decorator(func):
    def wrapper():
        start_time = time.time()
        func()
        end_time = time.time()
        print('run time is %s' % (end_time - start_time))
    return wrapper

@decorator
def index():
    print('欢迎来到python世界')

@decorator
def home():
    print('欢迎来到scala世界')

@decorator
def edit():
    print('欢迎来到Java世界')

index()
home()
edit()  

运行结果:

View Code

 对于上面的代码,第一眼看上去是不是感觉很蒙圈,其实那是因为我中间省略了一个重要的步骤:

#!/usr/bin/python
# -*- coding:utf-8 -*-

import time

def decorator(func):
    def wrapper():
        start_time = time.time()
        func()
        end_time = time.time()
        print('run time is %s' % (end_time - start_time))
    return wrapper

def index():
    print('欢迎来到python世界')
index = decorator(index)
print(index,'   ',index.__name__)

def home():
    print('欢迎来到scala世界')
home = decorator(home)

def edit():
    print('欢迎来到Java世界')
edit = decorator(edit)


index()
home()
edit()

随后在上一张图片:当执行完  index = decorator(index) 之后:返回的wrapper实际上是一个指针变量,所以index和wrapper将指向同一块内存空间,也就是说index已经不是我们之前的index了,而是悄悄的变成了闭包函数wrapper,wrapper在控制着index。

我想你也许发现哪里发生变化了,即index = decorato(index)等价于@decorator,对的,这就是装饰器的本质:Python的一种语法糖而已,装饰器就是闭包函数的一种实现。

上面我们使用的是简单的无参装饰器,但是对于上面的程序实际上还是有两个缺陷的:

假如3个原生函数是这样的:

View Code

如果我们还按照上面的方式去运行代码,将会抛出错误,因为当执行到func()的时候,实际上执行的是原始的index函数,但是我们在调用的时候并没有传入参数,所以:解决bug1:原生函数带有参数

import time

def decorator(func):
    def wrapper(*args,**kwargs):   #wrapper('小泡芙')
        start_time = time.time()
        func(*args,**kwargs)   #执行到这里的时候会发现少了一个参数,因为原始的index函数必须传入一个参数
        end_time = time.time()
        print('run time is %s' % (end_time - start_time))
    return wrapper

@decorator
def index(name):
    print('欢迎来到python世界')

@decorator
def home():
    print('欢迎来到scala世界')

@decorator
def edit():
    print('欢迎来到Java世界')

index('小泡芙')
home()
edit()

 但是接下来问题又来了,如果原生函数带有返回值我们怎么获取呢?

@decorator
def index(name):
    print('欢迎来到python世界')
    return 'Hello World'

@decorator
def home():
    print('欢迎来到scala世界')

@decorator
def edit():
    print('欢迎来到Java世界')

print(index('小泡芙'))
home()
edit()

如果我们还按照以前的方式的话,你会发现:返回值将是None,为什么呢?因为wrapper函数的返回值就是None。

解决bug2:原生函数带有返回值

#!/usr/bin/python
# -*- coding:utf-8 -*-

import time

def decorator(func):
    def wrapper(*args,**kwargs):   #wrapper('小泡芙')
        start_time = time.time()
        res = func(*args,**kwargs)   #执行到这里的时候会发现少了一个参数,因为原始的index函数必须传入一个参数
        end_time = time.time()
        print('run time is %s' % (end_time - start_time))
        return res 
    return wrapper

@decorator
def index(name):
    print('欢迎来到python世界')
    return 'Hello World'

@decorator
def home():
    print('欢迎来到scala世界')

@decorator
def edit():
    print('欢迎来到Java世界')

print(index('小泡芙'))
home()
edit()

 运行结果:

欢迎来到python世界
run time is 0.0
Hello World
欢迎来到scala世界
run time is 0.0
欢迎来到Java世界
run time is 0.0

Process finished with exit code 0

 到这里我们将引出常用无参装饰器的模型:

def decorator(func):
    def wrapper(*args,**kwargs):
        res = func(*args,**kwargs)
        return res
    return wrapper

 接下来我们举一个具体的实例场景来说明装饰器的具体应用:要求函数在执行真正的代码之前,先实现一段认证功能,只有认证通过了,

才可以执行真正的功能。(该场景实际上是后期Django的cookie和session的应用)

#!/usr/bin/python
# -*- coding:utf-8 -*-

import time

def decorator(func):
    def wrapper(*args,**kwargs):
        name = input('请输入用户名:')
        pwd = input('请输入密码:')
        if name == 'eric' and pwd == '123456':
            print('\033[42m登陆成功....\033[0m')
            res = func(*args,**kwargs)
            return res
        else:
            print('\033[42m登陆失败,您的用户名或者密码输入有误..\033[0m')
    return wrapper

@decorator
def index():
    print('欢迎来到登陆页面')

@decorator
def home():
    print('欢迎来到用户信息界面')

@decorator
def edit():
    print('欢迎来到编辑界面')


index()
home()
edit()

 运行结果:

请输入用户名:eric
请输入密码:123456
登陆成功....
欢迎来到登陆页面
请输入用户名:Jack
请输入密码:12346
登陆失败,您的用户名或者密码输入有误..
请输入用户名:Angela
请输入密码:123
登陆失败,您的用户名或者密码输入有误..

但是上面的这个程序实际上还是存在问题,什么问题呢?这个问题就好比我已经登陆了京东的网页页面,但是后续无论我访问什么页面都需要再次登录一下登陆界面,这个问题确实有点扯。

解决方法:这个问题的真实场景是通过cookie或者session来记录用户登录状态的,但是在这里我们只能暂时通过全局变量来模拟这种效果了。

#!/usr/bin/python
# -*- coding:utf-8 -*-

import time

user_info = {'user':None,'status':False}

def decorator(func):
    def wrapper(*args,**kwargs):
        if user_info['user'] and user_info['status']:
            res = func(*args, **kwargs)
            return res
        else:
            name = input('请输入用户名:')
            pwd = input('请输入密码:')
            if name == 'eric' and pwd == '123456':
                print('\033[42m登陆成功....\033[0m')
                # 用户一旦登陆成功,我们就将登陆成功的信息记录下来
                user_info['user'] = 'eric'
                user_info['status'] = True

                res = func(*args,**kwargs)
                return res
            else:
                print('\033[42m登陆失败,您的用户名或者密码输入有误..\033[0m')
    return wrapper

@decorator
def index():
    print('欢迎来到登陆页面')

@decorator
def home():
    print('欢迎来到用户信息界面')

@decorator
def edit():
    print('欢迎来到编辑界面')


index()
home()
edit()

接下来运行结果就正常了:

请输入用户名:eric
请输入密码:123456
登陆成功....
欢迎来到登陆页面
欢迎来到用户信息界面
欢迎来到编辑界面

Process finished with exit code 0

 

上面的无参装饰器我们讲完了,接下来我们来谈论有参装饰器的概念:

有参装饰器的模型:

def outer(driver='file'):
    def decorator(func):
        def wrapper(*args, **kwargs):
            res = func(*args, **kwargs)
            return res
        return wrapper
    return decorator

其实有参装饰器也没有想象中的那么难,本质上也是闭包函数和无参装饰器的扩展。

以上面的场景为例:用户认证的方式包括很多,如文件认证、数据库认证等等,如果利用有参装饰器进行实现呢?

代码示例:

#!/usr/bin/python
# -*- coding:utf-8 -*-

import time

user_info = {'user':None,'status':False}

def outer(auth='file'):
    def decorator(func):
        def wrapper(*args,**kwargs):
            if auth == 'file':
                print('file的验证方式')
                if user_info['user'] and user_info['status']:
                    res = func(*args, **kwargs)
                    return res
                else:
                    name = input('请输入用户名:')
                    pwd = input('请输入密码:')
                    if name == 'eric' and pwd == '123456':
                        print('\033[42m登陆成功....\033[0m')
                        # 用户一旦登陆成功,我们就将登陆成功的信息记录下来
                        user_info['user'] = 'eric'
                        user_info['status'] = True

                        res = func(*args,**kwargs)
                        return res
                    else:
                        print('\033[42m登陆失败,您的用户名或者密码输入有误..\033[0m')
            elif auth == 'dba':
                print('dba的验证方式')
                if user_info['user'] and user_info['status']:
                    res = func(*args, **kwargs)
                    return res
                else:
                    name = input('请输入用户名:')
                    pwd = input('请输入密码:')
                    if name == 'eric' and pwd == '123456':
                        print('\033[42m登陆成功....\033[0m')
                        # 用户一旦登陆成功,我们就将登陆成功的信息记录下来
                        user_info['user'] = 'eric'
                        user_info['status'] = True

                        res = func(*args,**kwargs)
                        return res
                    else:
                        print('\033[42m登陆失败,您的用户名或者密码输入有误..\033[0m')
            else:
                print('其余的验证方式')
                if user_info['user'] and user_info['status']:
                    res = func(*args, **kwargs)
                    return res
                else:
                    name = input('请输入用户名:')
                    pwd = input('请输入密码:')
                    if name == 'eric' and pwd == '123456':
                        print('\033[42m登陆成功....\033[0m')
                        # 用户一旦登陆成功,我们就将登陆成功的信息记录下来
                        user_info['user'] = 'eric'
                        user_info['status'] = True

                        res = func(*args,**kwargs)
                        return res
                    else:
                        print('\033[42m登陆失败,您的用户名或者密码输入有误..\033[0m')
        return wrapper
    return decorator



def index():
    print('欢迎来到登陆页面')
decorator = outer(auth='file')
index = decorator(index)

#index本质上还是wrapper
print(index,index.__name__)


def home():
    print('欢迎来到用户信息界面')
decorator = outer(auth='else')
home = decorator(home)

#home本质上还是wrapper
print(home,home.__name__)


def edit():
    print('欢迎来到编辑界面')
decorator = outer(auth='dba')
edit = decorator(edit)

#edit本质上还是wrapper
print(edit,edit.__name__)


index()
home()
edit()

我们写成装饰器的形式:

#!/usr/bin/python
# -*- coding:utf-8 -*-

import time

user_info = {'user':None,'status':False}

def outer(auth='file'):
    def decorator(func):
        def wrapper(*args,**kwargs):
            if auth == 'file':
                print('file的验证方式')
                if user_info['user'] and user_info['status']:
                    res = func(*args, **kwargs)
                    return res
                else:
                    name = input('请输入用户名:')
                    pwd = input('请输入密码:')
                    if name == 'eric' and pwd == '123456':
                        print('\033[42m登陆成功....\033[0m')
                        # 用户一旦登陆成功,我们就将登陆成功的信息记录下来
                        user_info['user'] = 'eric'
                        user_info['status'] = True

                        res = func(*args,**kwargs)
                        return res
                    else:
                        print('\033[42m登陆失败,您的用户名或者密码输入有误..\033[0m')
            elif auth == 'dba':
                print('dba的验证方式')
                if user_info['user'] and user_info['status']:
                    res = func(*args, **kwargs)
                    return res
                else:
                    name = input('请输入用户名:')
                    pwd = input('请输入密码:')
                    if name == 'eric' and pwd == '123456':
                        print('\033[42m登陆成功....\033[0m')
                        # 用户一旦登陆成功,我们就将登陆成功的信息记录下来
                        user_info['user'] = 'eric'
                        user_info['status'] = True

                        res = func(*args,**kwargs)
                        return res
                    else:
                        print('\033[42m登陆失败,您的用户名或者密码输入有误..\033[0m')
            else:
                print('其余的验证方式')
                if user_info['user'] and user_info['status']:
                    res = func(*args, **kwargs)
                    return res
                else:
                    name = input('请输入用户名:')
                    pwd = input('请输入密码:')
                    if name == 'eric' and pwd == '123456':
                        print('\033[42m登陆成功....\033[0m')
                        # 用户一旦登陆成功,我们就将登陆成功的信息记录下来
                        user_info['user'] = 'eric'
                        user_info['status'] = True

                        res = func(*args,**kwargs)
                        return res
                    else:
                        print('\033[42m登陆失败,您的用户名或者密码输入有误..\033[0m')
        return wrapper
    return decorator



@outer(auth='file')
def index():
    print('欢迎来到登陆页面')


@outer(auth='dba')
def home():
    print('欢迎来到用户信息界面')


@outer(auth='else')
def edit():
    print('欢迎来到编辑界面')



index()
home()
edit()

对于有参装饰器,我们注意两点就够了:

@outer(auth='file') ===> @decorator  ==> index=decorator(index),也就是说,有参装饰器又给内部提供了一个参数。

实际上就是在最开始多了一个步骤,后面的步骤和我们上面是一模一样的。

OK,如有问题,欢迎留言指正。

 

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一只懒得睁眼的猫

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值