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

编程入门 行业动态 更新时间:2024-10-25 16:25:11

Python<a href=https://www.elefans.com/category/jswz/34/1768853.html style=专家编程系列: 4. 善用类装饰器(Python Class Decorators)"/>

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

0. 标题

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

作者: quantgalaxy@outlook   
blog:   
欢迎交流

1. 介绍

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

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

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

1.1 装饰器的概念

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

在 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 '+ citytypical_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)

1.2 Python的类装饰器

PEP3129 中引入了类装饰器。
这在社区中引起了很大的反响,因为社区更倾向于使用元类。
其主要目的是将装饰函数的能力扩展到类。

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

class Accolade:def __init__(self, function):self.function = functiondef __call__ (self, name):# Adding Excellency before namename = "Excellency " + nameself.function(name)# Saluting after the nameprint("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   
blog:   
欢迎交流

2. 一些内置的类装饰器

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

2.1 property

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

class Pencil:def __init__(self, count):self._counter = count@propertydef counter(self):return self._counter@counter.setterdef counter(self, count):self._counter = count@counter.getterdef counter(self):return self._counterHB = Pencil(100)
print (HB.counter)  # 加了@property后,可以用调用属性的形式来调用方法,后面不需要加()HB.counter = 20
print (HB.counter)

Output:

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。

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

from functools import cached_propertyclass Circle:def __init__(self, radius):self.radius = radius@cached_propertydef area(self):return 3.14 * self.radius ** 2circle = Circle(10)
print(circle.area)
# prints 314.0print(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 装饰器:

class Circle:def __init__(self, radius):self.radius = radius@classmethoddef from_diameter(cls, diameter):return cls(diameter / 2)@propertydef diameter(self):return self.radius * 2@diameter.setterdef diameter(self, diameter):self.radius = diameter / 2c = Circle.from_diameter(8)
print(c.radius)  # 4.0
print(c.diameter)  # 8.0

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

2.4 staticmethod

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

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

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

class Student:def __init__(self, first_name, last_name):self.first_name = first_nameself.last_name = last_nameself.nickname = Nonedef set_nickname(self, name):self.nickname = name@staticmethoddef suitable_age(age):return 6 <= age <= 70print(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 装饰器,它已经足够满足使用:

from dataclasses import dataclass@dataclass
class Point:x: floaty: floatpoint = Point(1.0, 2.0)
print(point)
# Point(x=1.0, y=2.0)

2.6 total_ordering

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

from functools import total_ordering@total_ordering
class Student:def __init__(self, name, grade):self.name = nameself.grade = gradedef __eq__(self, other):return self.grade == other.gradedef __lt__(self, other):return self.grade < other.gradestudent1 = 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. 作者信息

作者: quantgalaxy@outlook   
blog:   
欢迎交流

更多推荐

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

本文发布于:2024-03-09 05:36:38,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1724005.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:专家   系列   Python   Decorators   Class

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!