python 装饰器的基本使用

作者:changhao 时间:2021-04-01 07:12:50 

知识点

  • 简单的装饰器

  • 带有参数的装饰器

  • 带有自定义参数的装饰器

  • 类装饰器

  • 装饰器嵌套

  • @functools.wrap装饰器使用

基础使用

简单的装饰器


def my_decorator(func):
 def wrapper():
   print('wrapper of decorator')
   func()
 return wrapper()

def test():
 print('test done.')

test = my_decorator(test)
test

输出:
wrapper of decorator
test done.

这段代码中,变量test指向了内部函数wrapper(), 而内部函数wrapper()中又会调用原函数test(),因此最后调用test()时,就会打印'wrapper of decorator' 然后输出 'test done.'

这里的函数my_decorator()就是一个装饰器,它把真正需要执行的函数test()包裹在其中,并且改变了它的行为,但是原函数test()不变。

上述代码在Python中更简单、更优雅的表示:


def my_decorator(func):
 def wrapper():
   print('wrapper of decorator')
   func()
 return wrapper()

@my_decorator
def test():
 print('test done.')

test

这里的@, 我们称为语法糖,@my_decorator就相当于前面的test=my_decorator(test)语句

如果程序中又其他函数需要类似装饰,只需要加上@decorator就可以,提高函数的重复利用和程序可读性

带有参数的装饰器


def args_decorator(func):
 def wrapper(*args, **kwargs):
   print('wrapper of decorator')
   func(*args, **kwargs)
 return wrapper

@args_decorator
def identity(name, message):
 print('identity done.')
 print(name, message)

identity('changhao', 'hello')

输出:
wrapper of decorator
identity done.
changhao hello

通常情况下,会把args和*kwargs,作为装饰器内部函数wrapper()的参数。 表示接受任意数量和类型的参数

带有自定义参数的装饰器

定义一个参数,表示装饰器内部函数被执行的次数,可以写成这个形式:


def repeat(num):
 def my_decorator(func):
   def wrapper(*args, **kwargs):
     for i in range(num):
       func(*args, **kwargs)
   return wrapper
 return my_decorator

@repeat(3)
def showname(message):
 print(message)

showname('changhao')

输出:
changhao
changhao
changhao

类装饰器

类也可以作装饰器,类装饰器主要依赖于函数 __call__每当调用一个示例时,函数__call__()就会被执行一次。


class Count:
 def __init__(self, func):
   self.func = func
   self.num_calls = 0

def __call__(self, *args, **kwargs):
   self.num_calls += 1
   print('num of calls is: {}'.format(self.num_calls))
   return self.func(*args, **kwargs)

@Count
def example():
 print('example done.')

example()
example()

输出:
num of calls is: 1
example done.
num of calls is: 2
example done.

这里定义了类Count,初始化时传入原函数func(),而__call__()函数表示让变量num_calls自增1,然后打印,并且调用原函数。因此我们第一次调用函数example()时,num_calls的值是1,而第一次调用时,值变成了2。

装饰器的嵌套


import functools
def my_decorator1(func):
 @functools.wraps(func)
 def wrapper(*args, **kwargs):
   print('execute decorator1')
   func(*args, **kwargs)
 return wrapper

def my_decorator2(func):
 @functools.wraps(func)
 def wrapper(*args, **kwargs):
   print('execute decorator2')
   func(*args, **kwargs)
 return wrapper

@my_decorator1
@my_decorator2
def test2(message):
 print(message)

test2('changhao')

输出:
execute decorator1
execute decorator2
changhao

类装饰器

类也可以作装饰器,类装饰器主要依赖于函数 __call__每当调用一个示例时,函数__call__()就会被执行一次。


class Count:
 def __init__(self, func):
   self.func = func
   self.num_calls = 0

def __call__(self, *args, **kwargs):
   self.num_calls += 1
   print('num of calls is: {}'.format(self.num_calls))
   return self.func(*args, **kwargs)

@Count
def example():
 print('example done.')

example()
example()

输出:
num of calls is: 1
example done.
num of calls is: 2
example done.

这里定义了类Count,初始化时传入原函数func(),而__call__()函数表示让变量num_calls自增1,然后打印,并且调用原函数。因此我们第一次调用函数example()时,num_calls的值是1,而第一次调用时,值变成了2。

装饰器的嵌套


import functools
def my_decorator1(func):
 @functools.wraps(func)
 def wrapper(*args, **kwargs):
   print('execute decorator1')
   func(*args, **kwargs)
 return wrapper

def my_decorator2(func):
 @functools.wraps(func)
 def wrapper(*args, **kwargs):
   print('execute decorator2')
   func(*args, **kwargs)
 return wrapper

@my_decorator1
@my_decorator2
def test2(message):
 print(message)

test2('changhao')

输出:
execute decorator1
execute decorator2
changhao

@functools.wrap装饰器使用


import functools
def my_decorator(func):
 @functools.wraps(func)
 def wrapper(*args, **kwargs):
   print('wrapper of decorator')
   func(*args, **kwargs)
   return wrapper

@my_decorator
def test3(message):
 print(message)

test3.__name__

输出
test3

通常使用内置的装饰器@functools.wrap,他会保留原函数的元信息(也就是将原函数的元信息,拷贝到对应的装饰器里)

装饰器用法实例

身份认证


import functools

def authenticate(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
 request = args[0]
 if check_user_logged_in(request):
  return func(*args, **kwargs)
   else:
  raise Exception('Authentication failed')
 return wrapper

@authenticate
def post_comment(request):
pass

这段代码中,定义了装饰器authenticate;而函数post_comment(),则表示发表用户对某篇文章的评论。每次调用这个函数前,都会检查用户是否处于登录状态,如果是登录状态,则允许这项操作;如果没有登录,则不允许。

日志记录


import time
import functools

def log_execution_time(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
 start = time.perf_counter()
 res = func(*args, **kwargs)
 end = time.perf_counter()
 print('{} took {} ms'.format(func.__name__, (end - start) * 1000))
 return wrapper

@log_execution_time
def calculate_similarity(times):
pass

这里装饰器log_execution_time记录某个函数的运行时间,并返回其执行结果。如果你想计算任何函数的执行时间,在这个函数上方加上@log_execution_time即可。

总结

所谓装饰器,其实就是通过装饰器函数,来修改原函数的一些功能,使得原函数不需要修改。

来源:https://segmentfault.com/a/1190000038959829

标签:python,装饰器
0
投稿

猜你喜欢

  • 如何区分MySQL的innodb_flush_log_at_trx_commit和sync_binlog

    2024-01-19 19:38:30
  • 详解JavaScript基本类型和引用类型

    2024-04-29 13:43:56
  • MySQL数据库中应当如何实施info()函数

    2008-11-27 15:04:00
  • Python translator使用实例

    2021-02-09 22:40:24
  •  Python错误与异常处理

    2022-10-28 01:10:33
  • MIME Type 引出的两难困境

    2011-06-14 09:45:41
  • Django框架表单操作实例分析

    2022-01-27 23:43:59
  • Mysql中SQL语句不使用索引的情况

    2024-01-28 04:19:57
  • Pytorch.nn.conv2d 过程验证方式(单,多通道卷积过程)

    2021-12-24 13:28:27
  • pygame实现简单五子棋游戏

    2022-03-22 08:50:57
  • python编写计算器功能

    2022-10-29 14:07:33
  • Python 基于Twisted框架的文件夹网络传输源码

    2021-03-19 03:01:00
  • python实现LBP方法提取图像纹理特征实现分类的步骤

    2023-05-24 02:12:27
  • python函数map()和partial()的知识点总结

    2023-10-04 14:58:11
  • php购物车实现方法

    2023-11-16 22:54:51
  • python文件读取失败怎么处理

    2023-05-06 17:14:46
  • python套接字流重定向实例汇总

    2022-04-15 07:53:41
  • MySQL日志维护策略汇总

    2024-01-26 18:11:53
  • tensorflow: variable的值与variable.read_value()的值区别详解

    2023-01-02 22:29:40
  • 使用PHP 5.0创建图形的巧妙方法

    2023-10-27 00:59:07
  • asp之家 网络编程 m.aspxhome.com