Python装饰器实例讲解(二)

Python装饰器实例讲解(一)

你最好去看下第一篇,虽然也不是紧密的链接在一起

参考B站码农高天的视频,大家喜欢看视频可以跳转忽略本文:https://www.bilibili.com/video/BV19U4y1d79C

一键三连哦

本文的知识点主要是

​ 类装饰器

​ 装饰器的本质(up主说的万能公式)

案例

  • 代码

    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的面向对象的知识
    • 一些类的魔术方法如__init__和__call__
    class CountTime:
    def __init__(self,function_name): # 类没传参一说,但实例化是可以传参的,类比 def count_time(func):
    self.function_name = function_name
    def __call__(self, *args, **kwargs): # 类实例的(),像函数的call , ==>def wrapper(*args,**kwargs):
    from time import time
    start_time = time()
    result = self.function_name(*args,**kwargs) # 也就改了这里,其他都一样
    end_time = time()
    print(f'统计花了{end_time-start_time}时间')
    return result
  • 完整的代码

    def is_prime(x):
    if x == 2 :
    return True
    elif x % 2 == 0 or x == 1 :
    return False
    for i in range(3, int(x ** 0.5) + 1, 2):
    if x % i == 0:
    return False
    return True class CountTime:
    ... # 就不重复上面了 @CountTime # 类是一个装饰器
    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)) # 效果是一样的

码农高天说

我把up主的一些话摘录一些写到这里,辅助大家理解

  • 装饰器decorator:是一个输入是函数,输出也是函数的函数(看讲解一中的装饰器)
  • 类装饰器 class decorator,up主说有一定的歧义
    • 可以当做装饰器的类(装饰器本身)
    • 可以装饰类的装饰器(装饰器要装饰的对象)
  • 装饰器本身既可以是函数也可以是类,装饰的对象同样可以是函数或者类
  • 背这些理论没有意义,关键要弄懂背后的原理
  • __call__可以让类的实例当做函数用(就是callable)

万能公式

  • 装饰器语法糖背后

    class CountTime:
    ...# 同上 @CountTime
    def add(a,b): # 就用码农的demo函数
    return a+b print(add(1,2))
  • @CountTime等价于,所谓的万能公式

    add = CountTime(add)
  • print(add(1,2))已经不再是使用的原始的add了,用的是新的add

    print(add(1,2)) 等价于
    
    print(CountTime(add)(1,2))
  • 也就是说

    # @CountTime  # 去掉装饰器,你就是定义了一个简单的函数
    # # add = CountTime(add) # 函数名被重定义了 相当于这样
    def add(a,b):
    return a+b print(CountTime(add)(1,2))
  • 你还可以这样

    def add(a,b):
    return a+b new_add = CountTime(add)
    print(new_add(1,2))
  • 是的,被装饰过的函数已经不再是原来的函数了,它总是会先去执行装饰器(CountTime(add))

  • 总结:

    • 在一个函数上做装饰器,等价于装饰器调用这个函数
    • 在类装饰器的这个例子中,add从一个函数变成了一个类的实例(type看下即可)

改造,有参数的装饰器

  • 我们看到过很多的装饰是有参数的,这是怎么实现的呢?

  • 比如你想要输出的信息可以调整其前缀

    计时: 0.46秒
    或者
    用时: 0.46秒
  • 你希望是这样装饰和调用的

    @CountTime(prefix='用时:')
    def add(a,b):
    return a+b print(add(1,2))
  • 那咋实现呢?

  • 回到万能公式:

    @CountTime(prefix='用时:')
    def add(a,b):
    ...
    # 等价于
    add = CountTime(add) # 那么
    @CountTime(prefix='用时:')
    def add(a,b):
    ... # 等价于
    add = CountTime(prefix='用时:')(add)
  • CountTime这个类能CountTime(prefix='用时:'),就是实例化做到的,所以...类的init方法要改一下,不再是传参function_name了,而是传你的prefix,像这样

    class CountTimeV2:
    def __init__(self,prefix='用时:'):
    self.prefix = prefix
  • 但现在还不能继续,add = CountTime(prefix='用时:')(add)中你(add)还要处理,前面是init做的,()就是callable做的,里面的参数是add,也就是函数的名字,所以你的call也要改造,像这样吗?

        def __call__(self, function_name):
    from time import time
    start_time = time()
    result = function_name(*args,**kwargs)
    end_time = time()
    print(f'统计花了{end_time-start_time}时间')
    return result
  • 不对的,光这样改造不够的,因为你这个function_name(*args,**kwargs)在IDE中就会报错,哪里来的呢?没有定义。

  • 回想讲解一中,函数装饰器里层,还有一个函数,此处就可以参考

    class CountTimeV2:
    def __init__(self, prefix='用时:'):
    self.prefix = prefix def __call__(self, function_name):
    def wrapper(*args, **kwargs): # 加了个函数 , 包裹一层
    from time import time
    start_time = time()
    result = function_name(*args, **kwargs) # 这样就可以用参数了
    end_time = time()
    print(f'{self.prefix}{end_time - start_time}') # 用之前的定义
    return result return wrapper @CountTimeV2(prefix='耗时:') # 可以改为用时、计时等等
    def add(a, b):
    return a + b print(add(1, 2))

前面谈的是类是一个装饰器,装了一个函数

下面谈的是函数是一个装饰器,装饰一个类

类的装饰器

  • 现在有这么一个类

    class Person:
    pass wuxianfeng = Person()
    print(wuxianfeng) # <__main__.Person object at 0x000002361C15A460>
  • 你学过python可以这样修改

    class Person:
    def __str__(self):
    return f"{self.__class__.__name__}" wuxianfeng = Person()
    print(wuxianfeng) # Person
  • 但如果有很多的类都要如此呢?

  • 可以写个装饰器,来装饰这些类呗

  • 怎么写?回想刚才你学到的知识,万能公式!

    def show_classname(): # 先不写参数
    pass # 先不写内容 @show_classname
    class Person:
    pass Person = show_classname(Person) wuxianfeng = Person()
    print(wuxianfeng)
    • 你现在要写一个函数,名字随意,如show_classname

    • 你肯定要装饰在类上

      @show_classname
      class Person:
      pass
    • 根据万能公式,你的Person应该变了

      Person = show_classname(Person)
      # 从上面这段代码,你要能分析出以下内容
      # 1. show_classname应该有个参数,传参是个类名
      # 2. 因为可以Person = ,所以show_classname有个返回值
    • 对于使用者而言,应该没有任何操作上的差异

      wuxianfeng = Person()
      # 从上面这段代码,你要能分析出以下内容
      # 1. Person已经被你改变了
      # 2. Person()==>show_classname(Person)(),所以show_classname这个函数的返回值还是一个类
      print(wuxianfeng)
    • 分析完了,函数体部分是有点不好理解的

      def show_classname(class_name):
      def __str__(self):
      return self.__class__.__name__
      class_name.__str__ = __str__
      return class_name @show_classname
      class Person:
      pass Person = show_classname(Person) wuxianfeng = Person()
      print(wuxianfeng)
    • 看着这个结果,我们来解释下(也许你会更好理解)

      1. show_classname(Person) 返回仍然是Person
      2. 但这个时候的Person被改变了一点(你要做的不就是如此吗?)
      3. 原来你是这样写的
      class Person:
      def __str__(self):
      return f"{self.__class__.__name__}"
      看看现在的写法
      def __str__(self):
      return self.__class__.__name__
      class_name.__str__ = __str__
      # 前面的class_name.__str__ 是类自己的函数(本段解释的line 5)
      # 后面的= __str__ ,是line8的函数
      # 是的,函数可以被重新赋值,函数是一等对象,
    • 如果还不明白...尽力了

带参数的类的装饰器

码农高天并没有给出示例代码

当然如果你真懂了前面的"改造,有参数的装饰器",也很简单

  • 直接上代码

    def show_classname(info='类名:'):
    def wrapper(class_name):
    def __str__(self):
    return info+ self.__class__.__name__
    class_name.__str__ = __str__
    return class_name
    return wrapper @show_classname('类的名字是:') #
    class Person:
    pass wuxianfeng = Person()
    print(wuxianfeng)
  • 默认值就是='类名:',怎么用呢

    @show_classname()
    class Human:
    pass
    qianyuli = Human()
    print(qianyuli)
  • 注意不能这样

    @show_classname
    class Human:
    pass
    qianyuli = Human()
    print(qianyuli)
  • 提示错误

    Traceback (most recent call last):
    File "demo.py", line 21, in <module>
    qianyuli = Human()
    TypeError: wrapper() missing 1 required positional argument: 'class_name'
  • 提个问题,为何会报错?

  • 如果你无法解释的通,你应该还没理解。

  • 答案其实还是万能公式。

    @show_classname
    class Human:
    pass
    # 1.
    等价于(万能公式来了)
    Human = show_classname(Human) # 2.
    show_classname(Human) 执行这个的时候其实你在做
    def show_classname(info='类名:'):
    ...
    Human这个东西传给了info
    # 你要不信,你改为下面这样就知道了;信的话就过
    def show_classname(info='类名:'):
    print('info是啥?',info.name)
    class Human:
    name = '女娲' # 3.
    show_classname(Human)这个的返回是wrapper
    但wrapper这个函数是有个参数的,看你的定义def wrapper(class_name): # 4.
    定义的时候是感知不到问题的,下面的报错行
    qianyuli = Human() 其实你是在
    Human()=>show_classname(Human)()=>wrapper(),错了,(看3),你需要一个class_name参数
  • 如果还不明白...尽力了

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

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

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

  2. python 装饰器(五):装饰器实例(二)类装饰器(类装饰器装饰函数)

    回到装饰器上的概念上来,装饰器要求接受一个callable对象,并返回一个callable对象(不太严谨,详见后文). 那么用类来实现也是也可以的.我们可以让类的构造函数__init__()接受一个函 ...

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

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

  4. Python 装饰器实例

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

  5. (转)python装饰器二

    Python装饰器进阶之二 保存被装饰方法的元数据 什么是方法的元数据 举个栗子 def hello(): print('Hello, World.') print(dir(hello)) 结果如下: ...

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

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

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

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

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

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

  9. Python装饰器讲解

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

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

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

随机推荐

  1. java将秒数转换为时分秒格式

    /** * 转换时间格式为xx小时xx分xx秒 * @param second xxxxx */ public String changeTimeFormat(String second) { Int ...

  2. java学习之MybBaits

    0x00前言 我前面使用的jdbc和jdbc的工具类集成的但是它们在少部分代码的情况下会会简单,但是以后如果项目较大jdbc的固定代码会很难维护,如果使用框架会简单很多,也标志着java学习正式进入到 ...

  3. WPF之lognet4的基本使用

    log4net是.Net下一个非常优秀的开源日志记录组件.log4net记录日志的功能非常强大.它可以将日志分不同的等级,以不同的格式,输出到不同的媒介.本文介绍lognet4的基本使用. 第一步:新 ...

  4. 修复 docker build 错误 "ERROR: No build stage in current context"

    若 docker build 时遇到了错误 "ERROR: No build stage in current context",则有可能是没有将 FROM 命令语句放置在第一行. ...

  5. laravel的_token传值 ; header中传_token

    laravel框架中只要是涉及到post传值都需要传 _token ,这是框架中为了防止crsf攻击所做的安全措施,那么我们用到ajax中的post 方式传值时,也需要在所传数据中添加一个_token ...

  6. 刚哥谈架构(八)- 为你的应用选择合适的API

    前言: 架构师的主要活动是做出正确的技术决策.选择合适的API是一项重要的技术决策.那么今天就看看API的选择问题. 应用程序编程接口(API)是一种计算接口,它定义了多个软件中介之间的交互.它定义了 ...

  7. Ubuntu环境下LLVM 15.0 完全编译 附windows编译LLVM master

    1. 预先安装 sudo apt install ninja-build sudo apt install llvm clang # 第一次编译需要 sudo apt-get install libn ...

  8. HTTP Analyzer 伴侣,解决Probably another instance is already up

    解决方案1: NET START HttpAnalyzer_StdV7_NetFilter START start HttpAnalyzerStdV7.exe将以上内容保存为.bat 解决方案2: p ...

  9. 论文解读(CDCL)《Cross-domain Contrastive Learning for Unsupervised Domain Adaptation》

    论文信息 论文标题:Cross-domain Contrastive Learning for Unsupervised Domain Adaptation论文作者:Rui Wang, Zuxuan ...

  10. 看完这篇,还不懂JAVA内存模型(JMM)算我输

    欢迎关注专栏[JAVA并发] 更多技术干活尽在个人公众号--JAVA旭阳 前言 开篇一个例子,我看看都有谁会?如果不会的,或者不知道原理的,还是老老实实看完这篇文章吧. @Slf4j(topic = ...