• Python小技巧:用类写装饰器的正确方法,一般新手都不知道


     

    装饰器是一个返回函数的函数。写一个装饰器,除了最常见的在函数中定义函数以外,Python还允许使用类来定义一个装饰器。
    pS注意:不管你是为了Python就业还是兴趣爱好,记住:项目开发经验永远是核心,如果你缺新项目练习或者没有python精讲教程,可以去小编的Python交流.裙 :七衣衣九七七巴而五(数字的谐音)转换下可以找到了,里面很多新教程项目,还可以跟老司机交流讨教!

    1、用类写装饰器

    下面用常见的写法实现了一个缓存装饰器。

    def cache(func):

    data = {}

    def wrapper(*args, **kwargs):

    key = f'{func.__name__}-{str(args)}-{str(kwargs)})'

    if
    key
    in
    data:

    result = data.get(key)
                print('cached')

    else:

    result = func(*args, **kwargs)

    data[key] = result
                print('calculated')

    return
    result

    return wrapper

    看看缓存的效果。

    @cache
    def rectangle_area(length, width):

    return length * width

    rectangle_area(23)
    # calculated
    # 6

    rectangle_area(23)
    # cached
    # 6

    装饰器的@cache是一个语法糖,相当于func = cache(func),如果这里的cache不是一个函数,而是一个类又会怎样呢?定义一个类class Cache, 那么调用func = Cache(func)会得到一个对象,这时返回的func其实是Cache的对象。定义__call__方法可以将类的实例变成可调用对象,可以像调用函数一样调用对象。然后在__call__方法里调用原本的func函数就能实现装饰器了。所以Cache类也能当作装饰器使用,并且能以@Cache的形式使用。

    接下来把cache函数改写为Cache类:

    class Cache:

    def __init__(self, func):
            self.func = func
            self.data = {}


    def __call__(self, *args, **kwargs):
            func = self.func

    data = self.data

    key = f'{func.__name__}-{str(args)}-{str(kwargs)})'

    if
    key
    in
    data:

    result = data.get(key)
                print('cached')

    else:

    result = func(*args, **kwargs)

    data[key] = result
                print('calculated')

    return
    result

    再看看缓存结果,效果一样。

    @Cache
    def rectangle_area(length, width):

    return length * width

    rectangle_area(23)
    # calculated
    # 6

    rectangle_area(23)
    # cached
    # 6

    2、装饰类的方法

    装饰器不止能装饰函数,也经常用来装饰类的方法,但是我发现用类写的装饰器不能直接用在装饰类的方法上。(有点绕…)

    先看看函数写的装饰器如何装饰类的方法。

    class Rectangle:

    def __init__(self, length, width):

    self.length = length

    self.width = width

        @cache

    def area(self):

    return
    self.length * self.width

    r = Rectangle(23)
    r.area()
    # calculated
    # 6

    r.area()
    # cached
    # 6

    但是如果直接换成Cache类会报错,这个错误的原因是area被装饰后变成了类的一个属性,而不是方法。

    class Rectangle:

    def __init__(self, length, width):

    self.length = length

    self.width = width

        @Cache

    def area(self):

    return
    self.length * self.width

    r = Rectangle(23)
    r.area()
    # TypeError: area() missing 1 required positional argument: 'self'

    Rectangle.area
    # <__main__.Cache object at 0x0000012D8E7A6D30>
    r.area
    # <__main__.Cache object at 0x0000012D8E7A6D30>

    回头再来看看没有装饰器的情况,Python在实例化对象后把函数变成了方法。

    class Rectangle:

    def __init__(self, length, width):

    self.length = length

    self.width = width


    def area(self):

    return
    self.length * self.width

    Rectangle.area
    # <function Rectangle.area at 0x0000012D8E7B28C8>
    r = Rectangle(23)
    r.area
    # <bound method Rectangle.area of <__main__.Rectangle object

    因此解决办法很简单,要用类写的装饰器来装饰类的方法,只需要把可调用对象包装成函数就行。

    # 定义一个简单的装饰器,什么也不做,仅仅是把可调用对象包装成函数
    def method(call):

    def wrapper(*args, **kwargs):

    return call(*args, **kwargs)

    return wrapper

    class Rectangle:

    def __init__(self, length, width):

    self.length = length

    self.width = width

        @method
        @Cache

    def area(self):

    return
    self.length * self.width

    r = Rectangle(23)
    r.area()
    # calculated
    # 6

    r.area()
    # cached
    # 6

    或者用@property还能直接把方法变成属性。

    class Rectangle:

    def __init__(self, length, width):

    self.length = length

    self.width = width

        @property
        @Cache

    def area(self):

    return
    self.length * self.width

    r = Rectangle(23)
    r.area
    # calculated
    # 6

    r.area
    # cached
    # 6

    总结

    用类写装饰器并非什么特别的技巧,一般情况下确实没必要这么写,不过这样就可以用一些类的特性来写装饰器,比如类的继承,也算是提供了另一种思路吧。

    最后注意:不管你是为了Python就业还是兴趣爱好,记住:项目开发经验永远是核心,如果你缺新项目练习或者没有python精讲教程,可以去小编的Python交流.裙 :七衣衣九七七巴而五(数字的谐音)转换下可以找到了,里面很多新教程项目,还可以跟老司机交流讨教!
    本文的文字及图片来源于网络加上自己的想法,仅供学习、交流使用,不具有任何商业用途,版权归原作者所有,如有问题请及时联系我们以作处理。

     

  • 相关阅读:
    ubuntu 制做samba
    《Programming WPF》翻译 第4章 前言
    《Programming WPF》翻译 第4章 3.绑定到数据列表
    《Programming WPF》翻译 第4章 4.数据源
    《Programming WPF》翻译 第5章 6.触发器
    《Programming WPF》翻译 第4章 2.数据绑定
    《Programming WPF》翻译 第4章 1.不使用数据绑定
    《Programming WPF》翻译 第5章 7.控件模板
    《Programming WPF》翻译 第5章 8.我们进行到哪里了?
    《Programming WPF》翻译 第5章 5.数据模板和样式
  • 原文地址:https://www.cnblogs.com/chengxuyuanaa/p/12581179.html
Copyright © 2020-2023  润新知