前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Python专家编程系列: 4. 善用类装饰器(Python Class Decorators)

Python专家编程系列: 4. 善用类装饰器(Python Class Decorators)

原创
作者头像
AIFEx
修改2023-10-06 11:28:16
2160
修改2023-10-06 11:28:16
举报
文章被收录于专栏:AIFEx的专栏AIFEx的专栏

0. 标题

Python专家编程系列: 4. 善用类装饰器(Python Class Decorators)

代码语言:txt
复制
作者: quantgalaxy@outlook.com   
blog: https://blog.csdn.net/quant_galaxy  
欢迎交流

1. 介绍

Python是唯一有习语的语言。这增强了它的可读性,也许还有它的美感。装饰器遵循Python的禅宗,又名“Pythonic”方式。

装饰器从 Python 2.2 开始可用。PEP318增强了它们。

装饰器的初学者教程,参见Python装饰器(Python Decorator)介绍

1.1 装饰器的概念

装饰器(不要与装饰器模式混淆)是一种在不更改原始函数的情况下添加/更改函数行为的方法。

在 Python 中,装饰器是一种设计模式,允许您通过将函数包装在另一个函数中来修改函数的功能。

外部函数称为装饰器,它将原始函数作为参数并返回修改后的版本。

我们通过一个例子来看,在这里,我们声明一个调试装饰器。它有助于打印函数的输出,而不是添加print语句。

因为直接使用print语句会让代码变得冗余,删除起来也非常乏味。

代码语言:python
复制
def debug(function):
    def wrapper(name, address):
    	print ('Debugging:')
    	func = function(name, address)
    	print (func)
    return wrapper


@debug
def typical_crunching_function(name, city):
	return 'You are '+ name + ' from '+ city


typical_crunching_function('John','Los Angeles')

Output:

代码语言:shell
复制
Debugging:
You are John from Los Angeles

在这里,我们在第 1-6 行定义了装饰器,并在第 8 行使用 @ 语法将其应用于函数 typical_crunching_function上。

起到的作用相当于: typical_crunching_function = debug(typical_crunching_function)

1.2 Python的类装饰器

PEP3129 中引入了类装饰器。

这在社区中引起了很大的反响,因为社区更倾向于使用元类。

其主要目的是将装饰函数的能力扩展到类。

下面是一个类装饰器增强函数功能的示例。

代码语言:python
复制
class Accolade:
    def __init__(self, function):
        self.function = function
    
  
    def __call__ (self, name):
        # Adding Excellency before name
        name = "Excellency " + name
        self.function(name)
        # Saluting after the name
        print("Thanks "+ name + " for gracing the occasion")


@Accolade
def simple_function(name):
    print (name)


simple_function('John McKinsey')

Output:

代码语言:shell
复制
Excellency John McKinsey
Thanks Excellency John McKinsey for gracing the occasion

这里定义了 Accolade 类,可用于对 simple_function 执行预处理和后处理。

在这个例子中,我们只需在字符串名称中添加 "name",然后在打印名称后,感谢他们出席这一场合。

这个简单的示例说明,我们可以使用类装饰器轻松地对函数参数进行预处理和后处理。

这些预处理任务可以是以下任何一项,但不限于这些:

  • 添加时间信息和打印调试信息
  • 连接数据库
  • 关闭连接资源
  • 存储数据
代码语言:txt
复制
作者: quantgalaxy@outlook.com   
blog: https://blog.csdn.net/quant_galaxy  
欢迎交流

2. 一些内置的类装饰器

下面展示一些内置的类装饰器,它们在python中非常常用。

2.1 property

该装饰器允许为类中的一个属性添加 setter 和 getter 函数。

代码语言:python
复制
class Pencil:
    def __init__(self, count):
        self._counter = count
  
    @property
    def counter(self):
        return self._counter
  
    @counter.setter
    def counter(self, count):
        self._counter = count

    @counter.getter
    def counter(self):
        return self._counter


HB = Pencil(100)
print (HB.counter)  # 加了@property后,可以用调用属性的形式来调用方法,后面不需要加()

HB.counter = 20
print (HB.counter)

Output:

代码语言:shell
复制
100
20

加了@property后,可以用调用属性的形式来调用方法,后面不需要加()。

property 有 getter, setter 和 deleter 三种方法可以用作装饰器:

  1. 只有@property表示 只读。
  2. 同时有@property和@x.setter表示 可读可写。
  3. 同时有@property和@x.setter和@x.deleter表示可读可写可删除。
  4. @getter装饰器用于定义类中的属性获取器。允许您在不使用括号的情况下访问属性的值。 如果需要定制读的函数,就需要写出来这个函数,如果只是直接访问变量,使用@property就可以了。

2.2 cached_property

Python 3.8 为 functool 模块引入了一个新的强大装饰器 @cached_property。

它可以将一个类的方法转换为一个属性,该属性的值计算一次,然后在实例的生命周期内作为普通属性缓存。

代码语言:python
复制
from functools import cached_property

class Circle:
    def __init__(self, radius):
        self.radius = radius

    @cached_property
    def area(self):
        return 3.14 * self.radius ** 2


circle = Circle(10)
print(circle.area)
# prints 314.0

print(circle.area)
# returns the cached result (314.0) directly

在上面的代码中,我们通过@cached_property 修饰了area方法。所以没有对同一个不变实例的circle.area进行重复计算。

2.3 classmethod

在 Python 类中,有 3 种可能的方法类型:

  1. 实例方法:绑定到实例的方法。他们可以访问和修改实例数据。在类的实例上调用实例方法,它可以通过 self 参数访问实例数据。
  2. 类方法:绑定到类的方法。他们不能修改实例数据。在类本身上调用类方法,它接收类作为第一个参数,通常命名为 cls。
  3. 静态方法:未绑定到实例或类的方法。

实例方法可以定义为普通的 Python 函数,只要它的第一个参数是 self。

但是,要定义一个类方法,我们需要使用@classmethod 装饰器:

代码语言:python
复制
class Circle:
    def __init__(self, radius):
        self.radius = radius

    @classmethod
    def from_diameter(cls, diameter):
        return cls(diameter / 2)

    @property
    def diameter(self):
        return self.radius * 2

    @diameter.setter
    def diameter(self, diameter):
        self.radius = diameter / 2


c = Circle.from_diameter(8)
print(c.radius)  # 4.0
print(c.diameter)  # 8.0

@classmethod 相当于变相实现类构造器。

2.4 staticmethod

如前所述,静态方法不绑定到实例或类。它们被包含在一个类中只是因为它们在逻辑上属于那个类。

静态方法通常用于执行一组相关任务(例如数学计算)的实用程序类。通过将相关函数组织到类中的静态方法中,我们的代码将变得更有条理,也更容易理解。

要定义一个静态方法,我们只需要使用@staticmethod 装饰器。让我们看一个例子:

代码语言:python
复制
class Student:
    def __init__(self, first_name, last_name):
        self.first_name = first_name
        self.last_name = last_name
        self.nickname = None

    def set_nickname(self, name):
        self.nickname = name

    @staticmethod
    def suitable_age(age):
        return 6 <= age <= 70


print(Student.suitable_age(99)) # False
print(Student.suitable_age(27)) # True
print(Student('yang', 'zhou').suitable_age(27)) # True

2.5 dataclass

@dataclass装饰器(Python 3.7引入)可以自动为一个类生成几个特殊的方法,如__init、__repr、__eq、__lt等。

因此,它可以为我们节省大量编写这些基本方法的时间。如果一个类主要用于存储数据,那么@dataclass 装饰器是最好的选择。

为了演示,下面的示例只定义了一个名为 Point 的类的两个数据字段,有了 @dataclass 装饰器,它已经足够满足使用:

代码语言:python
复制
from dataclasses import dataclass

@dataclass
class Point:
    x: float
    y: float

point = Point(1.0, 2.0)
print(point)
# Point(x=1.0, y=2.0)

2.6 total_ordering

functools 模块中的 @total_ordering 装饰器用于根据定义的方法为 Python 类生成缺少的比较方法。

代码语言:python
复制
from functools import total_ordering


@total_ordering
class Student:
    def __init__(self, name, grade):
        self.name = name
        self.grade = grade

    def __eq__(self, other):
        return self.grade == other.grade

    def __lt__(self, other):
        return self.grade < other.grade


student1 = Student("Alice", 85)
student2 = Student("Bob", 75)
student3 = Student("Charlie", 85)

print(student1 < student2)  # False
print(student1 > student2)  # True
print(student1 == student3)  # True
print(student1 <= student3) # True
print(student3 >= student2) # True

如上面的代码所示,在 Student 类中没有定义 __ge、__gt 和 __le__ 方法。

但是,有了 @total_ordering 装饰器,我们在不同实例之间的比较结果都是正确的。

这个装饰器的好处是显而易见的:

  1. 它可以使您的代码更清晰并节省您的时间。因为你不需要写所有的比较方法。
  2. 一些旧类可能没有定义足够的比较方法。将 @total_ordering 装饰器添加到它以供进一步使用会更安全。

4. 作者信息

代码语言:txt
复制
作者: quantgalaxy@outlook.com   
blog: https://blog.csdn.net/quant_galaxy  
欢迎交流

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 0. 标题
  • 1. 介绍
    • 1.1 装饰器的概念
      • 1.2 Python的类装饰器
      • 2. 一些内置的类装饰器
        • 2.1 property
          • 2.2 cached_property
            • 2.3 classmethod
              • 2.4 staticmethod
                • 2.5 dataclass
                  • 2.6 total_ordering
                  • 4. 作者信息
                  领券
                  问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档