(9)100天python从入门到拿捏《装饰器》

装饰器

在 Python 中,装饰器(Decorator) 是一种非常强大的工具,它允许你在不修改函数本身的情况下,动态地增加或修改函数的行为。装饰器广泛应用于函数的增强、权限验证、日志记录、缓存、计时等场景。

1. 装饰器的基本概念

装饰器本质上是一个函数,它接受一个函数作为参数,并返回一个新的函数。返回的新函数通常是对原始函数的增强版,具有某些额外的功能。

装饰器通常用于“包装”函数或方法,使得对原始函数的调用可以自动添加一些功能。

语法:

def decorator(func):
    def wrapper():
        # 装饰器可以在这里增加代码
        print("Before function call")
        func()  # 调用原始函数
        print("After function call")
    return wrapper

2. 使用装饰器的基本例子

首先,我们定义一个简单的装饰器,来观察它如何增强原始函数。

案例:

# 定义一个装饰器
def my_decorator(func):
    def wrapper():
        print("Before function call")
        func()  # 调用原始函数
        print("After function call")
    return wrapper

# 使用装饰器
@my_decorator
def greet():
    print("Hello, world!")

greet()

输出:

Before function call
Hello, world!
After function call

说明:

  1. @my_decorator 是装饰器的简写,它相当于执行 greet = my_decorator(greet)
  2. 当调用 greet() 时,实际执行的是装饰器返回的 wrapper() 函数,而不是原始的 greet() 函数。
  3. wrapper() 中的 func() 调用原始的 greet() 函数,前后都增加了额外的行为(打印日志)。

3. 带参数的装饰器

大多数情况下,我们希望装饰器能够处理带有参数的函数。装饰器的内部 wrapper 函数需要能够接收并传递这些参数。

示例:

def my_decorator(func):
    def wrapper(*args, **kwargs):
        print("Before function call")
        result = func(*args, **kwargs)  # 调用原始函数并传递参数
        print("After function call")
        return result  # 返回原始函数的结果
    return wrapper

@my_decorator
def add(a, b):
    return a + b

print(add(3, 5))  # 输出:Before function call  8  After function call

说明:

  • *args**kwargs 允许 wrapper() 函数接收并传递任意数量的位置参数和关键字参数。
  • 装饰器通过传递这些参数来调用原始函数,并在调用前后增加自定义行为。

4. 带返回值的装饰器

装饰器不仅可以增强函数的行为,还可以控制函数的返回值。例如,在装饰器中修改原始函数的返回值。

案例:

def add_one_decorator(func):
    def wrapper(*args, **kwargs):
        result = func(*args, **kwargs)
        return result + 1  # 在原始函数返回值的基础上加 1
    return wrapper

@add_one_decorator
def add(a, b):
    return a + b

print(add(3, 5))  # 输出:9

说明:

  • 装饰器修改了 add() 函数的返回值,在其基础上增加了 1。

5. 使用装饰器的多个函数

你可以为多个函数使用相同的装饰器,也可以为一个函数叠加多个装饰器。多个装饰器按从下到上的顺序执行。

案例:

def decorator_one(func):
    def wrapper(*args, **kwargs):
        print("Decorator One Before")
        result = func(*args, **kwargs)
        print("Decorator One After")
        return result
    return wrapper

def decorator_two(func):
    def wrapper(*args, **kwargs):
        print("Decorator Two Before")
        result = func(*args, **kwargs)
        print("Decorator Two After")
        return result
    return wrapper

@decorator_one
@decorator_two
def greet(name):
    print(f"Hello {name}")

greet("Alice")

输出:

Decorator One Before
Decorator Two Before
Hello Alice
Decorator Two After
Decorator One After

说明:

  • greet 函数先被 decorator_two 包装,然后被 decorator_one 包装。两个装饰器按顺序执行,最内层的装饰器先执行。

6. 装饰器带参数

有时,我们希望装饰器能够接受参数,以便灵活地控制装饰器的行为。可以通过在装饰器外面再加一层函数来实现这一点。

案例:

def repeat(n):
    def decorator(func):
        def wrapper(*args, **kwargs):
            for _ in range(n):
                result = func(*args, **kwargs)
            return result
        return wrapper
    return decorator

@repeat(3)
def say_hello():
    print("Hello")

say_hello()  # 输出:Hello  Hello  Hello

说明:

  • 装饰器 repeat 接受参数 n,并返回一个新的装饰器。
  • 这个装饰器会调用原始函数 n 次。在此例中,say_hello 会被调用三次。

7. 内置装饰器

Python 提供了一些内置的装饰器,可以直接使用,最常见的有:

  • @staticmethod:将方法转换为静态方法。
  • @classmethod:将方法转换为类方法。
  • @property:将方法转换为属性(不需要显式调用)。
案例:使用 @staticmethod
class MyClass:
    @staticmethod
    def greet():
        print("Hello from static method")

MyClass.greet()  # 输出:Hello from static method
案例:使用 @classmethod
class MyClass:
    @classmethod
    def greet(cls):
        print(f"Hello from class method {cls}")

MyClass.greet()  # 输出:Hello from class method <class '__main__.MyClass'>
案例:使用 @property
class MyClass:
    def __init__(self, name):
        self._name = name

    @property
    def name(self):
        return self._name

    @name.setter
    def name(self, value):
        self._name = value

obj = MyClass("Alice")
print(obj.name)  # 输出:Alice
obj.name = "Bob"
print(obj.name)  # 输出:Bob

8. 函数的元数据

使用装饰器时,装饰器返回的新函数 wrapper 会覆盖原始函数的一些元数据(如函数名、文档字符串)。为了解决这个问题,可以使用 functools.wraps 来保持原始函数的元数据。

案例:

from functools import wraps

def my_decorator(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        print("Before function call")
        result = func(*args, **kwargs)
        print("After function call")
        return result
    return wrapper

@my_decorator
def greet():
    """This is a greet function."""
    print("Hello")

print(greet.__name__)  # 输出:greet
print(greet.__doc__)   # 输出:This is a greet function.

说明:

  • 使用 @wraps(func) 保留了 greet 函数的名称和文档字符串,避免了装饰器修改这些信息。

9. 装饰器的应用场景

装饰器的应用非常广泛,常见的应用场景包括:

  • 日志记录:在函数调用前后记录日志。
  • 权限校验:在执行某些操作前,验证用户权限。
  • 缓存:缓存函数的结果,以提高性能。
  • 计时:测量函数执行时间。
  • 输入校验:在函数执行前检查输入是否合法。

案例:计时装饰器

import time

def timer(func):
    def wrapper(*args, **kwargs):
        start_time = time.time()
        result = func(*args, **kwargs)
        end_time = time.time()
        print(f"Function {func.__name__} took {end_time - start_time} seconds to execute.")
        return result
    return wrapper

@timer
def long_running_function():
    time.sleep(2)

long_running_function()

输出:

Function long_running_function took 2.000123456 seconds to execute.

python学习专栏导航
(1)100天python从入门到拿捏《Python 3简介》
(2)100天python从入门到拿捏《python应用前景》
(3)100天python从入门到拿捏《数据类型》
(4)100天python从入门到拿捏《运算符》
(5)100天python从入门到拿捏《流程控制语句》
(6)100天python从入门到拿捏《推导式》
(7)100天python从入门到拿捏《迭代器和生成器》
(8)100天python从入门到拿捏《函数和匿名函数》

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

硅基工坊

你的打赏是对我最大的鼓励

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值