首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >python装饰器学习笔记

python装饰器学习笔记

原创
作者头像
薛定谔的馋猫
修改2024-11-03 23:27:09
修改2024-11-03 23:27:09
14800
代码可运行
举报
运行总次数:0
代码可运行

一、装饰器的作用

装饰器用于修改或扩展函数或方法的行为。装饰器本质上是一个高阶函数,它可以接受一个或者多个函数作为参数,并返回一个新的函数。

二、装饰器举例

1、基本的装饰器

装饰器通常定义一个外层函数,该外层函数返回一个内层函数。内层函数则负责执行实际的装饰逻辑。

代码语言:txt
复制
def decorator(func):
    def wrapper():
        print('调用函数前加个打印功能1')
        func()
        print('调用函数后加个打印功能2')
    return wrapper


@decorator
def say_hello():
    print('say hello')


if __name__ == '__main__':
    say_hello()  

输出:

调用函数前加个打印功能1

say hello

调用函数后加个打印功能2

#装饰器的工作原理

(1)@decorator等价于 say_hello = my_decorator(say_hello)。

(2)调用过程:

say_hello 被传递给 my_decorator,my_decorator 返回 wrapper 函数。say_hello 现在指向 wrapper 函数。当调用 say_hello() 时,实际上是调用 wrapper()。

2、带参数的装饰器

如果希望装饰器本身需要能接受参数,这可以通过再嵌套一层函数来实现。

代码语言:javascript
代码运行次数:0
运行
复制
# def decorator(func):
#     def wrapper():
#         print('调用函数前加个打印功能1')
#         func()
#         print('调用函数后加个打印功能2')
#     return wrapper
#
#
# @decorator
# def say_hello():
#     print('say hello')

def for_func(num):
    def decorator(func):
        def wrapper(*args, **kwargs):
            for i in range(num):
                result = func(*args, **kwargs)
            return result
        return wrapper
    return decorator


@for_func(3)
def greet(name):
    print(f"hello,{name}")


if __name__ == '__main__':
    greet('wade')

输出:

hello,wade

hello,wade

hello,wade

3、保留元数据

使用装饰器后,原始函数的元数据(如名称、文档字符串等)会被覆盖。为了保留这些元数据,可以使用 functools.wraps。

(1)未使用装饰器前

代码语言:javascript
代码运行次数:0
运行
复制
def say_goodbye():
    """这是原函数"""
    print("python is the best language in the world")


print(say_goodbye.__name__)
print(say_goodbye.__doc__)

输出:

say_goodbye

这是原函数

(2)使用装饰器后

代码语言:javascript
代码运行次数:0
运行
复制
def decorator(func):
    def wrapper(*args, **kwargs):
        """这是装饰器内层函数"""
        print("调用原函数之前的操作")
        result = func(*args, **kwargs)
        print("调用原函数之后的操作")
        return result
    return wrapper


@decorator
def say_goodbye():
    """这是原函数"""
    print("python is the best language in the world")


print(say_goodbye.__name__)
print(say_goodbye.__doc__)

输出:

wrapper

这是装饰器内层函数

(3)import functools 后的效果

代码语言:javascript
代码运行次数:0
运行
复制
import functools


def decorator(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        """这是装饰器内层函数"""
        print("调用原函数之前的操作")
        result = func(*args, **kwargs)
        print("调用原函数之后的操作")
        return result
    return wrapper


@decorator
def say_goodbye():
    """这是原函数"""
    print("python is the best language in the world")


print(say_goodbye.__name__)
print(say_goodbye.__doc__)

输出:

say_goodbye

这是原函数

4、类装饰器

类装饰器就是使用类来实现的装饰器。它们通常通过在类中定义 __call__ 方法来实现。当我们使用 @ 语法应用装饰器时,Python 会调用装饰器类的 __init__ 方法创建一个实例,然后将被装饰的函数或类作为参数传递给 __init__ 方法。当被装饰的函数或方法被调用时,Python 会调用装饰器实例的 __call__ 方法

代码语言:javascript
代码运行次数:0
运行
复制
class MyDecorator:
    def __init__(self, func):
        self.func = func

    def __call__(self, *args, **kwargs):
        print("Before call")
        result = self.func(*args, **kwargs)
        print("After call")
        return result


@MyDecorator
def hello():
    print("Hello, world!")

输出:

Before call

Hello, world!

After call

在上述例子中,MyDecorator类的实例被创建并传入hello函数作为参数。当我们调用hello时,实际上是在调用MyDecorator实例的__call__方法。

5、多个装饰器

多个装饰器应用于同一个函数,装饰器的执行顺序是从上到下。

代码语言:javascript
代码运行次数:0
运行
复制
def decorator1(func):
    def wrapper():
        print("这是decorator 1装饰器")
        func()
    return wrapper


def decorator2(func):
    def wrapper():
        print("这是decorator 2装饰器")
        func()
    return wrapper


@decorator1
@decorator2
def say_goodbye():
    """这是原函数"""
    print("python is the best language")


say_goodbye()

输出:

这是decorator 1装饰器

这是decorator 2装饰器

python is the best language

三、装饰器实际应用演示

1、函数装饰器-日志打印

代码语言:txt
复制
import logging


# 配置日志
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')


def log_func_call(func):
    def wrapper(*args, **kwargs):
        logging.info(f"Calling {func.__name__} with args={args},kwargs={kwargs}")
        result = func(*args, **kwargs)
        logging.info(f"{func.__name__} returned {result}")
        return result
    return wrapper


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


# 调用函数
add(4, 5)

输出:

2024-11-03 23:10:18,295 - INFO - Calling add with args=(4, 5),kwargs={}

2024-11-03 23:10:18,295 - INFO - add returned 9

2、类装饰器-统计函数调用次数

代码语言:javascript
代码运行次数:0
运行
复制
class Counter:
    def __init__(self, func):
        self.func = func
        self.count = 0

    def __call__(self, *args, **kwargs):
        self.count += 1
        print(f"函数{self.func.__name__} 已经被调用{self.count}次")
        return self.func(*args, **kwargs)


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


add(3, 5)
add(4, 9)

输出:

函数add 已经被调用1次

函数add 已经被调用2次

四、总结

相比函数装饰器,类装饰器有几个主要优势。

  1. 更好的组织:类装饰器可以利用 Python 的面向对象特性,将相关的方法和数据封装在一起,这使得代码更易于理解和维护。
  2. 更大的灵活性:类装饰器可以利用继承来复用和扩展代码。例如,你可以创建一个基础的装饰器类,然后通过继承这个类来创建特定的装饰器。
  3. 更好的控制:类装饰器可以使用实例变量来保存状态。这在一些需要保存状态的装饰器(例如计数器或缓存)中非常有用。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、装饰器的作用
  • 二、装饰器举例
    • 1、基本的装饰器
    • 2、带参数的装饰器
    • 3、保留元数据
      • (1)未使用装饰器前
      • (2)使用装饰器后
      • (3)import functools 后的效果
    • 4、类装饰器
      • 5、多个装饰器
  • 三、装饰器实际应用演示
    • 1、函数装饰器-日志打印
    • 2、类装饰器-统计函数调用次数
  • 四、总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档