前置认识
'''
学习装饰器,python重要特性之一;用函数修改函数
'''
# 1 函数内定义函数:只能函数内部访问
def hi(name = 'greet'):
print('S1: Inside hi function!' )
def greet():
return 'now in greet function'
def hello():
return 'now in hello func!'
print(greet())
print(hello())
print('S4: Hi func end!')
if name == 'greet':
return greet
else:
return hello
hi()
#函数名赋值给另一个名字;删除它
talk = hi
talk()
# del hi
# talk()
# 2 函数返回函数, 函数当做参数传递给函数;
fun1 = hi()
fun2 = hi('hello')
print(fun1,fun2)
def acceptFunc(func):
print("Do sth. before hi func")
print(func()())
acceptFunc(hi) # 先自行Do sth before hi; 再自行hi()输出,最后返回了一个函数名再次被执行,故func()()
# 总结:函数可以嵌套函数、当做参数传值给另一个函数、当做返回值返回结果
# 3 第一个装饰器:利用 "learn hard", "finallay succeed!" wrap包装 hello decorator!
print(20 * '>>',"First decorator in python world")
def first_decorator(func):
def wrapFunc():
print("1st, I learn hard!")
func()
print("Finally succeed!")
return wrapFunc #一定要返回,内部包裹的函数,以后面形成嵌套 @结构
def hello():
print('Hello, decorator world! >>>>')
hello = first_decorator(hello)
hello() #这里的装饰器就好像辅助函数,帮助主体核心hello实现外在的“不重要”功能(开始、结束巴啦啦)
print("Same way using @", 10 * ">>")
@first_decorator
def hello2():
print('Hello, short @ decorator!')
hello2()
#我们在代码里并没有使用 @ 符号?那只是一个简短的方式来生成一个被装饰的函数。这里是我们如何使用 @ 来运行之前的代码:
#@a_new_decorator 是缩写:a_func_requre_decoration = a_new_decorator(a_func_require_decoration)
print(hello2.__name__) #因为缩写那句改为first_decorator, 里面又是warp函数覆盖的
print(15 * ">>", "通过functools的wraps函数还原被装饰的函数名,作为func.__name__名称")
from functools import wraps
def second_decorator(func):
@wraps(func) #名称进行命名反覆盖,装饰器;
def wrapfunc():
print("Second decorator, lucky!")
func()
print("Happy end!")
return wrapfunc
@second_decorator
def hello3():
print("Say hello too many times")
hello3()
print(hello3.__name__)
# @wraps接受一个函数来进行装饰,并加入了复制函数名称、注释文档、参数列表等等的功能。这可以让我们在装饰器里面访问在装饰之前的函数的属性。
蓝本、举例:授权、日志和类装饰器
# 4 装饰器常用场景之一:
# 蓝本
print('Give a decorator template!', 10 * '>>')
def decorater_name(f):
@wraps(f)
def wrapFunc():
if not Ctrl_flag:
return 'Will not run'
return f()
return wrapFunc
Ctrl_flag = True
@decorater_name
def hello4():
return ('I am typical decorator hello')
print(hello4())
Ctrl_flag = False
print(hello4()) # hello not funcs
# 5 授权authorization
# 八股的逻辑,检查如果未授权就开始授权; 如果授权,就返回状态说已经授权;
def decorator_author(func):
@wraps(func) #名称反覆盖、前面变量传入里面等作用
def wrapFunc(*args, **kwargs):
if not Author_Flag:
return Author()
return func(*args, **kwargs)#'Already authorized!'
return wrapFunc
def Author():
print("未授权:开始授权...,授权成功!")
@decorator_author
def authorFunc(*args, **kwargs):
ls = list(args)
di = kwargs
print("Users:")
[print(it) for it in args]
print("At places of:")
[print(k,'->', w) for (k,w) in kwargs.items()]
print("已经成功授权")
Author_Flag = True
authorFunc('fei','cui','peng', home=True, School = True)
Author_Flag = False
authorFunc()
# 6 日志
from functools import wraps
def logger(func):
@wraps(func)
def func_wrap(x):
print(func.__name__ + ' is called!')
return func(x)
return func_wrap
@logger
def loggingMy(x):
print('x square:', x ** 2)
loggingMy(10)
# 7 类做装饰器,__init__传初始参数,__call__做装饰器;
class logger(object):
def __init__(self, file = 'logging_file.txt'):
self.file = file
def __call__(self, func):
@wraps(func)
def wrapFunc(*args, **kwargs):
str = func.__name__
print(str + " is called!")
with open(self.file,'w') as f:
f.write(str)
return func(*args, **kwargs)
return wrapFunc
@logger()
def addArgs(*args, **kwargs):
s = sum(* args)
print(*kwargs, ' can count ', str(s))
addArgs([2,3,4], Me = 50)
额外知识:内置funtools中的wraps实现
# 8 装饰器内核:wraps实现
print(10 * "<<",'探索wraps实现')
def decoratorExample(func):
@wraps(func)
def wrapFunc(*args, **kwargs):
'''wrapFunc'''
print('wrapFunc')
return func(*args, **kwargs)
return wrapFunc
@decoratorExample
def hello6(*args):
'''Doc:hello 6'''
print('hello', [a for a in args])
hello6('peng','fei')
print(hello6.__name__, hello6.__doc__)
# 实际的wraps装饰器,就是一个三层套函数的装饰器,第一层传装饰器参量func;第二层传内核函数名fwrap;第三层传内核函数的参数列表args等
def wrapsEx(func): #传参数套函
def TmpWraps(fwrap): #实际嵌入函数套函
def wrapFunc(*args, **kwargs): #嵌入函数的传参数套函
wrapFunc.__name__ = func.__name__
wrapFunc.__doc__ = func.__doc__
return fwrap(*args, **kwargs)
return wrapFunc
return TmpWraps
def decoratorEx(func):
@wrapsEx(func) # func = wrapsEx(func) = TmpWraps
def wrapFunc(*args, **kwargs):
'''wrapFunc2'''
print('wrapFunc')
return func(*args, **kwargs) # TmpWraps() = wrapFunc
return wrapFunc
@decoratorEx
def hello7(*args): #hello7 = decoratorEx(hello7) = wrapFunc
'''doc:hello 7'''
print('hello', [a for a in args])
hello7('peng2', 'fei2') # = wrapFunc() = hello7()
print(hello7.__name__, hello7.__doc__)
output
<<<<<<<<<<<<<<<<<<<< 探索wraps实现
wrapFunc
hello [‘peng’, ‘fei’]
hello6 hello 6
wrapFunc
hello [‘peng2’, ‘fei2’]
hello7 hello 7
- 两个case: hello6和hello7输出完全一样;
- 说明 @wraps装饰器是一个典型的3层嵌套装饰器结构,通过(func)传入函数名,通过内部的wrapFunc内核函数,将wrapFunc.__name__和__doc__由wrapFunc内容,翻转为func函数内容
总结
- 装饰器使用函数来包裹函数(类包裹函数),完成一些统一的前置、后置处理操作,非常简洁、方便;自己成为一个体系;
- 并且随时修改内核的(被包裹函数)的内容、功能、方式等
- 重点使用注意:@wraps(func)用来反转name函数被覆盖弊端、传入之前函数的值、类型等
- 重点使用注意:内部定义def wrapFunc(*args),返回两次(一次可能是内部的func执行),另一个是必须的return wrapFunc,这是函数修饰decorator函数,修改函数的关键; 内部包裹,自我实现;核心成为装饰函数的一个部分,同时又返回给内核函数名称(甘愿成为配角,凸显、服务、只为核心函数)
- 类装饰器,适配python一切皆对象的理念,理所当然;并且通过__init__赋装饰函数值;通过__call__(func)来做正式装饰函数主体,整洁clean,易于理解。