Python学习记录——装饰器
一.函数即对象
1.函数是最高级的对象(对象是类的实例化,可以调用相应的方法,函数是包含变量对象的对象)
2.只有函数加载到内存才可以被调用
3.函数满足的两个条件:
(1)可以赋值给其他变量
如f1=f2这种表达式合理
(2)其可以被定义在另外一个函数内(作为参数&作为返回值),类似于整形,字符串等对象
函数名作为参数
def foo(func):
print('foo')
func()
def bar():
print('bar')
foo(bar)
函数名作为返回值
def foo():
print('foo')
return bar
def bar():
print('bar')
b=foo()
b()
二.闭包
1.引入:闭包涉及到嵌套函数,通过在函数内部def的关键字再声明一个函数即为嵌套:
def outer():
x = 1
def inner():
print (x)
return inner
in_func=outer()
in_func()
不能直接赋值in_func=inner,因为没有执行outer之前inner并没有存入内存中,会出现找不到的错误
2.定义:如果在一个内部函数里,对在外部作用域(但不是在全局作用域)的变量进行引用,那么内部函数就被认为是闭包(closure)。即闭包=内部函数+定义函数时的环境。
3.解释:如上实例,inner就是内部函数,inner里引用了外部作用域的变量x(x在外部作用域outer里面,不是全局作用域),则这个内部函数inner就是一个闭包。
三.装饰器
1.意义:可以让其他函数在不需要修改代码的前提下增加额外的功能,装饰器的返回值也是一个函数对象。
2.应用范围:插入日志、性能测试、事务处理、缓存、权限校验等应用场景。装饰器是解决这类问题的绝佳设计,有了装饰器,我们就可以抽离出大量与函数功能本身无关的雷同代码并继续重用。
3.简单装饰器:
(1)应用举例:(监测程序运行耗时)
import time #引入时间模块
def show_time(f):
def wrapper():
start=time.time()
f()
end=time.time()
print('spend %s seconds'%(end-start)) #采用格式化输出
return wrapper #装饰器返回值为函数对象
@show_time #等价于f1=show_time(f1)
def f1():
print('f1...')
time.sleep(2) #模仿程序运行时间
f1()
(2)在这个例子中,函数进入和退出时 ,被称为一个横切面(Aspect),这种编程方式被称为面向切面的编程(Aspect-Oriented Programming)。
(3)@符号是装饰器的语法,在定义函数的时候使用,避免再一次赋值操作
(4)如果有其他的类似函数,可以继续调用装饰器来修饰函数,而不用重复修改函数或者增加新的封装。这样,程序的可重复利用性提高了,并且程序的可读性增加了。
(5)注意:foo=show_time(foo)其实是把wrapper引用的对象引用给了foo,而wrapper里的变量func之所以可以用,就是因为wrapper是一个闭包函数。
(6)装饰器在Python使用如此方便都要归因于Python的函数能像普通的对象一样能作为参数传递给其他函数,可以被赋值给其他变量,可以作为返回值,可以被定义在另外一个函数内。
4.带参数的被装饰函数
(1)应用举例:(若被监测函数原本带有参数,此处直接以不定长参数为例)
import time #引入时间模块
def show_time(f):
def wrapper(*args,**kwargs):
start=time.time()
f(*args,**kwargs)
end=time.time()
print('spend %s seconds'%(end-start)) #采用格式化输出
return wrapper #装饰器返回值为函数对象
@show_time #等价于f2=show_time(f2)
def f2(*args,**kwargs):
sum=0
for i in args:
sum+=i
print(sum)
time.sleep(2) #模仿程序运行时间
f2(1,2,3,4)
5.带参数的装饰器:
(1)应用举例:(作用是通过参数,控制附加某些功能是否执行,作开关用)
import time #引入时间模块
def time_log(flag=0): #利用默认参数
def show_time(f):
def wrapper(*args,**kwargs):
start=time.time()
f(*args,**kwargs)
end=time.time()
print('spend %s seconds'%(end-start)) #采用格式化输出
if flag:
print('已将该操作记录至日志')
return wrapper #装饰器返回值为函数对象
return show_time
@time_log(1) #设置打开写日志开关
def f2(*args,**kwargs):
sum=0
for i in args:
sum+=i
print(sum)
time.sleep(2) #模仿程序运行时间
f2(1,2,3,4)
(2)上面的time_log是允许带参数的装饰器。它实际上是对原有装饰器的一个函数封装,并返回一个装饰器(一个含有参数的闭包函数)。当使用@time_log(1)调用的时候,Python能够发现这一层的封装,并把参数传递到装饰器的环境中。