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",然后在打印名称后,感谢他们出席这一场合。
这个简单的示例说明,我们可以使用类装饰器轻松地对函数参数进行预处理和后处理。
这些预处理任务可以是以下任何一项,但不限于这些:
- 添加时间信息和打印调试信息
- 连接数据库
- 关闭连接资源
- 存储数据
作者: 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 三种方法可以用作装饰器:
- 只有@property表示 只读。
- 同时有@property和@x.setter表示 可读可写。
- 同时有@property和@x.setter和@x.deleter表示可读可写可删除。
- @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 种可能的方法类型:
- 实例方法:绑定到实例的方法。他们可以访问和修改实例数据。在类的实例上调用实例方法,它可以通过 self 参数访问实例数据。
- 类方法:绑定到类的方法。他们不能修改实例数据。在类本身上调用类方法,它接收类作为第一个参数,通常命名为 cls。
- 静态方法:未绑定到实例或类的方法。
实例方法可以定义为普通的 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 装饰器,我们在不同实例之间的比较结果都是正确的。
这个装饰器的好处是显而易见的:
- 它可以使您的代码更清晰并节省您的时间。因为你不需要写所有的比较方法。
- 一些旧类可能没有定义足够的比较方法。将 @total_ordering 装饰器添加到它以供进一步使用会更安全。
4. 作者信息
代码语言:txt复制作者: quantgalaxy@outlook.com
blog: https://blog.csdn.net/quant_galaxy
欢迎交流