学习笔记:
闭包closure:
1.函数名是指向函数的变量。函数名=变量。
def lazy_sum(*args):
def sum():
ax = 0
for n in args:
ax = ax + n
return ax
return sum
f = lazy_sum(1, 3, 5, 7, 9)
print(f) #<function lazy_sum.<locals>.sum at 0x000002744971F828>
备注:此时的lazy_sum的输出为函数sum,因此目前f指向函数sum。打印f时结果应为<function lazy_sum.<locals>.sum at 0x000002744971F828>。因此f()也就是sum()才能具体计算结果。
2.闭包就是能够读取其他函数内部变量的函数。可以理解为定义在一个函数内部的函数。
注意:由于返回的是一个函数,此时该函数并没有立即执行,只有调用了f()时才执行。在调用f()执行时,内部变量可能会发生变化。
错误
def count():
fs = []
for i in range(1, 4):
def f():
return i*i
fs.append(f)
return fs
f1, f2, f3 = count()
f1 #<function count.<locals>.f at 0x0000017BDCEED828>
f1() #9
f2() #9
f3() #9
正确
def count():
def f(j):
def g():
return j*j
return g
fs = []
for i in range(1, 4):
fs.append(f(i))
return fs
f1, f2, f3 = count()
f1 #<function count.<locals>.f.<locals>.g at 0x00000189F9936A68>
f1() #1
f2() #4
f3() #9
个人理解:
1)首先fs作为一个list,可以被分别赋值给f1、f2、f3,此时相当于f1=fs[0]。
2)错误版本运行完后f=i2,但调用时查找到i已经变为3,因此所有输出结果均为9。
正确版本运行时将i分别固定给了j,即将i和f1建立了关联。
3)如下版本虽然也将i和f1建立了关联,但返回值不是函数,为固定值。
def count():
fs = []
for i in range(1, 4):
def f(i):
return i*i
fs.append(f(i))
return fs
f1, f2, f3 = count()
print(f1) #1
print(f2) #4
print(f2) #9
4)正确版本缩减:使用lambda函数简化。
def count():
def f(j):
return lambda:j*j
fs = []
for i in range(1, 4):
fs.append(f(i))
return fs
f1, f2, f3 = count()
3.使用闭包时,对外层变量赋值前,需要先使用nonlocal声明该变量不是当前函数的局部变量。
def inc():
x = 0
def fn():
nonlocal x
x = x + 1
return x
return fn
f = inc()
print(f()) # 1
print(f()) # 2
小结:
一个函数可以返回一个计算结果,也可以返回一个函数。
返回一个函数时,牢记该函数并未执行,返回函数中不要引用任何可能会变化的变量。
装饰器decorator:装饰器本质上就是一个函数,它用来给别的函数增加功能。
装饰器对被装饰函数是完全透明的,也就是说,装饰器不会对要加功能的函数的源代码进行修改,而且也不会改变函数的调用方式。
装饰器可以在代码运行期间动态地增加函数的功能,当我们想要给多个函数增加相同的功能,一个一个地去修改这些函数效率很低,而且代码混乱,就可以采用装饰器。
def 装饰器名称(被装饰函数func):
def 装饰器作用函数wrapper(如果func有参数那么输入*args,**kw):
执行语句xxxxxxxxxxxxxxx
func(如果被func有参数那么输入*args,**kw)
# 如果被装饰函数中含有return则需要给func赋值
# value_func=func(*args,**kw)
执行语句xxxxxxxxxxxxxxx
# 如果被装饰函数中含有return则需要返回value_func
# return value_func
return 装饰器作用函数wrapper
@装饰器名称
def 被装饰函数():
执行语句
# return
被装饰函数()
函数作用:查找2-10000内的素数。
import time # 引入计时模块
def display_time(func):
# 装饰器 作用为计时
def wrapper():
t1 = time.time()
func()
t2 = time.time()
print(t2-t1)
return wrapper
def is_Prime(num):
if num < 2:
return True
elif num == 2:
return False
else:
for i in range(2,num):
if num % i == 0:
return False
return True
@display_time
def find_Prime():
for i in range(2,10000):
if is_Prime(i):
print(i)
find_Prime
# find_Prime=display_time(find_Prime)
# <function display_time.<locals>.wrapper at 0x000001D60C837AF8>
find_Prime() # 等价于wrapper()
当find_Prime有return时:
import time
def display_time(func):
def wrapper():
t1 = time.time()
result = func()
t2 = time.time()
print(t2-t1)
return result
return wrapper
def is_Prime(num):
pass
@display_time
def find_Prime():
count = 0
for i in range(2,10000):
if is_Prime(i):
count += 1
return count
当find_Prime有参数时:
def display_time(func):
def wrapper(*args,**kw):
t1 = time.time()
result = func(*args,**kw)
t2 = time.time()
print(t2-t1)
return result
return wrapper
def is_Prime(num):
pass
@display_time
def find_Prime(n):
count = 0
for i in range(2,n):
if is_Prime(i):
count += 1
return count
count = find_Prime(10000)
print (count)
注意:需要把原始函数的__name__等属性复制到wrapper()函数中,否则,有些依赖函数签名的代码执行就会出错。
import time
import functools
def display_time(func):
@functools.wraps(func)
def wrapper(*args,**kw):
t1 = time.time()
result = func(*args,**kw)
t2 = time.time()
print(t2-t1)
return result
return wrapper
def is_Prime(num):
pass
@display_time
def find_Prime(n):
count = 0
for i in range(2,n):
if is_Prime(i):
count += 1
return count
count_prime=find_Prime(10000)
print(count_prime)
find_Prime # <function find_Prime at 0x00000232638FBAF8>
当装饰器有参数时:需要编写一个返回decorator的高阶函数。(三层嵌套)
def log(text):
def decorator(func):
@functools.wraps(func)
def wrapper(*args, **kw):
print('%s %s():' % (text, func.__name__))
return func(*args, **kw)
return wrapper
return decorator
@log('execute')
def now():
print('2015-3-25')
now() #now = log('execute')(now) = decorator(now) = wrapper
# execute now():
# 2015-3-25
装饰器含参数(两层嵌套)
运行逻辑有错:
def log(text):
def wrapper(func):
print('%s %s():' % (text, func.__name__))
return func()
return wrapper
@log('execute')
def now():
print('2021')
print(now)
# execute now():
# 2021
# None
正确的两层嵌套:
def log(text):
def wrapper(func):
print('%s %s():' % (text, func.__name__))
return func
return wrapper
@log('execute')
def now():
print('2021')
print(now)
now()
# execute now():
# <function now at 0x0000023DE4AD0A68>
# 2021
错误原因:在执行@log('execute') def now():时,等价于执行了log('execute')(now) = wrapper(now) = now(),因此会在这两部分直接输出execute now():和2021,而运行print(now)时,由于now变量未定义,故输出为None。
修改后在执行@log('execute') def now():时,等价于执行了log('execute')(now) = wrapper(now) = now,因此会输出execute now():,而后运行print(now),输出<function now at 0x0000023DE4AD0A68>,而后运行now(),输出2021。
当装饰器有可变参数时:
def log(*text):
def decorator(func):
@functools.wraps(func)
def wrapper(*args, **kw):
if len(text)==0:
print('no text %s():' % (func.__name__))
return func(*args, **kw)
for i in text:
print('%s %s():' % (i, func.__name__))
return func(*args, **kw)
return wrapper
return decorator
@log()
def now(x,y):
return x + y
print(now) #<function now at 0x000001B0A029FAF8>
#也就是<function log.<locals>.decorator.<locals>.wrapper at 0x000001DABDE4FAF8>
print(now(1,2))