#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""装饰器"""
###### 第一波 ######
def foo():
print('foo')
print('函数对象,表示foo这个函数', foo)
foo() # 调用foo函数
###### 第二波 ######
def foo():
print('foo')
def foo(x): return x + 1
print('foo函数被重定义了:', foo(1))
######## 基础平台部门提供的功能如下 ##########
def f1():
print('f1')
def f2():
print('f2')
def f3():
print('f3')
def f4():
print('f4')
######## 业务部门A 调用基础平台部门提供的功能 ##########
f1()
f2()
f3()
f4()
######## 业务部门B 调用基础平台部门提供的功能 ##########
f1()
f2()
f3()
f4()
# 问题:之前基础平台部门的开发人员在写代码的时候,没有考虑验证相关的问题,基础平台提供的功能可以被任何人使用。
# 现在需要对基础平台的所有功能进行重构,加入验证机制。即调用基础平台的功能时,先验证,再调用
# 解决方法:只对基础平台的代码进行重构,其他业务部门无需做任何修改
######## 基础平台部门提供的功能如下 ##########
def check_login():
# 验证1
# 验证2
# 验证3
pass
def f1():
check_login()
print('f1')
def f2():
check_login()
print('f2')
def f3():
check_login()
print('f3')
def f4():
check_login()
print('f4')
# 缺点: 写代码要遵循开放封闭的原则:即对扩展开发开放,对已实现的功能模块封闭;
# 改进:使用装饰器@w1----仅仅对基础平台的代码进行修改,其他业务部门的无需做任何操作
######## 修改后的基础平台部门提供的功能如下 ##########
def w1(func):
def inner():
# 验证1
# 验证2
# 验证3
return func
return inner
@w1
def f1():
print('f1')
@w1
def f2():
print('f2')
@w1
def f3():
print('f3')
@w1
def f4():
print('f4')
# 原理解释
"""单独以f1为例解释"""
def w1(func):
def inner():
# 验证1
# 验证2
# 验证3
return func
return inner
@w1
def f1():
print('f1')
# 解释@w1内部会执行以下操作:
"""
执行w1函数,并将@w1下面的函数作为w1函数的参数,即:@w1等价于w1(f1)。即:将原来的f1函数塞进另一个函数中
接着,内部会执行下面的语句:
def inner():
# 验证
return f1 此时的func等于f1
return inner
最后,将执行完的w1函数返回值赋值给@w1下面的函数的函数名
w1函数的返回值:
def inner:
# 验证
return 原来的f1()
然后,将此返回值再重新赋值给f1
新f1 = def inner():
# 验证
return 原来的f1()
"""
# 一个参数
def w1(func):
def inner(arg):
# 验证
return func(arg)
return inner
@w1
def f1(arg):
print('f1')
# 两个参数
def w1(func):
def inner(arg1, arg2):
# 验证
return func(arg1, arg2)
return inner
@w1
def f1(arg1, arg2):
print('f1')
# 三个参数
def w1(func):
def inner(arg1, arg2, arg3):
# 验证
return func(arg1, arg2, arg3)
return inner
@w1
def f1(arg1, arg2, arg3):
print('f1')
# 装饰具有n个参数的函数的装饰器
def w1(func):
def inner(*argc, **kwargs):
# 验证
return func(*argc, **kwargs)
return inner
@w1
def f1(arg1, arg2, arg3):
print('f1')
# 同一个函数可以被多个装饰器修饰
def w1(func):
def inner(*argc, **kwargs):
# 验证
return func(*argc, **kwargs)
return inner
def w2(func):
def inner(*argc, **kwargs):
# 验证
return func(*argc, **kwargs)
return inner
@w1
@w2
def f1(arg1, arg2, arg3):
print('f1')
# 装饰器实战
def now():
print('2018-07-13')
f = now
print('函数对象', type(f))
f()
print('函数对象的name属性', f.__name__)
# 下面增强now函数的功能-----在函数调用前后自动打印日志,但又不修改now()函数定义-----在代码运行期间动态增加功能的方式,称为装饰器
def log(func): # log()是一个装饰器,返回一个函数;原来的now函数仍然存在,但调用now函数相当于执行了一个新函数,即在log函数中返回的inner函数
def inner(*args, **kwargs):
print('调用%s():' % func.__name__)
return func(*args, **kwargs)
return inner
# 无参数装饰器
@log # 相当于log(now)
def now():
print('2018-07-13')
# 调用now函数
now()
# 如果装饰器函数本身需要传入一个参数,则函数结构更加复杂
# 带参数装饰器
def log(text):
def decorator(func):
def inner(*args, **kwargs):
print('%s %s():' % (text, func.__name__))
return func(*args, **kwargs)
return inner
return decorator
# 等价于log('execute')(now)-----首先执行 log('execute'),返回的是decorator函数,再调用返回的函数.参数是now函数,返回值最终是inner函数
@log('execute')
def now():
print('2018-07-13')
# 调用now()函数
now()
# 经过装饰器装饰后的函数对象的属性已经改变
print(now.__name__)
# 解决方法:functools.wraps
import functools
def log(func): # log()是一个装饰器,返回一个函数inner;原来的now函数仍然存在,但调用now函数相当于执行了一个新函数,即在log函数中返回的inner函数
@functools.wraps(func) # 此语句就是解决了由于原函数name属性复制到inner函数属性的问题,出现在返回函数定义之前
def inner(*args, **kwargs):
print('调用%s():' % func.__name__)
return func(*args, **kwargs)
return inner
@log
def now():
print('2018-07-13')
# 已解决: 经过装饰器装饰后的函数对象的属性已经改变
print(now.__name__)
def log(text):
def decorator(func):
@functools.wraps(func)
def inner(*args, **kwargs):
print('%s %s():' % (text, func.__name__))
return func(*args, **kwargs)
return inner
return decorator
# 等价于log('execute')(now)-----首先执行 log('execute'),返回的是decorator函数,再调用返回的函数.参数是now函数,返回值最终是inner函数
@log('execute')
def now():
print('2018-07-13')
now()
print(now.__name__)
# 练习
import time
import functools
def metric(func):
functools.wraps(func)
def wraper(*args, **kwargs):
start = time.time() # 执行前计时
func(*args, **kwargs) # 执行函数
end = time.time() # 执行后计时
print("%s executed in %s ms" % (func.__name__, 1000 * (end - start)))
return func(*args, **kwargs)
return wraper
@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)
print(f)
s = slow(11, 22, 33)
print(s)
# 测试过程
if f != 33:
print('测试失败!')
elif s != 7986:
print('测试失败!')
# 练习2
def log1(arg):
if not isinstance(arg, str):
func = arg
@functools.wraps(func)
def wrapper(*args, **kw):
print('begin call %s' % (func.__name__))
r = func(*args, **kw)
print('end call %s' % (func.__name__))
return r
return wrapper
else:
text = arg
def decorator(func):
@functools.wraps(func)
def wrapper(*args, **kw):
print('begin %s %s()' % (text, func.__name__))
r = func(*args, **kw)
print('end %s %s()' % (text, func.__name__))
return r
return wrapper
return decorator
@log1
def now1():
print('2018-07-13')
now1()
print('-----------------')
@log1('execute')
def now1():
print('2018-07-13')
now1()