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

2023-10-06 11:28:16 浏览数 (1)

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代码运行次数:0复制
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代码运行次数:0复制
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代码运行次数:0复制
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代码运行次数:0复制
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代码运行次数:0复制
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代码运行次数:0复制
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代码运行次数:0复制
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代码运行次数:0复制
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  
欢迎交流

0 人点赞