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. RK3568开发笔记(五):在虚拟机上使用SDK编译制作uboot、kernel和ubuntu镜像

    前言   buildroot虽然灵活,但是基于实际情况,本身是侧重驱动和应用定制开发的只定制一次文件系统投入有点多,还不如直接ubunt自己交叉编译依赖库,做一些库的移植裁剪.  于是本篇就使用ubu ...

  2. Oracle中新建数据表的两种方法

    首发微信公众号:SQL数据库运维 原文链接:https://mp.weixin.qq.com/s?__biz=MzI1NTQyNzg3MQ==&mid=2247485212&idx=1 ...

  3. Kubernetes集群YAML文件详解

    Kubernetes集群YAML文件详解 概述 k8s 集群中对资源管理和资源对象编排部署都可以通过声明样式(YAML)文件来解决,也就是可以把需要对资源对象操作编辑到YAML 格式文件中,我们把这种 ...

  4. tekla软件安装教程

    Tekla2020 WIN10 64位安装步骤: 1.先使用"百度网盘客户端"下载Tekla2020_x64软件安装包到电脑磁盘,并鼠标右击进行解压缩,安装前先断开电脑网络,然后找 ...

  5. 给ofo共享单车撸一个微信小程序

    想学一下微信小程序,发现文档这东西,干看真没啥意思.所以打算自己先动手撸一个.摩拜单车有自己的小程序,基本功能都有,方便又小巧,甚是喜爱.于是我就萌生了一个给ofo共享单车撸一个小程序(不知道为啥of ...

  6. bugku 计算器

    打开就一个输入框和验证,尝试后发现输入框限制了位数,这还不简单,F12直接修改表单长度试试 成功得到flag

  7. (工具) TCMalloc笔记

    下载及编译 wget https://github.com/gperftools/gperftools/releases/download/gperftools-2.9.1/gperftools-2. ...

  8. C温故补缺(十五):栈帧

    栈帧 概念 栈帧:也叫过程活动记录,是编译器用来实现过程/函数调用的一种数据结构,每次函数的调用,都会在调用栈(call stack)上维护一个独立的栈帧(stack frame) 栈帧的内容 函数的 ...

  9. 一文带你了解 Spring 的@Enablexxx 注解

    layout: post categories: Java title: 一文带你了解 Spring 的@Enablexxx 注解 tagline: by 子悠 tags: - 子悠 前面的文章给大家 ...

  10. Python: 你所不知道的星号 * 用法

    以下内容为本人的学习笔记,如需要转载,请声明原文链接微信公众号「englyf」https://mp.weixin.qq.com/s/FHyosiG_tegF5NRUEs7UdA 本文大概 1193 个 ...