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"))
# .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 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():