Python专家编程系列: 4. 善用类装饰器(Python Class Decorators)
作者: quantgalaxy@outlook.com
blog: https://blog.csdn.net/quant_galaxy
欢迎交流
Python是唯一有习语的语言。这增强了它的可读性,也许还有它的美感。装饰器遵循Python的禅宗,又名“Pythonic”方式。
装饰器从 Python 2.2 开始可用。PEP318增强了它们。
装饰器的初学者教程,参见Python装饰器(Python Decorator)介绍
装饰器(不要与装饰器模式混淆)是一种在不更改原始函数的情况下添加/更改函数行为的方法。
在 Python 中,装饰器是一种设计模式,允许您通过将函数包装在另一个函数中来修改函数的功能。
外部函数称为装饰器,它将原始函数作为参数并返回修改后的版本。
我们通过一个例子来看,在这里,我们声明一个调试装饰器。它有助于打印函数的输出,而不是添加print语句。
因为直接使用print语句会让代码变得冗余,删除起来也非常乏味。
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:
Debugging:
You are John from Los Angeles
在这里,我们在第 1-6 行定义了装饰器,并在第 8 行使用 @ 语法将其应用于函数 typical_crunching_function上。
起到的作用相当于: typical_crunching_function = debug(typical_crunching_function)
PEP3129 中引入了类装饰器。
这在社区中引起了很大的反响,因为社区更倾向于使用元类。
其主要目的是将装饰函数的能力扩展到类。
下面是一个类装饰器增强函数功能的示例。
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:
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
欢迎交流
下面展示一些内置的类装饰器,它们在python中非常常用。
该装饰器允许为类中的一个属性添加 setter 和 getter 函数。
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:
100
20
加了@property后,可以用调用属性的形式来调用方法,后面不需要加()。
property 有 getter, setter 和 deleter 三种方法可以用作装饰器:
Python 3.8 为 functool 模块引入了一个新的强大装饰器 @cached_property。
它可以将一个类的方法转换为一个属性,该属性的值计算一次,然后在实例的生命周期内作为普通属性缓存。
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进行重复计算。
在 Python 类中,有 3 种可能的方法类型:
实例方法可以定义为普通的 Python 函数,只要它的第一个参数是 self。
但是,要定义一个类方法,我们需要使用@classmethod 装饰器:
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 相当于变相实现类构造器。
如前所述,静态方法不绑定到实例或类。它们被包含在一个类中只是因为它们在逻辑上属于那个类。
静态方法通常用于执行一组相关任务(例如数学计算)的实用程序类。通过将相关函数组织到类中的静态方法中,我们的代码将变得更有条理,也更容易理解。
要定义一个静态方法,我们只需要使用@staticmethod 装饰器。让我们看一个例子:
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
@dataclass装饰器(Python 3.7引入)可以自动为一个类生成几个特殊的方法,如__init、__repr、__eq、__lt等。
因此,它可以为我们节省大量编写这些基本方法的时间。如果一个类主要用于存储数据,那么@dataclass 装饰器是最好的选择。
为了演示,下面的示例只定义了一个名为 Point 的类的两个数据字段,有了 @dataclass 装饰器,它已经足够满足使用:
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)
functools 模块中的 @total_ordering 装饰器用于根据定义的方法为 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 装饰器,我们在不同实例之间的比较结果都是正确的。
这个装饰器的好处是显而易见的:
作者: quantgalaxy@outlook.com
blog: https://blog.csdn.net/quant_galaxy
欢迎交流
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。