前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >python装饰器案例2则

python装饰器案例2则

作者头像
叶子陪你玩
发布2022-05-22 15:01:16
4420
发布2022-05-22 15:01:16
举报

有时候我们好不容易实现了某个功能,假设有一个处理图片的功能。

代码语言:javascript
复制
def process_img():
  pass

现在有一个新的需求,要求必须是注册登录的用户才能使用,否则无法使用。

这个时候你可能会去拆解你已经写好的process_img函数,看如何将登录注册的代码融合进去。

搞了半天,兴许成功了,也有可能原来能够工作的代码变的不能工作了,这个时候你会想,能不能将两个方法分开实现,我原先写好的代码不动,仅仅只增加一个新的功能代码,将它们像搭积木一样拼在一起即可。

装饰器是解决这类问题的绝佳设计,有了装饰器,我们就可以抽离出大量与函数功能本身无关的雷同代码并继续重用。概括的讲,装饰器的作用就是为已经存在的对象添加额外的功能,并让代码保持简短。

装饰器(Decorator)是 Python 非常重要的组成部分,它可以让其他函数在不需要做任何代码变动的前提下增加额外功能,装饰器的返回值也是一个函数对象。装饰器本质上是一个Python函数,它经常用于有切面需求的场景,比如:插入日志、权限校验等场景。


案例1:计算程序运行的时长

正常测量一段代码运行的时长可以这样写:

代码语言:javascript
复制
import time

def test():
    s = 0
    for i in range(10000000):
        s = s + i
    print(f'计算结果为{s}')
    
print(f'\n{test.__name__} 函数 开始运行.')
start_time = time.time()

# 运行函数
test()

end_time = time.time()
print(f'{test.__name__} 函数结束运行,总共耗时 {end_time - start_time} 秒')

结果:

代码语言:javascript
复制
test 函数 开始运行.
计算结果为49999995000000
test 函数结束运行,总共耗时 1.3232624530792236 秒

现在我又有一个函数,需要计算其运行时间。

代码语言:javascript
复制
def test2():
    s = 0
    for i in range(50000000):
        s = s + i
    print(f'计算结果为{s}')

简单,很容易就会想到将函数名改成test2()就可以了。

如果有更多需要计算运行时间的函数呢?能否封装一个函数,用一个变量来替换每次需要更改的函数名呢?

好了,一个专门计时的函数诞生了。

代码语言:javascript
复制
def timer(func):
    print(f'\n{func.__name__} 函数 开始运行.')
    start_time = time.time()
    # 运行函数
    func()
    end_time = time.time()
    print(f'{func.__name__} 函数结束运行,总共耗时 {end_time - start_time} 秒')

使用非常简单,直接调用,填入要测试的函数名即可。

代码语言:javascript
复制
timer(test2)

看上去非常方便,只不过需要每次调用它。

下面将其改成装饰器的写法。

代码语言:javascript
复制
def timer_deco(func):
    def deco():
        print(f'\n{func.__name__} 函数 开始运行.')
        start_time = time.time()
        # 运行函数
        func()
        end_time = time.time()
        print(f'{func.__name__} 函数结束运行,总共耗时 {end_time - start_time} 秒')
    return deco

@timer_deco
def test_deco():
    s = 0
    for i in range(10000000):
        s = s + i
    print(f'计算结果为{s}')
    
test_deco()

在定义test_deco函数前面加@timer_demo,就相当于给test_deco增加了一个测时功能,使用时直接调用原函数即可,是不是很方便呢。

又有一个函数要计算运行时间,这个函数还带有一个参数。

改成下面的装饰器代码即可。

代码语言:javascript
复制
def timer_deco(func):
    def deco(*args, **kwargs):
        print(f'\n{func.__name__} 函数 开始运行.')
        start_time = time.time()
        # 运行函数
        func(*args, **kwargs)
        end_time = time.time()
        print(f'{func.__name__} 函数结束运行,总共耗时 {end_time - start_time} 秒')
    return deco

@timer_deco
def test3(n):
    s = 0
    for i in range(n):
        s = s + i
    print(f'计算结果为{s}')

test3(5)

以后只要想测试某个函数的运行时间,直接在定义函数的前面加上@timer_deco即可。原来的代码不用做任何改变,就增加了一个新的功能。

案例2:首页登录装饰器

有一个进入首页的函数,正常直接调用即可进入。

代码语言:javascript
复制
def index():
    print("这是首页")

现在我们有了一个新想法,要求必须是登录的账号才能进入,否则要求登录。

代码语言:javascript
复制
def index():
    print("这是首页")
    
def required_login(fun):
    def wrapper():
        print("请输入账号密码")
        name = input("账号:")
        password = input("密码:")
        if name=="admin" and password=="admin12345":
            print("登录成功")
            return fun()
        else:
            print("登录失败")
    return wrapper
 
# 传入函数参数,返回新函数。
new_index = required_login(index)
# 调用函数
new_index()

上面调用函数的方法还不是很方便,更简便的方法,是将函数改成装饰器装饰,以后只要有需要登录的才能访问的函数,都可以用这个装饰器装饰一下即可。

代码语言:javascript
复制
@required_login
def index2():
    print("这是首页")


@required_login
def article():
    print("这是内容页")
    
index2()
print('-----------------')
article()

结果:

代码语言:javascript
复制
请输入账号密码
账号:admin
密码:12345
登录失败
-----------------
请输入账号密码
账号:admin
密码:admin12345
登录成功
这是内容页

下面又有新需求,要求能够判断是否已经登录过,登录过就直接进入,否则要求登录。

代码语言:javascript
复制
# 初始状态
state = "登录"

def get_args_required_login(state):
    def required_login(fun):
        def wrapper():
            if state=="登录":
                print("已经登录过,直接进入首页")
                return fun()
            else:
                print("你还未登录")
                name = input("账号:")
                password = input("密码:")
                if name=="admin" and password=="admin12345":
                    print("登录成功")
                    return fun()
                else:
                    print("登录失败")
        return wrapper
    return required_login

@get_args_required_login(state)
def index3():
    print("这是首页")

index3()

结果:

代码语言:javascript
复制
已经登录过,直接进入首页
这是首页

如果将状态改成未登录,结果会怎样呢?

代码语言:javascript
复制
# 更改初始状态为未登录
state = "未登录"
...

结果:

代码语言:javascript
复制
你还未登录
账号:admin
密码:admin1234
登录失败

你还未登录
账号:admin
密码:admin12345
登录成功
这是首页
本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2022-05-03,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 叶子陪你玩编程 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档