Python技术迷

Python 中的装饰器是什么?

我们时常会遇到一个非常强大的功能——装饰器。

简单来说,装饰器是一种在不修改原函数代码的情况下,动态地为函数或方法添加功能的方式。

对于我们这些程序员来说,它就像是给现有功能加上“外壳”,在不影响原始功能的基础上增强它。

说到这里,大家可能会觉得:“哎,听起来很高大上,但到底怎么用?”别急,咱们一起来看看。

什么是装饰器?

在Python中,装饰器实际上是一个函数。这个函数可以接收另一个函数作为参数,并在不修改原始函数的情况下,返回一个新的函数。

它的主要作用就是在函数的基础上,动态地添加一些额外的功能,比如日志记录、权限校验、性能测试等。

装饰器的基本结构

让我们用最简单的例子来理解装饰器。假设我们有一个普通的函数:

defsay_hello():
    print("Hello, world!")

现在我们想在 say_hello 函数执行之前,打印一条日志。那么我们可以通过装饰器来实现这个需求。我们定义一个装饰器 log_decorator,它接受一个函数作为参数,并返回一个增强版的函数:

deflog_decorator(func):
defwrapper():
        print("Executing function...")
        func()
return wrapper

然后,我们就可以把 log_decorator 装饰器应用到 say_hello 函数上:

@log_decorator
defsay_hello():
    print("Hello, world!")

这种写法的意思是,@log_decorator 就相当于把 say_hello 函数传给了 log_decorator,并将装饰后的函数替换了原来的 say_hello。

如果我们执行 say_hello(),就会看到以下输出:

Executing function...
Hello, world!

如你所见,我们通过装饰器成功地为 say_hello 函数添加了日志打印的功能。

装饰器的原理

装饰器的原理很简单,核心就是函数的传递和返回。

在 Python 中,函数是第一类对象,也就是说,函数可以作为参数传递给另一个函数,也可以作为返回值返回。这就为装饰器的实现提供了基础。

  1. 函数作为参数:装饰器会把目标函数作为参数传递给包装函数。
  2. 包装函数:包装函数可以修改目标函数的行为,甚至可以在目标函数之前或之后执行额外的操作。
  3. 返回新的函数:装饰器返回一个新的函数,这个新函数会替代原来的目标函数。

带参数的装饰器

我们刚才演示的装饰器没有接收任何参数,但实际上,装饰器也可以接受参数。让我们看一个例子,创建一个接受参数的装饰器。

假设我们需要一个装饰器,记录函数执行的时间。为了让装饰器更灵活,我们想要指定日志的前缀。下面是一个带参数的装饰器:

import time

deflog_with_prefix(prefix):
defdecorator(func):
defwrapper(*args, **kwargs):
            print(f"{prefix} - Starting execution of {func.__name__}")
            start_time = time.time()
            result = func(*args, **kwargs)
            end_time = time.time()
            print(f"{prefix} - {func.__name__} executed in {end_time - start_time} seconds")
return result
return wrapper
return decorator

@log_with_prefix("INFO")
defmy_function():
    time.sleep(1)
    print("Function executed!")

my_function()

这里,log_with_prefix 是一个带参数的装饰器,它返回一个装饰器函数 decorator,这个函数接受目标函数 func,并返回包装后的 wrapper 函数。

在 wrapper 函数内部,我们记录了函数执行的时间,并输出日志。

执行时,输出会是:

INFO - Starting execution of my_function
Function executed!
INFO - my_function executed in 1.00012 seconds

通过这种方式,我们就可以在不改变函数本身的情况下,动态地为它添加日志功能,而且我们可以自由控制日志的前缀。

装饰器的链式应用

你还可以将多个装饰器应用到一个函数上,这种方式被称为装饰器的链式应用。

多个装饰器会按照从上到下的顺序应用。

比如我们可以同时为一个函数添加日志记录和执行时间统计:

@log_decorator
@log_with_prefix("DEBUG")
defmy_function():
    time.sleep(1)
    print("Function executed!")

my_function()

执行时,会首先打印出 DEBUG 日志,再打印 Executing function...,然后是函数的执行输出。

装饰器的应用场景

  • 日志记录:在函数执行前后,记录一些日志信息。
  • 性能监控:测量函数的执行时间,记录性能数据。
  • 权限检查:在函数执行前,检查用户是否有权限。
  • 缓存机制:为函数结果添加缓存,避免重复计算。
  • 函数计数:记录函数被调用的次数。

常见的装饰器

Python标准库提供了一些常用的装饰器,最常见的包括:

  • @staticmethod:用于定义静态方法。
  • @classmethod:用于定义类方法。
  • @property:用于定义只读属性。

面试题:装饰器的最优回答

问题:装饰器是什么?它是如何工作的?

最佳回答: 装饰器是Python中的一种设计模式,它本质上是一个函数,可以动态地为另一个函数添加功能,而无需修改被装饰函数的代码。

装饰器通常用于一些横切关注点,如日志记录、权限验证、性能监控等。

它的工作原理是:装饰器接收一个函数作为输入,返回一个新的函数。

这个新的函数通常会在执行被装饰函数之前或之后,添加一些额外的行为。

例如,我们可以使用装饰器在函数执行之前打印日志,或者在函数执行后计算时间。

装饰器可以是带参数的,也可以通过链式应用组合多个装饰器。

Python中的标准库也提供了几种常用的装饰器,如 @staticmethod 和 @classmethod。

装饰器的使用让代码更加简洁和灵活,并且使得我们能够遵循“开闭原则”(对扩展开放,对修改封闭)来编写更容易维护的代码。

对编程、职场感兴趣的同学,大家可以联系我微信:golang404,拉你进入“程序员交流群”。
🔥虎哥私藏精品 热门推荐🔥

虎哥作为一名老码农,整理了全网最全《python高级架构师资料合集》。

资料包含了《IDEA视频教程》、《最全python面试题库》、《最全项目实战源码及视频》及《毕业设计系统源码》,总量高达650GB,全部免费领取。