python函数装饰器用法分解示例

本文详细介绍了如何在Python中使用装饰器增加函数功能,如记录函数执行的开始和结束时间,以及处理带参数的装饰器。通过实例演示了装饰器的工作原理和不同形式的应用。
摘要由CSDN通过智能技术生成

我们将通过举例一个小函数,通过使用装饰器增加功能(实现方法不一定最好,这里仅仅是为了说明装饰器用法,不要钻牛角尖)。

1.最简单的函数

这是一个最简单的函数,打印这个计算的结果和开始结束时间(此处计算量很小,几乎同时完成,所以开始结束时间一样没有关系)

源码

# coding: utf-8
import time

def do():
    print 'start:', time.time()

    result = 1 + 1
    print 'result:', result

    print 'end:', time.time()

do()

输出

start: 1508143969.67
result: 2
end: 1508143969.67

2.使用装饰器增加功能,打印函数执行开始和结束时间

在python中,函数是可以作为参数传递给另一个函数的。装饰器其实就是以一个函数为参数的普通函数,然后又返回一个函数。 请注意此例的注释和执行结果

  1. 包装器接受一个函数作为参数,又返回一个新的函数
  2. 包装的过程是在程序启动时就执行的,直接返回了新函数替换了def处的函数,并非等到执行do()才替换该函数

源码

# coding: utf-8
import time


def print_run_time(func):   # 接受一个函数作为参数
    def wrapper():  # 构造的新的函数,用于返回替换函数func
        print 'start:', time.time()
        func()
        print 'end:', time.time()
    return wrapper  # 注意没有后缀(),因为要返回构造的新函数,而不是新函数调用结果


def do():
    result = 1 + 1
    print 'result:', result

print u'调用原始的do函数:'
do()

print u'根据装饰器的定义手动调用,用装饰器包装do函数:'
print_run_time(do)()

print u'根据装饰器的定义手动调用,用装饰器包装do函数(分步骤调用,思考整个调用过程和原理):'
new_do = print_run_time(do)     # 把do函数作为参数,注意do没有后缀()
print u'装饰器返回一个函数', type(new_do)
new_do()    # 调用装饰后的新函数

输出

调用原始的do函数:
result: 2
根据装饰器的定义手动调用,用装饰器包装do函数:
start: 1508145089.23
result: 2
end: 1508145089.23
根据装饰器的定义手动调用,用装饰器包装do函数(分步骤调用,思考整个调用过程和原理):
装饰器返回一个函数 <type 'function'>
start: 1508145089.23
result: 2
end: 1508145089.23

3.使用语法糖包装函数

@语法糖相当于new_do = print_run_time(do)

源码

# coding: utf-8
import time


def print_run_time(func):
    def wrapper():
        print 'start:', time.time()
        func()
        print 'end:', time.time()
    return wrapper


@print_run_time
def do():
    result = 1 + 1
    print 'result:', result

do()

输出

start: 1508145584.21
result: 2
end: 1508145584.21

4.包装带参数的函数

do函数改为需要输入x,y然后计算结果

源码

# coding: utf-8
import time


def print_run_time(func):
    def wrapper(x, y, *args, **kwargs):     # wrapper为构造的新函数,那么原来func的参数都被传递给了wrapper 
        print 'start:', time.time()
        func(x, y, *args, **kwargs)
        print 'end:', time.time()
    return wrapper


@print_run_time
def do(x, y):
    result = x + y
    print 'result:', result

do(1, 2)

输出

start: 1508145970.8
result: 3
end: 1508145970.8

5.带参数的装饰器

之前输出的时间是时间戳,现在我们要根据装饰器的参数来决定打印时间戳或者日期时间。 这就需要一个工厂函数(根据参数创建一个装饰器,然后再去包装原始的do函数),原理同以上将的装饰器,不再赘述,仔细对比代码变化即可

源码

# coding: utf-8
import time, datetime


def print_run_time(time_type):  # 多了这一层来根据参数创建一个装饰器
    def wrapper_factory(func):      # 根据参数构造的装饰器
        def wrapper(x, y, *args, **kwargs):
            if time_type == 'datetime':
                print 'start:', datetime.datetime.now()
                func(x, y, *args, **kwargs)
                print 'end:', datetime.datetime.now()
            else:
                print 'start:', time.time()
                func(x, y, *args, **kwargs)
                print 'end:', time.time()

        return wrapper
    return wrapper_factory


@print_run_time('datetime')
def do(x, y):
    result = x + y
    print 'result:', result

do(1, 2)

输出

start: 2017-10-16 17:37:25.286000
result: 3
end: 2017-10-16 17:37:25.286000

6.带参数的装饰器(使用类的__call__)

以上带参数装饰器使用工厂函数,多了一层更加使人迷惑,其实原理不变,这里把装饰器工厂函数用类来实现更加清晰明了

源码

# coding: utf-8
import time, datetime


class print_run_time:
    def __init__(self, time_type):  # 装饰器参数
        self.time_type = time_type

    # __call__使实例变为可调用对象,相当于一个函数
    def __call__(self, func):
        def wrapper(x, y, *args, **kwargs):
            if self.time_type == 'datetime':
                print 'start:', datetime.datetime.now()
                func(x, y, *args, **kwargs)
                print 'end:', datetime.datetime.now()
            else:
                print 'start:', time.time()
                func(x, y, *args, **kwargs)
                print 'end:', time.time()

        return wrapper


@print_run_time('datetime')
def do(x, y):
    result = x + y
    print 'result:', result


do(1, 2)

输出

start: 2017-10-16 17:45:04.094000
result: 3
end: 2017-10-16 17:45:04.094000


									

作者:扑棱扑棱

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

沙巴卡

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值