目录
一、Python 装饰器:代码的魔法化妆师
(一)装饰器初体验
在 Python 的世界里,装饰器就像是一个神奇的化妆师,可以在不改变原有函数代码的基础上,为函数增添新的功能。比如,我们想在某个函数执行前后打印一些日志信息,来记录函数的执行情况,这时候装饰器就能派上用场啦!
下面是一个简单的示例:
def my_decorator(func):
def wrapper():
print("函数执行前,这是打印的日志信息。")
func()
print("函数执行后,这是打印的日志信息。")
return wrapper
@my_decorator
def say_hello():
print("Hello!")
say_hello()
在这个例子中,my_decorator就是一个装饰器函数,它接受一个函数func作为参数,并返回一个内部函数wrapper。在wrapper函数中,我们在调用原始函数func前后分别打印了日志信息。然后,我们使用@my_decorator语法糖将say_hello函数传递给装饰器进行装饰,这样当我们调用say_hello函数时,实际上调用的是经过装饰后的wrapper函数,也就实现了在函数前后打印日志的功能。
(二)装饰器的进阶应用
带参数的装饰器:有时候,我们希望装饰器能更加灵活,能够接受一些参数来定制其行为。比如,我们想让上面的日志装饰器可以打印不同级别的日志信息,就可以使用带参数的装饰器。
def log_decorator(level):
def outer(func):
def wrapper():
if level == "info":
print("INFO: 函数执行前")
elif level == "debug":
print("DEBUG: 函数执行前")
func()
if level == "info":
print("INFO: 函数执行后")
elif level == "debug":
print("DEBUG: 函数执行后")
return wrapper
return outer
@log_decorator(level="info")
def greet():
print("Hello, everyone!")
greet()
这里,log_decorator是一个带参数的装饰器,它接受一个参数level。最外层函数log_decorator返回一个内部函数outer,outer函数接受一个函数func作为参数,并返回最终的wrapper函数。通过传递不同的level参数,我们可以定制日志的级别。
类装饰器:除了函数装饰器,Python 还支持类装饰器。类装饰器是包含__call__方法的类,它接受一个函数作为参数,并返回一个新的函数。
class MyClassDecorator:
def __init__(self, func):
self.func = func
def __call__(self):
print("类装饰器在函数执行前执行")
self.func()
print("类装饰器在函数执行后执行")
@MyClassDecorator
def say_goodbye():
print("Goodbye!")
say_goodbye()
在这个例子中,MyClassDecorator类就是一个类装饰器。当我们使用@MyClassDecorator装饰say_goodbye函数时,实际上是创建了一个MyClassDecorator类的实例,并将say_goodbye函数作为参数传递给它的__init__方法。然后,当调用say_goodbye函数时,实际上调用的是类实例的__call__方法。
内置装饰器:Python 提供了一些非常实用的内置装饰器,比如@staticmethod、@classmethod和@property。
-
-
@staticmethod:将方法定义为静态方法,不需要实例化类即可调用,静态方法通常用于封装与类相关但不依赖于类实例的数据和逻辑。
-
class MathUtils:
@staticmethod
def add(x, y):
return x + y
result = MathUtils.add(3, 5)
print(result)
-
@classmethod:将方法定义为类方法,类方法的第一个参数是类本身(通常命名为cls),可以通过类或实例调用,通常用于创建工厂方法或修改类状态。
class Person:
population = 0
def __init__(self, name):
self.name = name
Person.population += 1
@classmethod
def get_population(cls):
return cls.population
person1 = Person("Alice")
person2 = Person("Bob")
print(Person.get_population())
-
@property:将方法转换为属性,使其可以像属性一样访问,通常用于实现属性的读取和设置,并且可以在设置属性时进行一些验证操作。
class Rectangle:
def __init__(self, width, height):
self._width = width
self._height = height
@property
def width(self):
return self._width
@width.setter
def width(self, value):
if value <= 0:
raise ValueError("Width must be positive")
self._width = value
@property
def height(self):
return self._height
@height.setter
def height(self, value):
if value <= 0:
raise ValueError("Height must be positive")
self._height = value
@property
def area(self):
return self._width * self._height
rect = Rectangle(4, 5)
print(rect.width)
print(rect.area)
rect.width = 10
print(rect.area)
(三)实战案例
缓存功能实现:在计算斐波那契数列时,使用递归方法计算的复杂度很高,如果我们多次计算相同的值,会导致大量的重复计算,进而影响程序性能。为了解决这个问题,我们可以采用缓存策略,使用装饰器来实现。
from functools import lru_cache
@lru_cache(maxsize=None)
def fibonacci(n):
if n < 2:
return n
return fibonacci(n - 1) + fibonacci(n - 2)
print(fibonacci(30))
这里,我们使用了functools模块中的lru_cache装饰器,它会自动缓存函数的计算结果。当再次以相同的参数调用fibonacci函数时,会直接从缓存中获取结果,而不需要重新计算,大大提高了计算效率。
日志记录功能:在一个 Web 应用中,我们可能需要记录每个函数的调用时间和返回值,以便进行调试和性能分析。
import time
import logging
def log_function_call(func):
def wrapper(*args, **kwargs):
start_time = time.time()
logging.basicConfig(level=logging.INFO)
logging.info(f"Function {func.__name__} is called with arguments: {args}, {kwargs}")
result = func(*args, **kwargs)
end_time = time.time()
elapsed_time = end_time - start_time
logging.info(f"Function {func.__name__} took {elapsed_time:.4f} seconds to run and return value: {result}")
return result
return wrapper
@log_function_call
def compute_sum(a, b):
time.sleep(1) # 模拟一个延迟
return a + b
compute_sum(3, 5)
在这个例子中,log_function_call装饰器记录了compute_sum函数的调用时间、参数和返回值,方便我们对函数的执行情况进行跟踪和分析。
二、Python 偏函数:函数的贴心助手
(一)什么是偏函数
在 Python 中,偏函数(Partial Function)是一个非常实用的概念,它允许我们固定一个函数的某些参数,从而生成一个新的函数。简单来说,偏函数就像是给函数穿上了一件 “定制外套”,这件外套帮我们固定了一些参数,让我们在调用函数时更加方便快捷。比如,我们有一个函数add(x, y)用于计算两个数的和,如果我们经常需要计算某个数加上 5 的结果,就可以通过偏函数将add函数的一个参数固定为 5,得到一个新的函数add_five(x),这样在调用时只需要传入一个参数x即可。偏函数的实现依赖于functools模块中的partial函数。