PYTHON装饰器详解

Python装饰器的作用

用于增强扩展Python函数或类功能的一种语法糖,类似与装饰者模式,还具备解耦的功能。
此语法糖在Python使用极其广泛,Python开发必备技能之一,常见使用于日志,性能检测,权限判断等。
以下用几个例子,由浅入深的了解装饰器。

Python装饰器的类型

  • 函数/类方法装饰器
  • 装饰器类(用类装饰一个函数)
  • 类的装饰器(用函数装饰器装饰一个类)

函数装饰器

函数装饰器分为两种,不带参数的与带参数的。

最简单的装饰器

# 例子1 一个最简单的装饰器
import time

# 自定义一个名为simple_decorator的装饰器
def simple_decorator(func):		     # func代表被装饰的函数
    def wrapper(*args, **kwargs):            # *args, **kwargs代表被装饰函数的参数
        return func()
    return wrapper

# 使用simple_decorator装饰器装饰doing函数
@simple_decorator
def doing():
    time.sleep(1)

doing()

不带参数的函数装饰器

# 例子2 在例子1的基础上增加对函数调用时间的输出功能
import time

def simple_decorator(func):
    def wrapper(*args, **kwargs):
        call_begin_time = time.time()
        func()
        cost_time = time.time() - call_begin_time
        print(f'调用{func.__name__}函数花费了:{cost_time}秒。')
    return wrapper

@simple_decorator
def doing():
    time.sleep(1)

doing()

==============================
输出结果:
调用doing函数花费了:1.012902021408081秒。
进程已结束,退出代码为 0

带参数的函数装饰器

# 例子3 带参数的函数装饰器需要套3层,我们在例子2的基础上做下修改。
import time

def calc_call_time(min_output_second):  # min_output_second是函数装饰器的参数
    def wrapper(func):                  # func代表被装饰的函数
        def deco(*args, **kwargs):      # *args, **kwargs代表被装饰函数的参数 
            call_begin_time = time.time()
            func(*args, **kwargs)
            cost_time = time.time() - call_begin_time
            if cost_time > min_output_second:
                print(f'调用{func.__name__}函数大于{min_output_second}秒,花费了: {cost_time}秒。')
            else:
                print(f'调用{func.__name__}函数小于{min_output_second}秒,花费了: {cost_time}秒。')
        return deco
    return wrapper

@calc_call_time(min_output_second=2)
def doing(sleep_time):
    time.sleep(sleep_time)

doing(1)
doing(2)

==============================
输出结果:
调用doing函数小于2秒,花费了: 1.0076000690460205秒。
调用doing函数大于2秒,花费了: 2.0079410076141357秒。

进程已结束,退出代码为 0

类方法装饰器

# 例子4 依然实现调用计时功能,整体与普通函数装饰器区别不大,差别在于需要将类方法的self进行传递。
import time

def calc_call_time(min_output_second):
    def wrapper(func):
        def deco(instance, *args, **kwargs):  # instance对象实例(即装饰类方法的self)
            call_begin_time = time.time()
            func(instance, *args, **kwargs)
            cost_time = time.time() - call_begin_time
            if cost_time > min_output_second:
                print(f'调用{func.__name__}函数大于{min_output_second}秒,花费了: {cost_time}秒。')
            else:
                print(f'调用{func.__name__}函数小于{min_output_second}秒,花费了: {cost_time}秒。')
        return deco
    return wrapper

class Test(object):
    @calc_call_time(min_output_second=1)
    def doing(self):
        time.sleep(1)

Test().doing()

==============================
输出结果:
调用doing函数大于1秒,花费了: 1.0066580772399902秒。

进程已结束,退出代码为 0

装饰器类(用类装饰一个函数)

装饰器类有两种写法,分别用于不同的场景。分别是可调用对象与全局对象。
可调用对象一般用于单个装饰器代码较为复杂的情况,需要独立成一个类来进行编写。
全局对象则用于需要管理器对各种变量进行缓存维护的场景。
在下面的例子中可以体会到这两者的不同。

可调用对象

# 例子5 这种装饰器类需要实现__init__与__call__两个魔术方法
#       __init__用于传参,__call__回调函数

import time

class TimeCalcDecorator(object):
    def __init__(self, min_output_second):
        self.min_output_second = min_output_second

    def __call__(self, func):
        def wrapper(*args, **kwargs):
            call_begin_time = time.time()
            func(*args, **kwargs)
            cost_time = time.time()-call_begin_time
            if cost_time > self.min_output_second:
                print(f'调用{func.__name__}函数大于'
                      f'{self.min_output_second}秒,花费了: {cost_time}秒。')
            else:
                print(f'调用{func.__name__}函数小于'
                      f'{self.min_output_second}秒,花费了: {cost_time}秒。')
        return wrapper

@TimeCalcDecorator(min_output_second = 1)
def doing():
    time.sleep(1)

doing()

==============================
输出结果:
调用doing函数大于1秒,花费了: 1.0096430778503418秒。

进程已结束,退出代码为 0

全局对象

# Flask框架的默认代码,极佳的展示了这种写法的优点
app = Flask(__name__)
@app.route('/')
def hello_world():
  return 'This Index Page'
# 例子6 使用装饰器类实现函数计时功能,并统计调用次数与调用时间。
import time

class CallFuncTimeTool(object):
    def __init__(self):
        self.call_func_total_second = 0   # 函数调用总时间
        self.call_func_total_count = 0    # 函数调用总次数

    def calc_call_time(self, min_output_second):
        def wrapper(func):
            def deco(*args, **kwargs):
                call_begin_time = time.time()
                func(*args, **kwargs)
                cost_time = time.time() - call_begin_time
                self.call_func_total_count += 1           # 新增代码,累计调用次数
                self.call_func_total_second += cost_time  # 新增代码,累计调用时间
                if cost_time > min_output_second:
                    print(f'调用{func.__name__}函数花费了: {cost_time}秒。')
            return deco
        return wrapper

    def output_info(self):
        """输出累计的调用次数与累计的调用时间"""
        print(f'总花费{self.call_func_total_second}秒'
              f'总调用了{self.call_func_total_count}次')


tool = CallFuncTimeTool()
@tool.calc_call_time(1)
def doing():
    time.sleep(1)


doing()
doing()
doing()
tool.output_info()

==============================
输出结果:
调用doing函数花费了: 1.0121991634368896秒。
调用doing函数花费了: 1.0110533237457275秒。
调用doing函数花费了: 1.0152339935302734秒。
总花费3.0384864807128906秒总调用了3次

进程已结束,退出代码为 0

类的装饰器(用函数装饰一个类)

类装饰器用于对现有类修改功能或新增功能,例如对一个类添加额外的属性与方法。
该装饰器将会在类实例化时进行调用。

# 例子7 以下
instances = {}

def singleton(cls):
    def get_instance(*args, **kwargs):
        cls_name = cls.__name__
        if not cls_name in instances:
            instance = cls(*args, **kwargs)
            instances[cls_name] = instance
        return instances[cls_name]
    return get_instance

@singleton
class User:
    pass

print(id(User()))
print(id(User()))
print(id(User()))

==============================
输出结果:
1887626427016
1887626427016
1887626427016

进程已结束,退出代码为 0

你可能感兴趣的:(python)