;> int('10010', **kw)
当传入:
>>> max2 = functools.partial(max, 10)
实际上会把10作为args的一部分自动加到左边,也就是:
>>> max2(5, 6, 7)
相当于:
>>> args = (10, 5, 6, 7)
>>> max(args)
10
当函数的参数个数太多,需要简化时,使用functools.partial可以创建一个新的函数,这个新函数可以固定住原函数的部分参数,从而在调用时更简单。
5.4.4 闭包
闭包就是一种函数的嵌套,首先定义了一个函数,称之为外部函数
。在这个外部函数体中又定义了一个内部函数
,并且这个内部函数体中使用到了外部函数的变量。外部函数最后return了内部函数。那么这个外部函数以及内部函数就构成了一个特殊的对象,称之为闭包。
闭包避免了使用全局变量,使得局部变量在函数外被访问成为可能,相比较面向对象,不用继承那么多的额外方法,闭包占用了更少的空间。
闭包示例
a = 1
def out_fun(b):
c = 3
def in_fun(d):
print(a + b + c + d)
return in_fun
infun = out_fun(2)
infun(4)
10
可以看到,在内部函数in_fun
中访问到了全局变量a、外部函数out_fun
的局部变量c以及参数b。
5.4.4.2 装饰器
装饰器(decorator)的本质:函数闭包(function closure)的语法糖(Syntactic sugar)。通过给函数装饰,可以加强函数的功能或者增加原本函数没有的功能。
装饰器在第一次调用被装饰函数时进行增强,并且只增强一次。
让我们先从一个简单的函数开始吧。假设现在有一个函数,用来计算从1到100累加之和,并输出结果。为了避免计算太快,我们在使用循环累加时设置等待0.01秒,函数定义如下:
def mysum1():
from time import sleep
total = 0
for _ in range(101):
sleep(0.01)
total += _
print(total)
mysum1()
5050
此时,如果我们想要知道调用这个函数执行一共花了多少时间,我们可以在执行前后获取时间再经过计算得到,也可以通过改造函数,在函数内部函数体前后获取时间计算得到。但是这两种方法都比较麻烦,尤其是第二种方法,需要侵入式修改函数代码。
这个时候就可以通过为函数添加装饰器来实现了。下面是创建装饰器的一般方法:
装饰器的简单定义
def decorator1(func):
def inner():
print('在这里执行被装饰函数执行前的增强操作')
func() # 执行被装饰的函数
print('在这里执行被装饰函数执行前的增强操作')
return inner
从上面可以看出,装饰器也是一个函数。只不过装饰器接收的参数是被装饰的函数。然后再装饰器内部定义一个函数,该内部函数体中执行要增强的操作代码以及执行被装饰的函数。最后再return该内部函数。
接下来是用装饰器来对某个函数进行装饰。我们以上面定义的mysum1函数来进行装饰:
装饰器的使用
@decorator1
def mysum1():
from time import sleep
total = 0
for _ in range(101):
sleep(0.01)
total += _
print(total)
mysum1()
在这里执行被装饰函数执行前的增强操作
5050
在这里执行被装饰函数执行前的增强操作
由上面可以看出,如果要装饰某个函数,只需要在定义这个函数时,在def语句的上一行添加@装饰器函数
即可。
对于装饰器装饰一个函数:
@decorator
def myfun():
print("hello")
上面的代码等价于:
def myfun():
print("hello")
myfun = decorator(myfun)
当一个装饰器装饰函数时,函数的功能增强了,因为在调用这个函数时,实际上调用的是在定义装饰器函数时,其内部函数。而此时内部函数是由增强功能命令和原被装饰函数组成。
创建一个统计函数运行时长的装饰器
import time
def decorator1(func):
def inner():
begin = time.time()
func() # 执行被装饰的函数
end = time.time()
print(f"函数`{func.__name__}`运行的总时间为:{end - begin:.3} 秒")
return inner
@decorator1
def mysum1():
from time import sleep
total = 0
for _ in range(101):
sleep(0.01)
total += _
print(total)
mysum1()
5050
函数mysum1
运行的总时间为:1.59 秒
5.4.4.2.2 被装饰函数接收参数
在上面的例子中,通过装饰器函数decorator
装饰的函数是不能有输入参数的,在实际使用中并不是很方便。
通过对装饰器进行改造可以避免这种情况,从而使装饰器函数有更广泛的用途。
装饰器定义:让被装饰函数接收参数
import time
def decorator2(func):
def inner(*args, **kwargs):
begin = time.time()
func(*args, **kwargs) # 执行被装饰的函数
end = time.time()
print(f"函数`{func.__name__}`运行的总时间为:{end - begin:.3}")
return inner
需要改造的地方:
1、为装饰器函数decorator
的内部函数inner
在定义时增加收集位置形参和收集关键字形参
2、在装饰器函数decorator
的内部函数inner
函数体中,执行被装饰器装饰的函数func
时,通过参数解包的方式传入参数。
装饰带有参数的函数:
@decorator2
def mysum2(a, b):
from time import sleep
total = a
for _ in range(total + 1, b + 1):
sleep(0.01)
total += _
print(total)
mysum2(1, 100)
5050
函数mysum1
运行的总时间为:1.56
5.4.4.2.3 装饰器函数接收参数
通过上面对装饰器进行改造,可以使的被装饰的函数可以输入参数。上面的装饰器函数decorator2
可以计算被装饰的函数执行时间,但是只能获取到执行一次的时间。如果想要通过参数获取执行任一次的时间,则需要使得装饰器可以接收参数。
装饰器定义:装饰器接收参数
import time
def decorator3(n):
def inner(func):
def wrapper(*args, **kwargs):
begin = time.time()
for _ in range(iteration):
func(*args,