Python 装饰器总结

时间:2023-03-09 14:36:23
Python 装饰器总结

装饰器总结

前提

使用装饰器的前提在于Python提供的特性:

  1. 函数即对象,可以进行传递;
  2. 函数可以被定义在另外一个函数中;

可以通过一个例子来了解:

def get_animal(name='dog'):
def dog():
return 'this is a dog'
def cat():
return 'this is a cat'
# 返回函数对象
if name == 'dog':
return dog
elif name == 'cat':
return cat
else:
return 'other animal'
animal = get_animal('cat')
print(animal) # <function get_animal.<locals>.cat at 0x104b30d90>
print(animal())

注意:其返回的并不是调用函数,而是返回函数对象,只有在函数对象后面加上括号才表明要进行调用对象。

装饰器

装饰器就是可以在原来函数的基础上增加其它的功能,而不改变原函数本身。

如何写一个装饰器呢,下面给出一个基础的模版:

def my_decorator(a_function_to_decorate):
def wapper_function(*args, **kwargs):
print('Before the function runs')
a_function_to_decorate(*args, **kwargs)
print('After the function runs')
return wapper_function
@my_decorator
def stand_function():
print('stand function runs')
stand_function()

执行结果为:

Before the function runsstand function runsAfter the function runs


如果要求原函数的信息不变,那么可以使用functools.wraps,其本身也是一个装饰器,作用是将原函数的名称、模块、文档字符串等拷贝到装饰器里面的fun函数中,例子如下:

# "functools" 可以改变这点
import functools
def bar(func):
@functools.wraps(func)
def wrapper():
return func()
return wrapper

需要注意的问题是:

  1. 如果有多个装饰器的时候,则由里到外装饰,也就是按照距离函数的位置谁越近越就被装饰。
  2. 在装饰器的基础上还可以进行装饰,比如装饰装饰器的装饰器。

如果我们想要使用类来作为装饰器,那么被装饰的函数会作为类的参数被传入到类中。

比如写一个登陆检查的例子:

class LoginCheck:
def __init__(self, f):
self._f = f
def __call__(self, *args):
Status = check_function()
if Status is True:
return self._f(*args)
else:
return alt_function()
def check_function():
return True
def alt_function():
return('Sorry - this is the forced behaviour')
@LoginCheck
def display_members_page():
print('This is the members page')
display_members_page()

过程:它在访问相应的页面的时候,会进行登陆与否的检查,成功则返回相应的信息,反之则提示。


最后写一个题目,写一个decorator,在函数开始和结束时增加log,并且统计函数执行时间,保留原函数名:

import logging, time
def create_logger():
logger = logging.getLogger("example_logger")
logger.setLevel(logging.INFO)
# create the logging file handler
handler = logging.FileHandler(r"./test.log")
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
handler.setFormatter(formatter)
# add handler to logger object
logger.addHandler(handler)
return logger
def exception(logger):
def decorator(func):
def wrapper(*args, **kwargs):
start = time.time()
try:
logger.info('function runs')
res = func(*args, **kwargs)
print('the function used time: {}'.format(time.time()-start))
logger.info('function stops')
return res
except:
# log the exception
err = "There was an exception in "
err += func.__name__
logger.exception(err)
raise Exception
return wrapper
return decorator
logger = create_logger()
@exception(logger=logger)
def foo(name='foo_function'):
print('i am {}'.format(name))
return True
print(foo())

运行结果如下:

i am foo_functionthe function used time: 0.0008778572082519531True

logger中的结果如下:

2017-08-15 03:25:59,234 - example_logger - INFO - function runs2017-08-15 03:25:59,235 - example_logger - INFO - function stops