首页 > 编程语言 >Python 装饰器模式:增强函数与类的优雅之道

Python 装饰器模式:增强函数与类的优雅之道

时间:2024-10-08 12:18:50浏览次数:10  
标签:__ function Python 优雅 之道 func print 装饰 def

在 Python 编程中,装饰器模式(Decorator Pattern)是一种强大且灵活的设计模式,它允许我们在不修改现有函数或类的结构的情况下,动态地添加额外的功能。这种模式遵循了开放 - 封闭原则,即软件实体(类、模块、函数等)应该对扩展开放,对修改封闭。本文将深入探讨 Python 中的装饰器模式,包括其概念、关键要点、实现方式、应用场景以及与其他相关概念的比较。

一、装饰器模式的概念

装饰器模式本质上是一种包装(Wrapper)机制,它将一个对象(在 Python 中通常是函数或类)包裹起来,在不改变原对象的接口(即调用方式)的前提下,为其添加新的功能或修改其行为。就像是给一个礼物(原对象)添加漂亮的包装纸(新功能),而礼物本身(原对象的功能)并没有改变,只是在外面添加了一些额外的东西。

二、关键要点

1. 可调用对象(Callable)

在 Python 中,装饰器模式主要围绕可调用对象展开。函数是最常见的可调用对象,但类也可以通过实现__call__方法成为可调用对象。装饰器本身也是可调用对象,它接受一个可调用对象作为参数,并返回一个经过包装后的可调用对象。

# 函数是可调用对象
def my_function():
    print("This is a function.")

my_function()

# 类实现__call__方法成为可调用对象
class MyCallableClass:
    def __call__(self):
        print("This is a callable class.")


obj = MyCallableClass()
obj()

2. 装饰器函数

装饰器函数是实现装饰器模式的核心工具。它接受一个函数作为参数,并在内部定义一个新的函数(闭包),新函数在调用原函数之前或之后添加额外的功能,然后返回这个新函数。

# 一个简单的装饰器函数示例
def my_decorator(func):
    def wrapper():
        print("Before function call.")
        func()
        print("After function call.")
    return wrapper


@my_decorator
def say_hello():
    print("Hello!")


say_hello()

3. 装饰器类

除了装饰器函数,我们还可以使用装饰器类来实现装饰器模式。装饰器类实现了__call__方法,在这个方法中,它可以在调用被装饰对象之前或之后执行额外的操作。

# 装饰器类示例
class MyDecoratorClass:
    def __init__(self, func):
        self.func = func

    def __call__(self):
        print("Before function call in class decorator.")
        self.func()
        print("After function call in class decorator.")


@MyDecoratorClass
def say_goodbye():
    print("Goodbye!")


say_goodbye()

4. 保留原函数的元信息

在使用装饰器时,一个重要的要点是要保留原函数的元信息,如函数名、文档字符串等。这对于代码的调试和文档生成非常重要。在 Python 中,我们可以使用functools.wraps装饰器来实现这一点。

import functools


def my_decorator(func):
    @functools.wraps(func)
    def wrapper():
        print("Before function call with meta - information preservation.")
        func()
        print("After function call with meta - information preservation.")
    return wrapper


@my_decorator
def important_function():
    """This is an important function."""
    print("Doing something important.")


print(important_function.__name__)
print(important_function.__doc__)

三、实现方式

1. 简单装饰器函数

如前面的示例所示,简单装饰器函数通过定义一个内部函数(闭包)来包装原函数,并在内部函数中添加额外的功能。

def log_decorator(func):
    def wrapper():
        print(f"Calling {func.__name__}...")
        result = func()
        print(f"{func.__name__} has been called.")
        return result
    return wrapper


@log_decorator
def simple_function():
    print("This is a simple function.")


simple_function()

2. 带参数的装饰器函数

有时候,我们需要给装饰器传递参数,以定制装饰器的行为。这可以通过在装饰器函数外部再嵌套一层函数来实现。

def repeat(n):
    def decorator(func):
        def wrapper():
            for _ in range(n):
                func()
        return wrapper
    return decorator


@repeat(3)
def print_message():
    print("Hello, world!")


print_message()

3. 装饰器类的实现

装饰器类的实现需要在类中定义__init__方法来接收被装饰的对象,然后在__call__方法中实现添加额外功能的逻辑。

class TimerDecorator:
    def __init__(self, func):
        self.func = func
        self.start_time = None

    def __call__(self):
        import time
        self.start_time = time.time()
        result = self.func()
        end_time = time.time()
        print(f"{self.func.__name__} took {end_time - self.start_time} seconds.")
        return result


@TimerDecorator
def long_running_function():
    import time
    time.sleep(2)


long_running_function()

四、应用场景

1. 日志记录

在大型应用程序中,日志记录是非常重要的功能。我们可以使用装饰器模式在函数调用前后添加日志记录的功能,记录函数的调用时间、参数、返回值等信息,而无需在每个函数内部编写大量的日志代码。

import functools


def log_function_call(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        print(f"Calling {func.__name__} with args={args}, kwargs={kwargs}")
        result = func(*args, **kwargs)
        print(f"{func.__name__} returned {result}")
        return result
    return wrapper


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


add(3, 5)

2. 权限验证

在 Web 应用或其他需要权限控制的系统中,装饰器模式可以用于验证用户是否具有访问某个函数或资源的权限。如果用户没有权限,装饰器可以阻止函数的执行并返回相应的错误信息。

def authorize(role):
    def decorator(func):
        def wrapper(*args, **kwargs):
            user_role = get_user_role()  # 假设这是一个获取用户角色的函数
            if user_role!= role:
                raise PermissionError("You do not have permission to access this function.")
            return func(*args, **kwargs)
        return wrapper
    return decorator


@authorize('admin')
def delete_user(user_id):
    # 执行删除用户的操作
    pass

3. 缓存机制

对于一些计算成本较高的函数,我们可以使用装饰器模式添加缓存机制。装饰器可以检查函数的输入参数,如果已经计算过相同参数的结果,则直接返回缓存中的结果,而无需再次执行函数。

def cache_decorator(func):
    cache = {}

    def wrapper(*args):
        if args in cache:
            return cache[args]
        result = func(*args)
        cache[args] = result
        return result
    return wrapper


@cache_decorator
def factorial(n):
    if n == 0 or n == 1:
        return 1
    else:
        return n * factorial(n - 1)

五、与其他相关概念的比较

1. 与继承的比较

  • 继承:继承是一种类与类之间的关系,子类继承父类的属性和方法。通过继承,子类可以获得父类的功能并在此基础上进行扩展。然而,继承是一种静态的关系,一旦类的继承关系确定,就很难在运行时改变。而且,过度使用继承可能会导致类的层次结构过于复杂,产生代码维护性差的问题。
  • 装饰器模式:装饰器模式是一种动态的功能扩展方式,它不依赖于类的继承关系。装饰器可以在运行时根据需要给对象添加功能,并且可以针对单个对象进行功能定制,而不会影响到其他对象。装饰器模式更加灵活,适用于在不修改现有代码结构的情况下进行功能扩展。

2. 与代理模式的比较

  • 代理模式:代理模式主要用于控制对对象的访问,代理对象和被代理对象通常实现相同的接口。代理对象可以在访问被代理对象之前或之后进行一些额外的操作,如权限验证、懒加载等。与装饰器模式类似,代理模式也是一种包装机制,但代理模式更侧重于对对象访问的控制,而装饰器模式侧重于给对象添加功能。

六、总结

Python 中的装饰器模式是一种非常有用的设计模式,它提供了一种灵活、动态的方式来给函数和类添加功能。通过理解装饰器模式的关键要点,如可调用对象、装饰器函数和类的实现方式以及保留原函数元信息的重要性,我们可以更好地运用装饰器模式。在日志记录、权限验证、缓存机制等众多应用场景中,装饰器模式都发挥着重要的作用。同时,与继承和代理模式的比较也有助于我们更深入地理解装饰器模式的特点和优势,从而在不同的编程场景中准确地选择合适的设计模式,提高代码的可维护性和可扩展性。

标签:__,function,Python,优雅,之道,func,print,装饰,def
From: https://blog.csdn.net/liuhailong0511/article/details/142634082

相关文章

  • Python 字符串基础知识
    字符串是计算机编程中非常重要的数据类型。在Python中,字符串是一系列字符的集合,可以包含字母、数字、符号和空格。Python提供了强大的字符串处理功能,使得操作字符串变得简单而直观。本文将深入探讨Python字符串的基本知识,包括字符串的创建、操作、常用方法以及字符串格式......
  • Python字符串打印格式
    一、旧式字符串格式化(%格式)在Python中,最早的字符串格式化方法是使用百分号(%)操作符。这种方式可以追溯到C语言,因此对于习惯于C语言的程序员来说是比较熟悉的。1.基本用法基本语法如下:name="Alice"age=30print("Mynameis%sandIam%dyearsold."%(name,age)......
  • Python 高级编程:深入解析 CSV 文件读取
    在Python中,读取CSV(逗号分隔值)文件是数据处理中的常见任务。以下将介绍一些高级的方法来读取CSV文件:使用pandas库读取CSV文件importpandasaspddf=pd.read_csv('file.csv')print(df)pandas是一个强大的数据处理库,read_csv函数可以方便地读取CSV文件并将其转换......
  • Python--暂停一秒输出
    在编程实践中,我们经常需要让程序在执行特定操作后暂停一段时间。Python中的time模块提供了一个简单而强大的sleep()函数,允许程序暂停指定的时间。本文将通过一个具体的例子,展示如何使用sleep()函数来实现每隔一秒输出一次当前时间的最后两位数字。一、导入time模块在Python中......
  • Python快速上手爬虫的7大技巧
    Python应用最多的场景还是Web快速开发、爬虫、自动化运维。爬虫在开发过程中也有很多复用的过程,这里总结一下,以后也能省些事情。   1、基本抓取网页    get方法    post方法   2、使用代理IP在开发爬虫过程中经常会遇到IP被封掉的情况,这时就需要用到代......
  • 如何优雅的摸鱼
      参考资料:探索Linux世界的趣味命令:解锁你的终端新玩法_linuxhollywood-CSDN博客一、前言在Linux的广阔世界里,隐藏着许多令人惊叹的工具和命令,一些工具和命令充满了趣味性和创意。今天让我们一起来看一下Linux命令行中有哪些既实用又有趣的命令吧!二、有趣的命令2.1 h......
  • Python 正则表达式高级应用指南
    正则表达式是一种强大的文本模式匹配工具,在Python中,我们可以使用re模块来进行正则表达式的操作。以下是一些高级的正则表达式应用示例:复杂的模式匹配importretext="Hello,[email protected]."email_pattern=r'\b[......
  • Python 高级编程:深入探索字符串切片
    在Python中,字符串切片是一种强大的操作,它允许我们从字符串中提取特定的部分。以下是关于Python字符串切片的高级教学: 基本的字符串切片string = "Hello, World!"# 提取从索引 7 到索引 11 的子串(不包括索引 11)substring = string[7:11]print(substring)......
  • 再谈如何优雅修改代码
    书接上回( https://www.cnblogs.com/OceanEyes/p/18450799)再做下扩展上文谈到:“基于抽象接口编程确实是最佳实践:把易于变动的功能点通过定义抽象接口的形式暴露出来,不同的实现做到隔离和扩展,这体现了开闭原则”publicclassFoo{  privateBarbar;  @Inject ......
  • 【重建虚拟环境】虚拟环境里python.exe被破坏了,对策
    虚拟环境里python.exe被破坏了,python.exe变成了0KB虚拟环境不能使用了。这个时候需要重建虚拟环境如果你重建虚拟环境,之前使用pipinstall安装的所有包确实会丢失,因为新的虚拟环境不会保留之前的包记录。不过,有一种简单的办法可以避免这个问题,并轻松恢复之前安装的包:如果你......