Python装饰器实例讲解(一)

多种角度讲述这个知识,这是个系列文章

但前后未必有一定的顺承关系

部分参考网络

本文以一个小案例引出装饰器的一些特点,不涉及理论,后面再谈

案例

  • 写一个代码来求一个数是否是质数

    def is_prime(x):
    if x == 2 :
    return True
    elif x == 1 or x % 2 == 0 :
    return False
    for i in range(3, int(x ** 0.5) + 1, 2):
    if x % i == 0:
    return False
    return True
  • 写个代码来计算某个数值范围内有多少个质数

    def get_prime_nums():
    return len(list(filter(is_prime,range(2,50000))))
  • 换一下,我们不是要学这个,我们要学装饰器

    def get_prime_nums():
    from time import time
    start_time = time()
    prime_nums = 0
    for num in range(2,50000):
    if is_prime(num):
    prime_nums = prime_nums + 1
    end_time = time()
    print(f'统计花了{end_time-start_time}时间')
    print(f'一共有{prime_nums}个质数') get_prime_nums()
    # 统计花了0.025316476821899414时间
    # 一共有5133个质数
  • 你在这里会发现一个潜在的需求,可能不光是你这么一个函数有统计时间的需求,其他函数一样有,但现在这种处理方法可能要在每个目标函数上去加那段时间处理的代码,非常麻烦,那有没有好的做法呢?答案就是装饰器。

装饰器

  • 改造(对比下跟之前的区别)

  • 获取质数个数函数,不需要统计时间

    def get_prime_nums():
    prime_nums = 0
    for num in range(2,50000):
    if is_prime(num):
    prime_nums = prime_nums + 1
    print(f'一共有{prime_nums}个质数')
  • 写一个装饰器的函数(不用管为何这么写,以后会详细说明)

    def count_time(func):
    def wrapper():
    from time import time
    start_time = time()
    func()
    end_time = time()
    print(f'统计花了{end_time-start_time}时间')
    return wrapper
  • 给要加时间的函数套上这个装饰器

    @count_time
    def get_prime_nums():
    prime_nums = 0
    ... # 不重复了
  • 再次执行get_prime_nums()效果跟之前是一样的

  • 同样的你可以将这个装饰器运用到其他函数上去

    @count_time
    def get_odd_nums():
    odd_nums = 0
    for num in range(2,50000):
    if num % 2 == 1:
    odd_nums = odd_nums + 1
    print(f'一共有{odd_nums}个奇数') get_odd_nums()
  • 完了吗,没有。可能性还有很多,主要是被装饰函数的变化导致了装饰器本身要随之适应变化。

装饰器改造一

  • 如果被装饰的函数有返回值呢?

    @count_time
    def get_prime_nums():
    prime_nums = 0
    for num in range(2,50000):
    if is_prime(num):
    prime_nums = prime_nums + 1
    return prime_nums
  • 此时你直接调用函数,而不改造装饰器的话,是无法得到这个数量的

    get_prime_nums()  # 统计花了0.032898664474487305时间
    
    print(get_prime_nums())
    # 统计花了0.039182424545288086时间
    # None
  • 改造装饰器

  • 如何改造呢?你应该要去理解装饰器的运行原理(没那么复杂,但我们这个课不深入,仅作为案例给你展示)

    def count_time(func):
    def wrapper():
    from time import time
    start_time = time()
    result = func() # 改动1: 用一个变量来接收func()的返回
    end_time = time()
    print(f'统计花了{end_time-start_time}时间')
    return result # 改动2: return出去
    return wrapper
  • 此时你再执行

    print(get_prime_nums())
    # 统计花了0.054421424865722656时间
    # 5133 就能看到这个返回值了
  • 完了吗?还没有,如果我们的被装饰函数有参数呢?

装饰器改造二

  • 你的被装饰函数存在参数

    @count_time
    def get_prime_nums(end):
    prime_nums = 0
    for num in range(2,end):
    if is_prime(num):
    prime_nums = prime_nums + 1
    return prime_nums print(get_prime_nums(50000))
  • 其实在IDE中get_prime_nums(50000)就会提示你意外实参

  • 执行结果

    Traceback (most recent call last):
    File "...\demo.py", line 37, in <module>
    print(get_prime_nums(50000))
    TypeError: wrapper() takes 0 positional arguments but 1 was given
  • 这是初学者最困惑的地方了,等我们讲了原理(或者说诀窍)你应该就非常清楚为何会这样报错了

  • 怎么修改呢?

    def count_time(func):
    def wrapper(*args): # 改动1: 增加一个不定参数
    from time import time
    start_time = time()
    result = func(*args) # func也增加
    end_time = time()
    print(f'统计花了{end_time-start_time}时间')
    return result
    return wrapper
  • 再次执行,就ok了

    print(get_prime_nums(50000))
    # 统计花了0.029825448989868164时间
    # 5133
  • 但是要注意,这样的话,如果你的被装饰函数是之前的没有参数的情况,是会报错的

    # 回到过去
    @count_time
    def get_prime_nums():
    prime_nums = 0
    for num in range(2,50000):
    if is_prime(num):
    prime_nums = prime_nums + 1
    return prime_nums
    print(get_prime_nums(50000))
  • 报错

    Traceback (most recent call last):
    File "...\demo.py", line 37, in <module>
    print(get_prime_nums(50000))
    File "...\demo.py", line 22, in wrapper
    result = func(*args)
    TypeError: get_prime_nums() takes 0 positional arguments but 1 was given 进程已结束,退出代码为 1
  • 但由于是*args,你改成多个参数倒是可以的

    @count_time
    def get_prime_nums(start,end):
    prime_nums = 0
    for num in range(start,end):
    if is_prime(num):
    prime_nums = prime_nums + 1
    return prime_nums print(get_prime_nums(2,50000)) # 可以执行

  • 如果你这样调用

    print(get_prime_nums(start=2,end=50000))
  • 报错

    Traceback (most recent call last):
    File "...\demo.py", line 37, in <module>
    print(get_prime_nums(start=2,end=50000))
    TypeError: wrapper() got an unexpected keyword argument 'start' 进程已结束,退出代码为 1
  • 可以这样修改你的装饰器

    def count_time(func):
    def wrapper(*args,**kwargs): # 加个关键字参数
    from time import time
    start_time = time()
    result = func(*args,**kwargs) # 这样也要加
    end_time = time()
    print(f'统计花了{end_time-start_time}时间')
    return result
    return wrapper

说在最后

  • 这个案例是入门的,讲解了装饰器的一些简单使用
  • 但,留了一些坑,你可能未必知道为何要这么修改,装饰器是怎么调度的等等
  • 且听下回分解

Python装饰器实例讲解(一)的更多相关文章

  1. python --装饰器内容讲解

    python装饰器就是用于拓展原来函数功能的一种函数,这个函数的特殊之处在于它的返回值也是一个函数,使用python装饰器的好处就是在不用更改原函数的代码前提下给函数增加新的功能. 3.1 定义装饰器 ...

  2. Python 装饰器实例

    retry 偶然看到一篇文章,想到了前几天的一个需求,git pull性能不稳,需要加入重试机制,正好这个装饰器的实例符合这样的场景. # coding:utf-8 import time impor ...

  3. python --装饰器通俗讲解

    装饰器 什么是装饰器?:在不修改源代码和调用方式的基础上给其增加新的功能,多个装饰器可以装饰在同一个函数上 Python中的装饰器是你进入Python大门的一道坎; 装饰器特点: 不改变原函数原代码: ...

  4. Python装饰器与面向切面编程

    今天来讨论一下装饰器.装饰器是一个很著名的设计模式,经常被用于有切面需求的场景,较为经典的有插入日志.性能测试.事务处理等.装饰器是解决这类问题的绝佳设计,有了装饰器,我们就可以抽离出大量函数中与函数 ...

  5. Python装饰器讲解

    Python装饰器讲解 定义:本质是函数,就是为其他函数添加附加功能.原则:1.不能修改被装饰的函数的源代码 2.不能修改被装饰的函数的调用方式 import time def timmer(func ...

  6. Python学习:11.Python装饰器讲解(二)

    回顾 上一节我们进行了Python简单装饰器的讲解,但是python的装饰器还有一部分高级的使用方式,这一节就针对python装饰器高级部分进行讲解. 为一个函数添加多个装饰器 今天,老板又交给你一个 ...

  7. Python的装饰器实例用法小结

    这篇文章主要介绍了Python装饰器用法,结合实例形式总结分析了Python常用装饰器的概念.功能.使用方法及相关注意事项 一.装饰器是什么 python的装饰器本质上是一个Python函数,它可以让 ...

  8. 使用python装饰器计算函数运行时间的实例

    使用python装饰器计算函数运行时间的实例 装饰器在python里面有很重要的作用, 如果能够熟练使用,将会大大的提高工作效率 今天就来见识一下 python 装饰器,到底是怎么工作的. 本文主要是 ...

  9. Python 装饰器(Decorators) 超详细分类实例

        Python装饰器分类 Python 装饰器函数: 是指装饰器本身是函数风格的实现; 函数装饰器: 是指被装饰的目标对象是函数;(目标对象); 装饰器类 : 是指装饰器本身是类风格的实现; 类 ...

  10. python装饰器在接口自动化测试中的应用

    在讲解装饰器在接口自动化测试项目的应用之前,我们先来介绍一下python装饰器到底是个什么 装饰器 说装饰器就不得不提一下函数这个一等公民了,在python中函数有几个特性先来了解一下 函数的一些特性 ...

随机推荐

  1. Sprint产品待办列表的优先级要怎么排?

    在梳理产品待办事项列表的过程中,产品负责人需要先做优先级排列,保证我们 在一定的时间盒内能够交付需要优先级最高.最具价值的用户故事. 那这个用户故事的优先级要怎么排列,我们怎样选择用户故事的实现顺序? ...

  2. 利用nginx自带的反向代理以及轮询功能实现应用的负载均衡

    针对中间件部署的应用(war包),可使用nginx自带的反向代理以及轮询功能,实现应用的负载均衡. 一.架构图 二.环境准备 准备2套环境,如19.1.0.18:7001,19.1.0.16:7001 ...

  3. Java反应式编程(1)

    您好,我是湘王,这是我的博客园,欢迎您来,欢迎您再来- 前面把Java函数式编程的由来和最主要的核心知识点讲完了.包括比较难懂的Lambda表达式是怎么演变而来的也全部都撸了一遍.Lambda表达式这 ...

  4. UBOOT编译--- UBOOT顶层Makefile中目标_all和all的关系及背景(四)

    @ 目录 1. 前言 2. 概述 3. 老版本UBOOT(背景) 4. 新版本UBOOT 5. 参考 1. 前言 UBOOT版本:uboot2018.03,开发板myimx8mmek240. 2. 概 ...

  5. beanshell报错:Error invoking bsh method: eval解决办法(beanshell 不支持Java中的泛型)

    起因:在beanshell中读取CSV文件中的内容,相同的代码在IDEA中可以执行通过,但是在beanshell中报错: ERROR o.a.j.u.BeanShellInterpreter: Err ...

  6. OpenAI 推出超神 ChatGPT 注册教程来了

    前几天,OpenAI 推出超神 ChatGPT,非常火爆.但是呢,因为不可抗力原因,大部分人无法体验到.这里我分享一下注册的攻略. 准备 首先能能访问 Google(前置条件,不能明确说,懂得都懂) ...

  7. 网络基础与osi七层与TCP/IP协议

     一 什么是网络 网络:计算机网络是一组计算机或网络设备通过有形 的线缆或无形的媒介如无线,连接起来,按照一定的 规则,进行通信的集合. 通信,是指人与人.人与物.物与物之间通过某种媒 介和行为进行的 ...

  8. 微服务组件-----Spring Cloud Alibaba 注册中心Nacos的CP架构Raft协议分析

    前言 本篇幅是继  注册中心Nacos源码分析 的下半部分. 意义 [1]虽说大部分我们采用注册中心的时候考虑的都是AP架构,为什么呢?因为性能相对于CP架构来说更高,需要等待的时间更少[相对于CP架 ...

  9. 【机器学习】李宏毅——Transformer

    Transformer具体就是属于Sequence-to-Sequence的模型,而且输出的向量的长度并不能够确定,应用场景如语音辨识.机器翻译,甚至是语音翻译等等,在文字上的话例如聊天机器人.文章摘 ...

  10. 从Spring中学到的【2】--容器类

    容器类 我们在实际编码中,常常会遇到各种容器类,他们有时叫做POJO,有时又叫做DTO,VO, DO等,这些类只具有容器的作用,具有完全的get,set方法,作为信息载体,作数据传输用. 其实,很多地 ...