设为首页 加入收藏

TOP

Python装饰器实例讲解(二)(一)
2023-07-25 21:25:02 】 浏览:54
Tags:Python

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 = tim
首页 上一页 1 2 下一页 尾页 1/2/2
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇Python引入模块报错:Import &quo.. 下一篇类和类的定义

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目