python中的装饰器

什么是装饰器

@decorator
def target_func():
  ...

从字面理解, 装饰器就是用来装饰器目标函数target_func,既然是装饰,就不能修改目标函数执行,因此只能在目标函数target_func的首尾进行操作。因此装饰器适合用来做一些记录函数日志,统计函数执行时间等工作,这些功能在不同的函数中代码一致,因此可以将它抽象出来,统一来写。
装饰器本质也是函数,这个函数的参数是函数。

装饰器早期实现

装饰器的早期(python version < 2.4)实现如下,通过函数调用函数来实现装饰器的功能,但这些的不方便之处就是函数阅读不直观。

import time

def logging_time(func):
  start_time = time.time()
  func()
  end_time = time.time()
  print("the execution of the function is {} seconds".format(end_time-start_time))


def hello_func():
  print("hello")

logging_time(hello_func)

简单装饰器

利用“@装饰器函数”的语法来实现装饰器功能的示例:

import time
def logging_time(func):
  def wrapper():
    start_time = time.time()
    func()
    end_time = time.time()
    print("the execution of the function is {} seconds".format(end_time-start_time))
  return wrapper

@logging_time
def hello_func():
  print("hello")

hello_func()

相比于早期的写法,将计算时间的函数放在了hello_func的实现上,避免了调用者调用logging_time函数

带参数的装饰器

目标函数含有参数

目标函数千千万, 函数参数写不完, 怎么办?用“*args, **kwargs”。

import time
def logging_time(func):
  def wrapper(*args, **kwargs):
    start_time = time.time()
    func(*args, **kwargs)
    end_time = time.time()
    print("the execution of the function is {} seconds".format(end_time-start_time))
  return wrapper

@logging_time
def hello_func(caller="caller"):
  print("hello, {}".format(caller))

hello_func("Caller")

装饰器带有参数

@decorator(params_d)
def target_func(params_t):
  ...

上述的写法相当于下列这种调用`

decorator(params_d)(target_func(params_t))

拓展开来,当有多个装饰器时:

@decorator_2(params_2)
@decorator_1(params_1)
def target_func(params_t):
  ...

相当于下列这种调用:

decorator_2(params_2)(decorator_1(params_1)(target_func(params_t)))

examle:

import time
def logging_time(python_version="2"):
  def inner_func(func):
    print("python version is: {}".format(python_version))
    def wrapper(*args, **kwargs):
      start_time = time.time()
      func(*args, **kwargs)
      end_time = time.time()
      print("the execution of the function is {} seconds".format(end_time-start_time))
    return wrapper
  return inner_func


@logging_time(python_version="2.7")
def hello_func(caller="caller"):
  print("hello, {}".format(caller))

hello_func("Caller")

基于类实现的装饰器

类装饰器不带参数

装饰器函数其实是这样一个接口约束,它必须接受一个callable对象作为参数,然后返回一个callable对象。在Python中一般callable对象都是函数,但也有例外。只要某个对象重载了__call__()方法,那么这个对象就是callable的,因此如果类中重载类__call__函数,这个类也可以用来当作装饰器。

import time
class logging_time(object):
  def __init__(self, func):
    self.func = func

  def __call__(self, *args, **kwargs):
    start_time = time.time()
    self.func(*args, **kwargs)
    end_time = time.time()
    print("the execution of the function is {} seconds".format(end_time-start_time))

@logging_time
def hello_func(caller="caller"):
  print("hello, {}".format(caller))

hello_func("Caller")

类装饰器带有参数

@decorator_cls(params)
def target_func(params_t):
   ...

相当于decorator_cls(params)(target_func(params_t)), 第一个括号相当于调用类的__init__()函数, 第二个括号相当于调用类的__call__()函数,这样理解就清晰了。
example:

import time
class logging_time(object):
  def __init__(self, python_version):
    self.python_version = python_version

  def __call__(self, func):
    print("python version is: {}".format(self.python_version))
    def wrapper(*args, **kwargs):
      start_time = time.time()
      func(*args, **kwargs)
      end_time = time.time()
      print("the execution of the function is {} seconds".format(end_time-start_time))
    return wrapper

@logging_time(python_version="2.7")
def hello_func(caller="caller"):
  print("hello, {}".format(caller))

hello_func("Caller")

易错点:错误的函数签名和文档

如果在target_func函数中调用上面的case中如果打印hello_func, 打印的会是wrapper, 究其原因就是上面的的hello_func实际调用的是logging_time(hello_func)。
解决方案是调用functools中的wraps模块解决。
example:

import time
from functools import wraps
def logging_time(func):
  @wraps(func)
  def wrapper():
    start_time = time.time()
    func()
    end_time = time.time()
    print("the execution of the function {} is {} seconds".format(func.__name__, end_time-start_time))
  return wrapper

@logging_time
def hello_func():
  print("hello")

hello_func()
print hello_func.__name__ # hello_func
以下是一些常见的 Python 装饰器面试问题及其回答: 1. 什么是装饰器装饰器Python 中一种高级的语法结构,它允许在不修改原函数代码的情况下,对函数进行增强或修改。装饰器本质上是一个函数,它接收一个函数作为参数,并返回一个函数。 2. 装饰器有哪些应用场景? 装饰器可以用于实现各种功能,如: - 日志记录:记录函数的调用时间、输入参数、输出结果等信息。 - 计时器:统计函数的运行时间。 - 缓存:缓存函数的输出结果,避免重复计算。 - 权限验证:验证用户是否有权调用函数。 - 接口限流:限制函数的调用频率。 3. 装饰器的语法是什么? 装饰器的语法如下: ```python @decorator def func(): pass ``` 其中,`decorator` 是装饰器函数,`func` 是被装饰函数。装饰器函数可以在函数执行前后对函数进行修改或增强。如果需要传递参数,可以使用带参数装饰器,如: ```python @decorator(arg1, arg2, ...) def func(): pass ``` 4. 装饰器的执行顺序是怎样的? 多个装饰器会按照从下往上的顺序依次执行,即先执行最外层的装饰器,然后执行内层的装饰器,最后执行被装饰的函数。例如: ```python @a @b @c def func(): pass ``` 执行顺序为 `func = a(b(c(func)))`。 5. 装饰器中如何传递参数? 装饰器可以使用带参数的形式,例如: ```python def my_decorator(arg1, arg2): def decorator(func): def wrapper(*args, **kwargs): # 在这里对函数进行增强或修改 return func(*args, **kwargs) return wrapper return decorator @my_decorator(arg1, arg2) def my_func(): pass ``` 在这个例子中,`my_decorator` 是一个带参数装饰器,它接收两个参数 `arg1` 和 `arg2`。`decorator` 是一个不带参数装饰器函数,它接收一个函数作为参数,并返回一个函数。`wrapper` 是被返回的函数,它接收任意数量的位置参数和关键字参数,并在其中对函数进行增强或修改。最后,使用 `@my_decorator(arg1, arg2)` 语法将装饰器应用到函数上。 6. 装饰器中如何保留被装饰函数的元信息? 当使用装饰器时,有时候需要保留被装饰函数的元信息,例如函数名、文档字符串等。为了实现这个功能,可以使用 `functools.wraps` 装饰器,它可以将被装饰函数的元信息复制到装饰器函数中。例如: ```python import functools def my_decorator(func): @functools.wraps(func) def wrapper(*args, **kwargs): # 在这里对函数进行增强或修改 return func(*args, **kwargs) return wrapper @my_decorator def my_func(): """这是一个示例函数""" pass ``` 在这个例子中,使用 `@functools.wraps(func)` 语法将被装饰函数的元信息复制到 `wrapper` 函数中。这样,当调用 `help(my_func)` 时,可以看到函数的文档字符串和函数名。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值