闭包
在一个外函数中定义了一个内函数,内函数里运用了外函数的临时变量,并且外函数的返回值是内函数的引用。这样就构成了一个闭包;
一般情况下,在我们认知当中,如果一个函数结束,函数的内部所有东西都会释放掉,还给内存,局部变量都会消失。但是闭包是一种特殊情况,如果外函数在结束的时候发现有自己的临时变量将来会在内部函数中用到,就把这个临时变量绑定给了内部函数,然后自己再结束。
定义:
如果在一个内部函数里,对在外部作用域(但不是在全局作用域)的变量进行引用,那么内部函数就被认为是闭包(closure);
闭包=内部函数+定义函数时的环境
举例一:
def outer():
x = 10
def inner():#条件一 inner就是内部函数
print(x)#条件二 外部环境的一个变量
return inner#结论:内部函数inner就是一个闭包
#outer()()
f = outer() #这两步相当于outer()()
f()
执行结果:
10
举例二:
def outer(x): #outer是外部函数,x是外部函数的临时变量;
def inner(y): #inner是内函数
nonlocal x #内函数想修改闭包变量,nonlocal关键字声明我要修改X;
x += y #x = x+y
return x #结束返回X的值;
return inner #外函数的返回值是内函数的引用;
a = outer(5)
print(a(5))
print(a(10))
执行结果:由此可见,每次调用inner的时候,使用的闭包变量x实际上是同一个。
10
20
装饰器
装饰器(Decorators)是 Python 的一个重要部分,本质上也是一个python函数。简单地说:他们是修改其他函数的功能的函数。他们有助于让我们的代码更简短,也更Pythonic(Python范儿),可以让其他函数在不需要做任何代码变动的情况下增加额外功能,装饰器的返回值也是一个函数对象。比如:插入日志、性能测试、事务处理、缓存、权限校验等应用场景。装饰器是解决这类问题的绝佳设计,有了装饰器,我们就可以抽离出大量与函数功能本身无关的雷同代码并继续重用。概括的讲,装饰器的作用就是为已经存在的对象添加额外的功能。
例如简单装饰器,计算代码执行时间:
import time
def show_time(x):
def inner():
start = time.time()
x()
end = time.time()
print('spend %s'%(end - start))
return inner
def bar():
print('bar....')
time.sleep(1) #为了模拟实验环境,让他这里等待一秒;
bar = show_time(bar)
bar()
@show_time #相当于foo = show_time(foo);
def foo():
print('foo.....')
time.sleep(2)
foo()
@show_time #test = show_time(test);
def test():
print('test.....')
time.sleep(3)
test()
@符号是装饰器的语法糖,在定义函数的时候使用,避免再一次赋值操作;
这样我们就可以省去foo = show_time(foo)这一句了,直接调用foo()即可得到想要的结果。如果我们有其他的类似函数,我们可以继续调用装饰器来修饰函数,而不用重复修改函数或者增加新的封装。这样,我们就提高了程序的可重复利用性,并增加了程序的可读性。
foo=show_time(foo)其实是把inner引用的对象引用给了foo,而inner里的变量func之所以可以用,就是因为inner是一个闭包函数。
执行结果:
bar....
spend 1.0014598369598389
foo.....
spend 2.0005860328674316
test.....
spend 3.000148296356201
带参数的被装饰函数
import time
def show_time(f):
def inner(x,y):
start_time = time.time()
f(x,y)
end_time = time.time()
print('spend %s'%(end_time - start_time))
return inner
@show_time
def add(a,b):
print(a + b)
time.sleep(2)
add(2,3)
执行结果:
5
spend 2.000265121459961
不定长参数
import time
def show_time(f):
def inner(*x,**y):
start_time = time.time()
f(*x,**y)
end_time = time.time()
print('spend %s'%(end_time - start_time))
return inner
@show_time
def add(*x,**y):
sum = 0
for i in x:
sum += i
print(sum)
time.sleep(2)
add(2,3,4,5,6)
执行结果:
20
spend 2.000115394592285
带参数的装饰器
装饰器还有更大的灵活性,例如带参数的装饰器:在上面的装饰器调用中,比如@show_time,该装饰器唯一的参数就是执行业务的函数。装饰器的语法允许我们在调用时,提供其它参数,比如@decorator(x)。这样,就为装饰器的编写和使用提供了更大的灵活性;
import time
def logger(flag = ''):
def show_time(f):
def inner(*x,**y):
start_time = time.time()
f(*x,**y)
end_time = time.time()
print('spend %s'%(end_time - start_time))
if flag == 'true':
print('这个操作时间将记录到日志中')
return inner
return show_time
@logger('true')
def add(*x,**y):
sums = 0
for i in x:
sums += i
print(sums)
time.sleep(2)
add(2,3,4,5,6)
@logger()
def foo():
print('foo...')
time.sleep(2)
foo()
结果如下:(需要记录到日志中的就加正确参数,不需要的就不用加参数)
20
spend 2.0003697872161865
这个操作时间将记录到日志中
foo...
spend 2.0006625652313232
logger是允许带参数的装饰器。它实际上是对原有装饰器的一个函数封装,并返回一个装饰器(一个含有参数的闭包函数)。当我 们使用@logger(‘true’)调用的时候,Python能够发现这一层的封装,并把参数传递到装饰器的环境中。
多层装饰器
def makebold(fn):
def wrapper():
return '<b>' + fn() + '</b>'
return wrapper
def makeitalic(fn):
def wrapper():
return '<i>' + fn() + '</i>'
return wrapper
@makebold
@makeitalic
def hello():
return "hello world!"
# hello()
print(hello())
执行结果:
<b><i>hello world!</i></b>
类装饰器
再来看看类装饰器,相比函数装饰器,类装饰器具有灵活度大、高内聚、封装性等优点。使用类装饰器还可以依靠类内部的__call__方法,当使用 @ 形式将装饰器附加到函数上时,就会调用此方法。
import time
class like(object):
def __init__(self, func):
self._func = func
def __call__(self):
start_time = time.time()
self._func()
end_time = time.time()
print('spend %s' % (end_time - start_time))
@like
def foo():
print('foo....')
time.sleep(2)
foo()
执行结果:
foo....
spend 2.0000457763671875
https://www.cnblogs.com/yuanchenqi/articles/5830025.html