import randomimport timefrom functools import wrapsdef 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 resultreturn wrapper@record_timedef download(filename):print(f'开始下载{filename}.')time.sleep(random.randint(2, 6))print(f'{filename}下载完成.')@record_timedef 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 装饰器将类中的方法装饰为静态方法,不需要创建类的实例,可以通过类名直接引用
class MathOperations:@staticmethoddef add(a, b):return a + b# 示例用法result = MathOperations.add(5, 10)print(result) # 输出: 15
@classmethod 可以用来装饰实例方法和类方法
class Person:def __init__(self, name, age):self.name = nameself.age = age@classmethoddef from_string(cls, person_str):name, age = person_str.split(',')return cls(name, int(age))@classmethoddef echoName(self):print(self.name)# 使用类方法创建实例person = Person.from_string("John,30")print(person.name) # Johnprint(person.age) # 30
@property 是一个装饰器,用于将一个方法转换为属性。这允许你通过属性的方式访问方法,类似于访问类的属性,而不需要显式地调用方法。这种做法可以让代码看起来更简洁和直观。
class Circle:def __init__(self, radius):self._radius = radius@propertydef radius(self):return self._radius@radius.setterdef radius(self, value):if value < 0:raise ValueError("Radius must be non-negative")self._radius = value@propertydef area(self):import mathreturn math.pi * (self._radius ** 2)# 创建一个 Circle 实例c = Circle(5)print(c.radius) # 5print(c.area) # 78.53981633974483c.radius = 10 # 设置半径print(c.area) # 314.1592653589793
@dataclass 装饰器(Python 3.7 引入)可以自动为一个类生成几个特殊的方法,如 init、repr、eq、lt 等。
@dataclass 是一种方便的工具,可以简化数据类的定义过程,使代码更简洁、更易读。通过 @dataclass,开发者可以专注于核心逻辑,而无需为基本的类方法编写样板代码。
from dataclasses import dataclass@dataclassclass Person:name: strage: intcity: 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: inty: intp = Point(1, 2)# p.x = 3 # 这行代码会引发错误,因为 p 是不可变的
使用 @dataclass 的注意事项
字段顺序:@dataclass 按字段定义的顺序生成 init 方法的参数。
字段顺序与默认值:定义字段时,有默认值的字段应放在无默认值的字段之后,否则会导致语法错误。
@dataclassclass Example:x: inty: int = 0 # 正确# z: int = 1# w: int # 错误,因为无默认值字段 `w` 在有默认值字段 `z` 之后
@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 + bsum = add(1,2)print(f"{datetime.datetime.now()}:{sum}")# 2024-08-28 15:06:30.564882:3sum = add(1, 2)print(f"{datetime.datetime.now()}:{sum}")# 2024-08-28 15:06:30.564882:3add.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()
functools 模块中的 @total_ordering 装饰器用于根据定义的方法为 Python 类生成缺少的比较方法。
使用 @total_ordering,只需实现其中的一个(如 lt)和 eq 方法,Python 就能自动为你生成其他的比较方法。
from functools import total_ordering@total_orderingclass Person:def __init__(self, name, age):self.name = nameself.age = agedef __eq__(self, other):return self.age == other.agedef __lt__(self, other):return self.age < other.age# 示例用法p1 = Person("Alice", 30)p2 = Person("Bob", 25)print(p1 > p2) # True,因为 30 > 25print(p1 <= p2) # False,因为 30 不小于等于 25print(p1 == p2) # False,因为 30 != 25
虽然方便,但在某些性能敏感的场景中,手动实现所有比较方法可能更高效。
@cached_property
是 Python 3.8 引入的一个装饰器,用于将方法的结果缓存为属性。这意味着方法第一次被调用时,计算结果会被存储下来,以后对该属性的访问将直接使用缓存的结果,而不是重新计算。这对于计算成本高昂的属性或需要避免重复计算的场景非常有用。
from functools import cached_propertyclass ExpensiveComputation:def __init__(self, value):self.value = value@cached_propertydef 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 装饰器可以让我们在 Python 解释器退出时执行一个函数。
这个装饰器对于执行最终任务非常有用,例如释放资源或只是说再见!
import atexit@atexit.registerdef destory():print("Bye bye!")print("Hello python!")
Python 提供一个单元测试库 unittest。
测试通过时输出 1 个 . 测试错误时输出 1 个 E 测试失败时输出 1 个 F
import unittestimport calc""""TestCalc 类继承自 unittest.TestCase 类专门用于测试 calc 模块中的函数每个测试方法都以 test_ 前缀开头"""class TestCalc(unittest.TestCase):"""测试 add 函数"""def test_add(self):self.assertEqual(calc.add(1, 2), 3)"""测试 add 函数"""def test_multi(self):self.assertEqual(calc.multi(1, 2), 2)@classmethoddef setUpClass(calc):print('所有测试用例开始前执行')@classmethoddef tearDownClass(calc):print('所有测试用例结束后执行')def setUp(self):print('单个测试用例开始前执行')def tearDown(self):print('单个测试用例结束后执行')"""unittest.main() 方法执行当前模块中所有测试用例"""if __name__ == '__main__':unittest.main()
除了测试所有模块外,也可以测试指定的模块/测试类/测试函数。
# 指定测试某个模块$ python -m unittest calc_test# 指定测试某个测试类$ python -m unittest calc_test.TestCalc# 指定测试某个测试类的方法$ python -m unittest calc_test.TestCalc.test_add
setUpClass/tearDownClass/setUp/tearDown 为测试钩子函数