入门 2-装饰器

函数装饰器

import random
import time
from functools import wraps
def record_time(func):
@wraps(func)
def wrapper(*args, **kwargs):
start = time.time()
result = func(*args, **kwargs)
end = time.time()
print(f'{func.__name__}执行时间: {end - start:.3f}秒')
return result
return wrapper
@record_time
def download(filename):
print(f'开始下载{filename}.')
time.sleep(random.randint(2, 6))
print(f'{filename}下载完成.')
@record_time
def upload(filename):
print(f'开始上传{filename}.')
time.sleep(random.randint(4, 8))
print(f'{filename}上传完成.')
download('MySQL从删库到跑路.avi')
upload('Python从入门到住院.pdf')
# 取消装饰器
download.__wrapped__('MySQL必知必会.pdf')
upload = upload.__wrapped__
upload('Python从新手到大师.pdf')

内置装饰器

@staticmethod

@staticmethod 装饰器将类中的方法装饰为静态方法,不需要创建类的实例,可以通过类名直接引用

class MathOperations:
@staticmethod
def add(a, b):
return a + b
# 示例用法
result = MathOperations.add(5, 10)
print(result) # 输出: 15

@classmethod

@classmethod可以用来装饰实例方法和类方法

  1. 实例方法 (@classmethod):
  • 默认第一个参数是 self,指向类的实例。
  • 可以访问实例属性和方法。
  • 可以访问类属性和类方法。
  1. 类方法 (@classmethod):
  • 默认第一个参数是 cls,指向类本身。
  • 可以访问类属性和类方法,但不能访问实例属性。
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
@classmethod
def from_string(cls, person_str):
name, age = person_str.split(',')
return cls(name, int(age))
@classmethod
def echoName(self):
print(self.name)
# 使用类方法创建实例
person = Person.from_string("John,30")
print(person.name) # John
print(person.age) # 30

@property

@property 是一个装饰器,用于将一个方法转换为属性。这允许你通过属性的方式访问方法,类似于访问类的属性,而不需要显式地调用方法。这种做法可以让代码看起来更简洁和直观。

class Circle:
def __init__(self, radius):
self._radius = radius
@property
def radius(self):
return self._radius
@radius.setter
def radius(self, value):
if value < 0:
raise ValueError("Radius must be non-negative")
self._radius = value
@property
def area(self):
import math
return math.pi * (self._radius ** 2)
# 创建一个 Circle 实例
c = Circle(5)
print(c.radius) # 5
print(c.area) # 78.53981633974483
c.radius = 10 # 设置半径
print(c.area) # 314.1592653589793

@dataclass

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

@dataclass 是一种方便的工具,可以简化数据类的定义过程,使代码更简洁、更易读。通过 @dataclass,开发者可以专注于核心逻辑,而无需为基本的类方法编写样板代码。

from dataclasses import dataclass
@dataclass
class Person:
name: str
age: int
city: str = "Unknown"
# 创建实例
p1 = Person(name="Alice", age=30)
p2 = Person(name="Bob", age=25, city="New York")
print(p1) # 输出: Person(name='Alice', age=30, city='Unknown')
print(p2) # 输出: Person(name='Bob', age=25, city='New York')

不可变数据类可以通过设置 frozen=True 使数据类不可变:

@dataclass(frozen=True)
class Point:
x: int
y: int
p = Point(1, 2)
# p.x = 3 # 这行代码会引发错误,因为 p 是不可变的

使用 @dataclass 的注意事项

字段顺序:@dataclass 按字段定义的顺序生成 init 方法的参数。

字段顺序与默认值:定义字段时,有默认值的字段应放在无默认值的字段之后,否则会导致语法错误。

@dataclass
class Example:
x: int
y: int = 0 # 正确
# z: int = 1
# w: int # 错误,因为无默认值字段 `w` 在有默认值字段 `z` 之后

@lru_cache

@lru_cache 是 Python functools 模块中的一个装饰器,用于实现 Least Recently Used (LRU) 缓存。它可以缓存函数的结果,以避免重复计算,从而提高程序的性能,尤其是在有大量重复调用的场景下非常有用。

from functools import lru_cache
@lru_cache(maxsize=128)
def add(a, b):
sleep(10)
return a + b
sum = add(1,2)
print(f"{datetime.datetime.now()}:{sum}")# 2024-08-28 15:06:30.564882:3
sum = add(1, 2)
print(f"{datetime.datetime.now()}:{sum}")# 2024-08-28 15:06:30.564882:3
add.cache_clear()
sum = add(1, 2)
print(f"{datetime.datetime.now()}:{sum}")# 2024-08-28 15:06:40.575432:3

maxsize:设置缓存的最大容量,默认为 128。如果设置为 None,缓存大小将不受限制。

typed:默认为 False。如果为 add(3) 和 add(3.0) 将被视为不同的调用。

清除缓存

add.cache_clear()

@total_ordering

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

使用 @total_ordering,只需实现其中的一个(如 lt)和 eq 方法,Python 就能自动为你生成其他的比较方法。

from functools import total_ordering
@total_ordering
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
def __eq__(self, other):
return self.age == other.age
def __lt__(self, other):
return self.age < other.age
# 示例用法
p1 = Person("Alice", 30)
p2 = Person("Bob", 25)
print(p1 > p2) # True,因为 30 > 25
print(p1 <= p2) # False,因为 30 不小于等于 25
print(p1 == p2) # False,因为 30 != 25

虽然方便,但在某些性能敏感的场景中,手动实现所有比较方法可能更高效。

@cached_property

@cached_property 是 Python 3.8 引入的一个装饰器,用于将方法的结果缓存为属性。这意味着方法第一次被调用时,计算结果会被存储下来,以后对该属性的访问将直接使用缓存的结果,而不是重新计算。这对于计算成本高昂的属性或需要避免重复计算的场景非常有用。

from functools import cached_property
class ExpensiveComputation:
def __init__(self, value):
self.value = value
@cached_property
def expensive_result(self):
print("Computing result...")
# 假设这是一个昂贵的计算
result = self.value ** 2 # 示例计算
return result
# 创建实例
obj = ExpensiveComputation(10)
# 第一次访问属性,触发计算
print(obj.expensive_result) # Computing result... 100
# 再次访问属性,直接使用缓存的结果
print(obj.expensive_result) # 100

第三方装饰器

@atexit.register

来自 atexit 模块的 @register 装饰器可以让我们在 Python 解释器退出时执行一个函数。

这个装饰器对于执行最终任务非常有用,例如释放资源或只是说再见!

import atexit
@atexit.register
def destory():
print("Bye bye!")
print("Hello python!")