这是我参与8月更文挑战的第10天,活动详情查看:8月更文挑战html
Python标准库包括许多强大的模块,可让你的代码既干净又高效,今天咱们介绍的是functools
模块,这个模块提供了许多有用的高阶函数,咱们能够利用这些高阶函数来实现缓存功能、重载、建立装饰器等,会然咱们的代码更加高效,好了,让咱们来看一下,functools
模块都给咱们提供了什么呢。python
functools
模块中提供了一个简单却又强大的缓存函数(装饰器)lru_cache()
,lru_cache()
能够直接以语法糖@
的形式做用于咱们的函数至上,为咱们的函数提供缓存的功能,遵循最近最少使用的原则,将函数执行结果缓存,方便下次函数执行前查询,若是函数的参数比较固定,逻辑比较固定,那么将很是必要,能够有效的减小函数的执行时间。缓存
import functools
import time
@functools.lru_cache(maxsize=32)
def add(a, b):
print("sleep 2s")
time.sleep(2)
return a+b
print("第1次调用:", add(4, 5)) # 第一次调用,无缓存,执行原函数,结果加入缓存
print("第2次调用:", add(4, 5)) # 第二次调用,参数与第一次相同,匹配到缓存,直接返回缓存,不执行原函数
print("第3次调用:", add(5, 5)) # 第三次调用,参数与以前不一样,无缓存,执行原函数,结果加入缓存
print(add.cache_info()) # 检查函数的缓存信息,它显示缓存命中和未命中的数量
复制代码
执行结果为:markdown
sleep 2s
第1次调用: 9
第2次调用: 9
sleep 2s
第3次调用: 10
CacheInfo(hits=1, misses=2, maxsize=32, currsize=2)
Process finished with exit code 0
复制代码
在这个例子中,咱们使用@lru_cache
装饰器处理add()
函数并缓存执行的结果(能够缓存32个结果)。为了查看缓存是否真的有效,咱们使用了cache_info()
方法检查函数的缓存信息,它显示缓存命中和未命中的数量,经过结果咱们能够看出,在第一次调用的时候并没有缓存,add()
函数执行结果存入缓存,第二次的时候命中缓存,第三次的时候没有命中缓存。app
若是你想要更细粒度的缓存,那么你也能够包含可选的typed=true
参数,这使得不一样类型的参数被分别缓存。 例如,add(4.0, 5.0)
和add(4, 5)
将被视为不一样的调用。函数
其实在Python3.8的版本以后,还有另外一个能够用于缓存的装饰器叫作cached_property
,这个函数用于缓存类属性的结果,若是你的类的属性是而又不可变的,可使用这个。oop
您可能已经知道,可使用__lt__()
、__gt__()
和__eq__()
在Python中实现比较操做符,如<
、>
或==
。可是实现__lt__()
、__gt__()
、__eq__()
、__le__()
、__ge__()
多是至关繁琐的,恰好functools
模块包含total_ordering
'装饰器,它能够帮助咱们作到这一点。
咱们只须要实现__eq__()
,和__lt__()
、__gt__()
、__le__()
、__ge__()
中的任一个方法,其余的它会帮咱们自动提供:post
from functools import total_ordering
@total_ordering
class MyNumber:
def __init__(self, value):
self.value = value
def __gt__(self, other):
return self.value > other.value
def __eq__(self, other):
return self.value == other.value
print(MyNumber(5) > MyNumber(3))
# True
print(MyNumber(1) < MyNumber(5))
# True
print(MyNumber(5) >= MyNumber(5))
# True
print(MyNumber(5) <= MyNumber(2))
# False
复制代码
很明显,它能够帮助咱们减小代码和提升可读性。ui
这里的偏函数和数学意义上的偏函数不是一个概念,functools
模块中的partial
函数的做用是,把一个函数的某些参数给固定住(也就是设置默认值),返回一个新的函数,调用这个新函数会比调用原来的函数更简单。理解起来是否是有些困惑,让咱们来看一些实际的例子。 在Python中,int()
函数能够把指定数字字符串转换为整数,默认是作十进制的转换:url
print(int(1234))
# 1234
复制代码
但int()
函数还提供了一个base
参数,默认值为10
。若是传入base
参数,就能够作N进制的转换,即,默认数字字符串的进制是N进制,要转化为10进制,例如:
print(int("1234", base=8))
# 默认1234位8进制的数字,转化为10进制:668
print(int("1234", base=16))
# 默认1234位16进制的数字,转化为10进制:4660
复制代码
假设要转换大量的8进制字符串,每次都传入int(x, base=8)
显得过于麻烦,因而,咱们想到,能够定义一个int8()
的函数,默认把base=8
传进去:
def int8(x, base=8):
return int(x, base)
print(int8("1234"))
复制代码
这样,就很是方便了。
其实functools.partial
的功能就是可以建立一个偏函数,不须要咱们本身定义int8()
,能够直接使用下面的代码建立一个新的函数int8
:
int2 = functools.partial(int, base=8)
print(int2('1234'))
复制代码
因此简单来讲,functools.partial
的做用就是,把一个函数的某些参数给固定住(也就是设置默认值),返回一个新的函数,使得咱们调用这个新函数会更简单。
咱们都知道严格意义上来讲,在Python中函数重载是不可能实现的,但实际上有一个简单的方法来实现和函数重载类似的功能,那就是使用functools
模块中singledispatch
函数装饰器。下面来看个例子:
@singledispatch
def show(obj):
print(obj, type(obj), "obj")
@show.register(str)
def _(text):
print(text, type(text), "str")
@show.register(int)
def _(n):
print(n, type(n), "int")
show(1234)
show("abcd")
show({"abcd": 1234})
复制代码
结果为:
1234 <class 'int'> int
abcd <class 'str'> str
{'abcd': 1234} <class 'dict'> obj
Process finished with exit code 0
复制代码
可见为show()
函数传递不一样的类型参数,就调用不一样的函数,表现不一样的行为,实际上singledispatch
实现的是单泛函数,给函数加上.register
方法,该方法支持绑定一个变量类型和一个函数。而后它返回一个被重载了的函数。当输入值的类型是经过.register
绑定的类型时,就调用同时绑定的函数。
这一部分在我以前的文章有提到,感性趣的能够看一下:内置装饰器@functools.wrap
functools
模块提供了许多有用的功能和装饰器,能够帮助咱们构建更好的代码,我这里提到的只是部份内容。感兴趣的小伙伴能够查看官方文档functools — Higher-order functions and operations on callable objects
最后,感谢女友在工做和生活中的包容、理解与支持 !