闭包
闭是封闭(函数中的函数),包是包含(该内部函数对外部函数作用域而非全局作用域变量的引用。)
闭包:
- 内部函数对外部函数作用域里的变量的引用
- 函数内的属性,都是有生命周期,都是在函数执行期间
- 闭包内的闭包函数私有化了变量,
def foo():
print("in foo()")
def bar ():
print("in bar()")
#1. 直接运行内部函数报错
# bar()
#
# 2.先运行外部函数,再运行内部函数,依然会报错
# foo()
# bar()
由于作用域的问题,函数内的属性都是有生命周期的,只有在函数执行期间
在这段代码,只有调用foo()是,内部的print()及bar()才能存活。
现在我们为了让foo()内bar()存活,就是调用bar()
- 把bar()函数返回给函数
def foo():
print("in foo()")
def bar():
print("in bar()")
return bar
var = foo()
var()
内部函数对外部函数作用域的引用
—>参数调用
def foo():
a = 66
print("in foo()")
def bar(num):
print("in bar()")
print(a + num )
return bar
var = foo()
var(22)
# in foo()
# in bar()
# 88
li = [1,2,3,4,5]
def foo(obj):
print("foo",obj)
def bar():
obj[0] += 1
print("bar",obj)
return bar
# 程序在执行时,foo()函数返回给了内部定义的bar()函数
var = foo(li) # 将返回值赋值给var
var()
var()
var()
# foo [1, 2, 3, 4, 5]
# bar [2, 2, 3, 4, 5]
# bar [3, 2, 3, 4, 5]
# bar [4, 2, 3, 4, 5]
装饰器
@func1
def func():
print('aaa')
装饰器存在的意义
- 不影响原有函数的功能
- 可以添加新功能
一般常见的,比如拿到第三方API接口,第三方不允许修改这个接口,需要使用装饰器。
装饰器本身是一个函数,作用是为现有存在的函数,在不改变函数的基础上,添加一些功能进行装饰。
它是以闭包的形式实现的。
在使用装饰器函数时,在被装饰的函数的前一行,使用@装饰器函数名
形式来进行装饰。
Demo:
现在在一个项目中,有很多函数,由于我们的项目越来越大,功能也越来越多,导致程序越来越慢。
其中一个功能函数的功能,是实现一百万次的累加。
def my_count():
s = 0
for i in range(1000001):
s += i
print("sum: ",s)
计算时间
import time
def my_count():
s = 0
for i in range(1000001):
s += i
print("sum: ",s)
start = time.time()
my_count()
end = time.time()
print("执行时间为:",(end - start))
# sum: 500000500000
# 执行时间为: 0.0608363151550293
每一个函数都进行时间计算,此方式比较麻烦,代码过于多余。
import time
def my_count():
s = 0
for i in range(1000001):
s += i
print("sum: ",s)
def count_time(func):
start = time.time()
func()
end = time.time()
print("执行时间为:", (end - start))
count_time(my_count) # 仍然更改了调用方式
# sum: 500000500000
# 执行时间为: 0.054853200912475586
修改之后,定义一个函数来实现时间计算功能。
使用时需要将对应的函数传入到时间计算函数中。
但是启用时间计算功能时更改了my_count的函数调用方式。
import time
def count_time(func):
def wrapper():
start = time.time()
func()
end = time.time()
print("执行时间为:", (end - start))
return wrapper
@count_time
def my_count():
s = 0
for i in range(1000001):
s += i
print("sum: ",s)
my_count()
这样实现的好处,定义闭包函数后,只需要通过@装饰器函数名
形式的装饰器语法,就可以将@装饰器函数名
加到要装饰的函数前即可
这种不改变原有函数功能,对函数进行拓展的形式,就称为装饰器
在执行@装饰器函数名
时,就是将原函数传递到闭包中。然后,原函数的引用指向闭包返回的装饰过的内部函数的引用。