函数装饰器
装饰器原理
python中一切皆对象
-
可以将函数的运行结果赋给另一个函数
def p1(name = "python"): return "p1 " + name print(p1()) # output:p1 python
-
可以将函数名赋给一个变量
greet = p1 print(greet()) # output:p1 python
-
尝试删除p1函数
del p1 print(p1())# 只是删除了函数p1的指向,但是函数对象本身并未删除。 # output:NameError print(greet()) # output:p1 python
如果一个函数对象没有任何对它的引用,并且它没有被其他对象所使用。在这种情况下,Python 的垃圾回收机制会自动处理函数对象的删除。
-
进阶:在函数中定义另一个函数
def auth(name = "python"): print("now you are in the auth()") def p1(): return "now you are in the p1()" def welcome(): return "now you are in the welcome()" print(p1()) print(welcome()) # 调用auth(),p1()和welcome()将会同时被调用,但是p1和welcome在auth之外无法访问 auth() # output: # now you are in the auth() # now you are in the p1() # now you are in the welcome() p1() # NameError: name 'p1' is not defined
-
高阶:函数也能返回函数
def auth(name): def p1(): return "now you are in the p1()" def welcome(): return "now you are in the welcome()" if name == "python": return p1 else: return welcome #注意:这里返回的是p1和welcome的函数体而不是p1(),welcome() print(auth("python")) # <function auth.<locals>.p1 at 0x000001BF61454B80> a = auth("python") b = auth("java") print(a()) # now you are in the p1() print(b()) # now you are in the welcome()
-
最后:函数可以作为参数传给另一个函数
def p1(name = "python"): return "p1 " + name def func(f): print("I am doing sth before p1()") print(f()) func(p1) # I am doing sth before p1() # p1 python
总结:创建函数func(f),在func()中定义inner(),在将inner赋值给hello
def func(f):
def inner():
print("I am doing something before f()")
f()# 将f()封装在一个内部函数中
print("I am doing something after f()")
return inner# func(f)返回内部函数体
def hello():
print("hello python")
# 将hello赋值给func的形参,func(hello)返回值inner赋给了hello
hello = func(hello)
hello()# 实际上是执行inner(), 是调用经过装饰后的函数
# output:
# I am doing something before f()
# hello python
# I am doing something after f()
在python中有一种简单的写法:将hello = func(hello)
改成如下形式:
@func #注释符号加函数名
def hello():
print("hello python")
hello()
# output:
# I am doing something before f()
# hello python
# I am doing something after f()
以上就是Python装饰器的原理
装饰器基础
又名装饰器函数
python提供声明装饰器的语法
@decorator
装饰是为函数和类指定管理或扩增代码的一种方式。装饰器本身采取可调用对象的形式(如函数),并处理其他可调用对象。
包含:
- 函数装饰器:装饰器提供一种方法,在函数和类定义语句结束时插入自动运行的代码—对于函数装饰器,在def语句中结束插入
- 类装饰器:在class语句结束时插入,这样代码可以扮演不同角色
原则:开闭原则
对扩展开放,对修改关闭
装饰器自身时一个返回可调用对象的可调用对象,也就是返回了一个对象,当通过其最初名称调用被装饰函数的时候,将会调用这个对象—它可以时拦截之后调用的一个包装器对象,也可以是以某种方式扩展的最初函数。(装饰器可以是任意类型的可调用对象,并且返回任意类型的可调用对象。
在不改变函数调用方式的前提下,实现身份的识别(扩展功能),需要使用装饰器(本质是闭包)
def hello(f):# 参数是一个函数对象
def wrapper():
print("hello world!")
f()# 调用函数
return wrapper
# @是语法糖
# @hello 等价于 func = hello(func) = wrapper
@hello
@hello
def func():
print("您好")
func()
# output:
# hello world!
# hello world!
# 您好
"""等效代码"""
func = hello(func)#用这行代码效果和@hello一样
func = hello(func)
func()
# output:
# hello world!
# hello world!
# 您好
特殊区别
#hello(func)() 等价于 @hello
# func()
@hello
def func():
print("您好")
func()
func()
# output:
# hello world!
# 您好
# hello world!
# 您好
"""等效代码"""
hello(func)()
hello(func)()
# output:
# hello world!
# 您好
# hello world!
# 您好
-
加装饰器前调用的是用原函数
-
加装饰器后调用的是wrapper函数
Demo
def hello(func):
def wrapper():
name = input("输入name: ")
pwd = input("输入pwd: ")
print("验证")
if name == "hzh" and pwd == "123456":
func()
print("登录成功")
else:
print("登录失败")
return wrapper
@hello
def login():
pass
login()
装饰器进阶
-
在inner函数中定义返回值
def func(p): def inner(): print("I am doing something before p()") result = p()# result接受p()返回值 print("I am doing something after p()") return result return inner @func def hello(): print("hello") return "world" print(hello()) # I am doing something before p() # hello # I am doing something after p() # world
对于有返回值的被装饰函数,要在内层把值返回,不然被装饰函数的工作就白做
-
在inner函数中定义参数
def func(p): def inner(arg):# 形参arg print("I am doing something before p()") result = p(arg)# result接受p(arg)返回值 print("I am doing something after p()") return result return inner @func def hello(name): print("hello") return name print(hello("python")) # I am doing something before p() # hello # I am doing something after p() # python
此外,万能参数可以解决p函数参数不一致问题
def func(p): def inner(*args, **kwargs): print("I am doing something before p()") result = p(*args, **kwargs) print("I am doing something after p()") return result return inner
-
@装饰器的多重嵌套
def f1(p): print("F1 外层1") def inner1(): print("F1 内层p()前") p() print("F1 内层p()后") return "inner1结束" print("F1 外层2") return inner1 def f2(p): print("F2 外层1") def inner2(): print("F2 内层p()前") p() print("F2 内层p()后") return "inner2结束" print("F2 外层2") return inner2 #先使用f2装饰hello,所以先调用f2里的外层方法,然后在使用f1装饰,所以再调用f1里的外层方法 @f1 # 总装饰器,装饰器inner2函数,函数名变成inner1 @f2 # 给hello改名inner2 # 相当于 hello = f1(f2(hello)) def hello(): print("p()在这!") print(hello()) # output: # F2 外层1 # F2 外层2 # F1 外层1 # F1 外层2 # F1 内层p()前 # F2 内层p()前 # p()在这! # F2 内层p()后 # F1 内层p()后 # inner1结束 print(hello.__name__)# inner1 证明先使用f1装饰,再使用f2装饰,所以函数名变成了f1的返回值
functools的wraps函数记录并加入了复制函数名称、注释文档、参数列表等功能。将
p.__name__
值改回inner装饰之前的值,解决函数被装饰后名称改变的问题from functools import wraps def func(p): # wraps函数先记录p函数未装饰之前的名称,p函数装饰后,再把p函数名称改回来 @wraps(p) def inner(*args, **kwargs): print("I am doing something before p()") result = p(*args, **kwargs) print("I am doing something after p()") return result return inner @func def hello(name): print("hello " + name) return "end" #未添加@wraps(p) print(hello.__name__) # inner # 添加@wraps(p)后 print(hello.__name__) # hello
分析可知:包裹在越内层的函数,被调用的时机就越晚,就和我们最上面简易的装饰器是一样的!
PS.记得在最内层要把res返回,不然在main运行的输出会打印出None -
装饰器加参数
def logging(level):# level是传给装饰器的参数 def out_wrapper(func):# func是被装饰的函数名 def in_wrapper(*args, **kwargs): print(f"{level}: enter {func.__name__}()") return func(*args, **kwargs) return in_wrapper return out_wrapper # 如果要给装饰器添加参数,就在装饰器外面再套一个函数。传入参数 @logging(level="INFO") # level是装饰器自己的参数 # 等价于logging = logging(level = "INFO") # @logging (即@out_wrapper) def hello(a, b, c): print(a, b, c) hello("hello", "my", "world") # INFO: enter hello() # hello my world print(hello.__name__)# in_wrapper
注意:只要我们使用了装饰器,那么不管这个函数是否被调用,装饰器外层的方法都会运行的!并且装饰了几个函数就会运行几次!!!
def func(p):
p("java1")
def inner(*args, **kwargs):
print("I am doing something before p()")
result = p(*args, **kwargs)
print("I am doing something after p()")
return result
p("java2")# 这个外层方法不管在inner之后还是之前,都会在inner之前先被运行,因为只有外层函数返回之后才调用inner函数
return inner
@func
def hello(name):
print("hello " + name)
return "end"
# 没有调用hello,但是只要使用了装饰器装饰器外层方法就会运行
if __name__ == '__main__':
pass
# output:
# hello java before
# hello java after
类装饰器
类也可以做装饰器,需要重写__call__
方法
__call__
方法让类对象以类名()的形式直接调用,以上三段代码等价。
class Deco(object):
def __init__(self):
self.name = "this is deco class"
def __call__(self, p):# call方法重写为装饰器
def wrapper(*args, **kwargs):
print("I am doing something before p()")
result = p(*args, **kwargs)
print("I am doing something after p()")
return result
return wrapper
@Deco() #注意要加(),相当于创建了一个类对象
def p1(name):
print("p1 is here")
return "I am " + name
print(p1("p1"))# 自动去调用类里面的call方法
# I am doing something before p()
# p1 is here
# I am doing something after p()
# I am p1
print(p1.__name__)
# wrapper
p1 = Deco().__call__(p1)
# 等于
p1 = Deco()(p1)
# 等于
@Deco()
def p1():