前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Python的装饰器

Python的装饰器

原创
作者头像
mariolu
发布2024-01-15 23:58:04
2010
发布2024-01-15 23:58:04
举报
文章被收录于专栏:Python实用主义CDN及云技术分享

一、装饰器(Decorator)的设计思路

在看python的装饰器机制之前。我们来研究一段python代码。

代码语言:python
代码运行次数:0
复制
def process():
    print("processing...")
    
process()

这段代码简单的执行了process()函数,process函数里面执行了一段print代码。

1.1嵌套写法

接着我们来拓展这个函数

代码语言:python
代码运行次数:0
复制
def decorator_fun(func): 
  print("Inside decorator") 
 
  def inner(*args, **kwargs): 
    print("Inside inner function") 
    print("Decorated the function") 
     
    func()
     
  return inner 
 

def process():
    print("processing...")

decorator_fun(process)() 

我们从追踪print日志,或者用python tutor来分析下这段程序的执行脉络

从Objects列可以看到实例化对象,从Frames列看到它们的执行顺序是:

(Global frame).decorator_func

--> (f1).decorator_fun --> (f1).decorator_fun.inner

--> (inner)

--> (f1).decorator_run.Return_value

-->(Global frame).process

也就是说执行(Global frame).process之前,我们可以插入一段proprocess代码。

1.2 decorator写法

我们把这个decorator_fun抽象出来,然后用一种以@开头的写法来注释这段process函数。decorator主干代码写法大概是这样

代码语言:python
代码运行次数:0
复制
def decorators(*args, **kwargs):
    def inner(func):
        '''
           do operations with func
        '''
        return func
    return inner #this is the fun_obj mentioned in the above content
 
@decorators(params)
def func():
    """
         function implementation
    """

使用@方式改写的process函数如下:

代码语言:python
代码运行次数:0
复制
def decorator_fun(func): 
  print("Inside decorator") 
 
  def inner(*args, **kwargs): 
    print("Inside inner function") 
    print("Decorated the function") 
     
    func()
     
  return inner 
 
@decorator_fun
def process():
    print("processing...")

process() 

这里从图中看到除了(Global frame).process指向了inner(*args, **kwargs) [parent=f],和以前的process指向不一样。其他都是一样的。就执行流程和结果来说,和之前的函数嵌套方式是一样的。

1.3 总结

到这里总结下,使用装饰器的写法,在每个实例化process的地方都不用修改代码。这在工程重构或者复用性有着独特的优势。

二、带参数的装饰器

我们再来升级下这个装饰器,让装饰器带上参数以完成更高级的事例。事例后面再讲,我们来说一下这种带参数的装饰器是怎么写的。

代码语言:python
代码运行次数:0
复制
def decorators(*args, **kwargs):
    def inner(func):
        '''
           do operations with func
        '''
        return func
    return inner #this is the fun_obj mentioned in the above content
 
@decorators(params)
def func():
    """
         function implementation
    """

我们在def decorator带上args和kwargs来携带tuple类型和dict类型的参数。在调用方这里加上一个小括号,放置参数。

因为这里@decorators(params)带上了小括号,所以这里会实例化这个decorators。decorators里面定义一个def inner(func),来表达Frame上的function。这个只有一层inner中间嵌套写法会导致 不用显示调用func就会 执行整套代码,包括func。为此,如果装饰器有需要携带params,我们会采用这一种写法:多层嵌套。

2.1 多层嵌套

代码语言:python
代码运行次数:0
复制
# Python code to illustrate
# Decorators with parameters in Python  (Multi-level Decorators)
 
def decodecorator(dataType, message1, message2):
    def decorator(fun):
        print(message1)
        def wrapper(*args, **kwargs):
            print(message2)
            if all([type(arg) == dataType for arg in args]):
                return fun(*args, **kwargs)
            return "Invalid Input"
        return wrapper
    return decorator
 
 
@decodecorator(str, "Decorator for 'stringJoin'", "stringJoin started ...")
def stringJoin(*args):
    st = ''
    for i in args:
        st += i
    return st

这里从日志输出看到第5行代码print(message2)并没有执行到。如果需要执行第5行代码进而执行第7行代码。需要这样做,加上print(stringJoin("I ",'like ',"tencent"))

代码语言:python
代码运行次数:0
复制
def decodecorator(dataType, message1, message2):
    def decorator(fun):
        print(message1)
        def wrapper(*args, **kwargs):
            print(message2)
            if all([type(arg) == dataType for arg in args]):
                return fun(*args, **kwargs)
            return "Invalid Input"
        return wrapper
    return decorator
 
 
@decodecorator(str, "Decorator for 'stringJoin'", "stringJoin started ...")
def stringJoin(*args):
    st = ''
    for i in args:
        st += i
    return st

print(stringJoin("I ", 'like ', "tencent"))

2.2 非decorator的等同写法

这段代码用非decorator的写法 来达到一样的执行顺序。则这样改

代码语言:python
代码运行次数:0
复制
def decodecorator(dataType, message1, message2):
    def decorator(fun):
        print(message1)
        def wrapper(*args, **kwargs):
            print(message2)
            if all([type(arg) == dataType for arg in args]):
                return fun(*args, **kwargs)
            return "Invalid Input"
        return wrapper
    return decorator

 
def stringJoin(*args):
    st = ''
    for i in args:
        st += i
    return st
 
print(decodecorator(str, "Decorator for 'stringJoin'", "stringJoin started ...")(stringJoin)("I ", 'like ', "tencent"))

我们使用python tutor查看Frame和Object如下。

三、实战

我们可以用decorator方便地实现很多场景

3.1 日志跟踪

代码语言:python
代码运行次数:0
复制

from functools import wraps

def log_args_and_response(log_level):
    def actual_decorator(func):
        @wraps(func)
        def log_func(*args, **kwargs):
            print("begin %s(%s, %s)" % (func.__name__, args, kwargs))
            res = func(*args, **kwargs)
            print("end %s(%s, %s)" % (func.__name__, args, kwargs))
            return res
        return log_func

    return actual_decorator

@log_args_and_response("debug")
def process():
    print("processing...")

process()

3.2 执行时间记录

代码语言:python
代码运行次数:0
复制
from collections import defaultdict
import time

call_times=defaultdict(int)
time_spent=defaultdict(int)
lastprint=time.time()

def timing_decorator2(*outer_args, **outer_kwargs):
    def decorator(func):
        print("++++++++")
        def wrapper(*args, **kwargs):
            global lastprint, call_times
            request_time = time.time()
            res = func(*args, **kwargs)
            response_time = time.time() - request_time
            warn_after = outer_kwargs.get('warn_after')
            if warn_after:
                if response_time > warn_after:
                    print('%s execution took %.3f (more than %.3f)' %
                            (func.__name__, response_time, warn_after))
            print("------")
        return wrapper
    return decorator

@timing_decorator2(warn_after=0.5, show_stats=True)
def example2_api():
    print("=======")
    time.sleep(10)
    pass

example2_api()

3.3 鉴权

代码语言:python
代码运行次数:0
复制
def caller(*allowed_callers, **kwargs):
    '''
    @caller
        check if services are allowed to call the decorated method

    Returns:
        The annotated function
    '''
    def make_check_allowed(method_name):
        def check_allowed(request):
            '''
            determine if the request is allowed or not
            '''
            return True
        #callers = get_caller(from_service, header)
        #is_omnipotent = caller in OMNIPOTENT_SERVICES
        #if is_omnipotent:
        #return check_allowed
        return True

    def callers_decorator(func):
        #func.check_allowed = make_check_allowed(func.__name__)
        func.allowed_callers = allowed_callers
        return func

    return callers_decorator

@caller("maintence")
def example_api():
    return True

example_api

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、装饰器(Decorator)的设计思路
    • 1.1嵌套写法
      • 1.2 decorator写法
        • 1.3 总结
        • 二、带参数的装饰器
          • 2.1 多层嵌套
            • 2.2 非decorator的等同写法
            • 三、实战
              • 3.1 日志跟踪
                • 3.2 执行时间记录
                  • 3.3 鉴权
                  领券
                  问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档