Python3 学习笔记24_函数式编程-装饰器_20180314

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# 学习网站:www.liaoxuefeng.com

#****************************************************
#       Python3 函数式编程-装饰器                   *
#****************************************************
print ("--------------------分割线------------------")

#============
# 装饰器Decorator
#============
# 函数对象赋值给变量,通过变量也能调用该函数。
def now():
    print('today is sunday')

fun1 = now
fun1()                                      # today is sunday

# 函数对象的__name__属性
print( now.__name__, fun1.__name__ )        # now now

'''
假设增强now()函数的功能,比如在函数调用前后自动打印日志,但又希望
修改now()函数的定义,这种在代码运行期间动态增加功能的方式,称为
装饰器(Decorator)

本质上,decorator就是一个返回函数的高阶函数,所以定义一个能打印日志的
decorator。
'''
def log(func):
    def wrapper(*args, **kw):
        print('call %s():' % func.__name__)
        return func(*args, **kw)
    return wrapper

# log()函数是一个decorator,所以接受一个函数作为参数,并返回一个函数
# 使用@语法,把decorator置于函数的定义处:

@log
def today():
    print('today is 2018-03-14')

today()
'''
函数执行结果:
call today():
today is 2018-03-14
'''

'''
把@log放到today()函数的定义处,相当于执行today = log(today)

由于log()是一个decorator,返回一个函数,所以原来的today函数仍然存在
只是现在同名的变量指向了新的函数,于是调用today()将执行新函数,即在
log()函数中返回的wrapper()函数

wrapper()函数的参数定义是(*args, **kw),因此wrapper()函数可以接受任意
参数的调用,在wrapper()函数内,先打印日志,再调用原始函数。
'''
print ("--------------------分割线------------------")

'''
如果decorator本身需要传入参数,那就需要编写一个返回decorator的高阶函数
'''
# 自定义Log的文本,3层嵌套的decorator
def Log(text):
    def decorator(func):
        def wrapper(*args, **kw):
            print('%s %s():' % (text, func.__name__))
            return func(*args, **kw)
        return wrapper
    return decorator

@Log('execute')
def hel():
    print('hello Bugliu')

hel()
'''
执行结果:
execute hel():
hello Bugliu
'''

'''
解析:
hel() 相当于 hel = log('execute')(now)
首先执行log('execute'),返回的是decorator函数,再调用返回的函数,
参数是now函数,返回值最终是wrapper函数。
'''
print ("--------------------分割线------------------")

print( hel.__name__ )                   # wrapper
# 经过decorator装饰之后,函数对象的__name__属性从原来的'hel'变成了
# ‘warpper’,因为返回的那个wrapper()函数名字就是'wrapper',所以需要把
# 原始函数的__name__属性复制到wrapper()函数中,否则,有些依赖函数签名
# 的代码执行就会出错

# 可以使用内置的functools.wraps,完整的decorator
import functools

def LOG(text):
    def decorator(func):
        @functools.wraps(func)      # 增加此行可解决属性问题
        def wrapper(*args, **kw):
            print('%s %s():' % (text, func.__name__))
            return func(*args, **kw)
        return wrapper
    return decorator

@LOG('正在执行')
def hel():
    print('hello Bugliu')

hel()
print( hel.__name__ )
'''
执行结果:
正在执行 hel():
hello Bugliu
hel
'''
print ("--------------------分割线------------------")

# 请设计一个decorator,它可作用于任何函数上,并打印该函数的执行时间:
import time
import functools

def metric(func):
    @functools.wraps(func)
    def wrapper(*args,**kw):
        startTime = time.time()
        test = func(*args,**kw)
        endTime = time.time()
        t = endTime - startTime
        print('executed in %s ms' % t)
        return test
    return wrapper

@metric
def fast(x, y):
    time.sleep(0.0012)
    return x + y;

@metric
def slow(x, y, z):
    time.sleep(0.1234)
    return x * y * z;

f = fast(11, 22)
s = slow(11, 22, 33)

if f != 33:
    print('测试失败!')
elif s != 7986:
    print('测试失败!')
'''
执行结果:
executed in 0.0025014877319335938 ms
executed in 0.1245872974395752 ms
'''
print ("--------------------分割线------------------")

"""
写出一个@log的decorator,使它既支持:
@log
def f():
    pass

又支持:
@log('execute')
def f():
    pass
"""

def log(arg):
    def inner_log(text='call'):
        def decorator(func):
            @functools.wraps(func)
            def wrapper(*args, **kw):
                print('%s %s():' % (text, func.__name__))
                return func(*args, **kw)
            return wrapper
        return decorator

    if callable(arg):
        return inner_log()(arg)
    return inner_log(arg)


@log
def f():
    pass

@log('execute')
def f2():
    pass

f()                                 # call f():
f2()                                # execute f2():
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值