| 
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 Trueclass 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_numsprint(get_prime_nums(2,50000))  # 效果是一样的
 
 
 我把up主的一些话摘录一些写到这里,辅助大家理解
 
 万能公式
 装饰器decorator:是一个输入是函数,输出也是函数的函数(看讲解一中的装饰器)
类装饰器 class decorator,up主说有一定的歧义
 
 可以当做装饰器的类(装饰器本身)
可以装饰类的装饰器(装饰器要装饰的对象)
 
装饰器本身既可以是函数也可以是类,装饰的对象同样可以是函数或者类
背这些理论没有意义,关键要弄懂背后的原理
__call__可以让类的实例当做函数用(就是callable)
 
 
 改造,有参数的装饰器
 装饰器语法糖背后复制代码class CountTime:    ...# 同上@CountTimedef add(a,b):  # 就用码农的demo函数    return a+bprint(add(1,2))
@CountTime等价于,所谓的万能公式咯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+bprint(CountTime(add)(1,2))
你还可以这样复制代码def add(a,b):    return a+bnew_add = CountTime(add)print(new_add(1,2))
是的,被装饰过的函数已经不再是原来的函数了,它总是会先去执行装饰器(CountTime(add))
总结:
 
 在一个函数上做装饰器,等价于装饰器调用这个函数
在类装饰器的这个例子中,add从一个函数变成了一个类的实例(type看下即可)
 
 
 
 
 我们看到过很多的装饰是有参数的,这是怎么实现的呢?
比如你想要输出的信息可以调整其前缀你希望是这样装饰和调用的复制代码@CountTime(prefix='用时:')def add(a,b):    return a+bprint(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 + bprint(add(1, 2))
 前面谈的是类是一个装饰器,装了一个函数类的装饰器下面谈的是函数是一个装饰器,装饰一个类
 
 
 
 带参数的类的装饰器
 现在有这么一个类复制代码class Person:    passwuxianfeng = 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_classnameclass Person:    passPerson = show_classname(Person)wuxianfeng = Person()print(wuxianfeng)
 你现在要写一个函数,名字随意,如show_classname
你肯定要装饰在类上复制代码@show_classnameclass 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_classnameclass Person:    passPerson = show_classname(Person)wuxianfeng = Person()print(wuxianfeng)
看着这个结果,我们来解释下(也许你会更好理解)复制代码1. show_classname(Person) 返回仍然是Person2. 但这个时候的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:    passwuxianfeng = Person()print(wuxianfeng)
默认值就是='类名:',怎么用呢复制代码@show_classname()class Human:    passqianyuli = Human()print(qianyuli)
注意不能这样复制代码@show_classnameclass Human:    passqianyuli = 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_classnameclass 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参数
如果还不明白...尽力了
 免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
 |