流畅的python
闭包
简介:当一个函数返回了一个函数后,其内部的局部变量还被新函数引用
- 闭包返回的函数不能引用任何循环变量或者后续,会发生变化的变量,如下示例。
def count():
fs = []
for i in range(1, 4):
def f():
return i*i
fs.append(f)
return fs
f1, f2, f3 = count()
输出内容:
>>> f1()
9
>>> f2()
9
>>> f3()
9
大家认为可能的输出为1,4,9,实际并不是,闭包返回的函数并没有立刻执行,而是等全部都返回了才执行。等到3个函数都返回时,它们所引用的变量i已经变成了3,因此最终结果为9。
- 若非要引用循环变量,可以修改函数,用一个变量绑定i值,这样就得到了想要的返回结果
(返回函数不要引用任何循环变量,或者后续会发生变化的变量)
def count():
fs = []
for i in range(1, 4):
def f(j = i):
return j * j
fs.append(f)
return fs
f1, f2, f3 = count()
print(f1())
print(f2())
print(f3())
执行结果
1
4
9
装饰器 = 高阶函数 + 函数嵌套 + 闭包
简介:装饰器是可调用的对象,其参数是另一个函数(也就是被装饰的函数),装饰器会处理被装饰的函数,然后把它返回,或者将其替换成另一个函数或可调用的对象
-
装饰器通常把函数替换成另一个函数
-
装饰器在加载模块是立即执行
registry = [] #创建列表用于存储被装饰的函数引用
def register(func):
print('running register(%s' % func.__name__)
registry.append(func)
return func
@register #被装饰的函数f1()将作为装饰器函数的func传入
def f1():
print('running f1()')
@register #加上装饰器相当于执行了f2 = register(f2)
def f2():
print('running f2()')
def f3():
print('running f3()')
def main():
print('running main')
print('registry-', registry)
f1()
f2()
f3()
main()
执行结果
running register f1
running register f2
running main
registry- [<function f1 at 0x0000027FDE716708>, <function f2 at 0x0000027FDE716948>]
running f1()
running f2()
running f3()
f1()和f2()被装饰器修饰,在main()没被调用前就执行了,并且返回该函数的引用。
为什么使用装饰器
首先看一个简单的求素数的例子,不用装饰器的代码如下:
#求素数
def is_prime(num):
if num == 2 or num == 3:
return True
if num % 2 == 0 or num % 3 == 0:
return False
for i in range(5, int(math.sqrt(num)) + 1, 6):
if num % i == 0 or num % (i + 2) == 0:
return False
return True
def primes(maxnum):
total = 0
t1 = time.time()
for i in range(2, 1000):
if is_prime(i):
print(i)
t2 = time.time()
print(t2 - t1)
上述代码求出了2到1000的素数,并且还输出了运行代码所需时间。这里的计算时间只能应用在primes这个方法里,如果我们其他方法也想用就要重新定义t1\t2,然后进行计算,无法复用。装饰器就可以做到代码复用。
下面是装饰器写法:
#装饰器计算时间
def display_time(func):
def wrapper(*args):
t1 = time.time()
result = func(*args)
t2 = time.time()
print(t2 - t1)
return result
return wrapper
@display_time
def primes(maxnum):
total = 0
for i in range(2, maxnum):
if is_prime(i):
total += 1
return total
方法display_time表示装饰器的名称,括号里是要运行的函数(也就是被装饰的函数primes),方法wrapper才是运行函数时要运行的内容,*args代表方法primes传的参数。