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. OpenHarmony移植案例: build lite源码分析之hb命令__entry__.py

    摘要:本文介绍了build lite 轻量级编译构建系统hb命令的源码,主要分析了_\entry__.py文件. 本文分享自华为云社区<移植案例与原理 - build lite源码分析 之 hb ...

  2. .net 温故知新:【9】.NET日志记录 ILogger使用和原理

    日志 日志作为我们程序记录的"黑匣子"是不论什么系统都会使用到的,比如我们经常使用的log4net就是第三方日志记录提供程序.NET 支持使用各种内置和第三方日志记录提供程序的日志 ...

  3. (C++) C++ template笔记 -- template关键字及typename关键字

    在调用C++ template函数时,有时候语法会存在歧义. 调用函数时,使用 obj.template func<...>() 形式的语法,避免歧义: 调用类型时,使用嵌入子类型时,使用 ...

  4. 模块/collections/random/time/datetime

    内容概要 模块--包的具体使用 编程思想介绍 软件开发--目录规范 常用模块介绍--collections模块 常用模块介绍--time.datetime 常用模块介绍--random 1.包的具体使 ...

  5. Linux 中的文件简单说明

    Linux 中的文件简单说明 作者:Grey 原文地址: 博客园:Linux 中的文件简单说明 CSDN:Linux 中的文件简单说明 说明 本文基于 CentOS 7 根目录(/)下文件夹主要作用 ...

  6. Java 中 String 与 StringBuffer 和 StringBuilder 的区别

    简介: String 是 Java 中很常用的类之一,同时,字符串是 Java 面试中最重要的话题之一. StringBuffer 和 StringBuilder 类提供了操作字符串的方法. 我们将研 ...

  7. android nativate 动态注册 静态注册

    说明:在java函数的入口比较容易分析, 把activity的生命周期或者关键函数通过放在so层,分析起来就困难多了 1.在MainActivity中 package com.demo.nativat ...

  8. MIsc writeup

    1. 杂项 图片里面有什么 ,附件为一张图片 通过Binwalk查看发现有压缩包,通过foremost分离一下. 打开输出文件,发现里面有两个图片. 00000000.png是原图,00000722. ...

  9. Django静态文件配置、form表单、request对象、连接数据库、ORM

    目录 静态文件配置 静态文件相关配置 1.接口前缀 浏览器停用缓存 2.接口前缀动态匹配 form表单 action 控制数据提交的地址 method 控制数据提交的方法 请求方法补充 get: 朝服 ...

  10. APICloud平台使用融云模块实现音视频通话实践经验总结分享

    需求概要:实现视频拨打.接听.挂断.视频界面大小窗口.点击小窗口实现大小窗口互换. 实现思路:一方拨打后,另一方要能收到相应事件,然后接听.接通后,渲染对方视频画面.那么己方视频画面什么时候渲染呢?对 ...