Python functools 模块

Python functools 模块#

虽然《 Python 语言参考》描述了 Python 语言的确切语法和语义,但该库参考手册描述了随 Python 分发的标准库。它还描述了 Python 发行版中通常包含的一些可选组件。Python 的标准库非常广泛,可提供各种功能。该库包含内置模块(用 C 编写),这些模块提供对系统功能的访问。

本文将提到有关 functools 模块的内容,只提到个人认为比较好用的部份 .

@cached_property 转换函数为属性值 (python 3.8)#

有些类的属性值为常数,我们可以一开始就设置,但有些却是随时在变化,如果我们随时都要去更新,在代码编写上很不好处理,因此,可以在需要的时候,以函数来取得结果。更简单的方式是调用属性时会自动调用函数,但是代码要如何来作到呢?相信很多人都不清楚要怎么作到.

下面这个范例,可以看出来,只要内容一有更动,就必须调用 get_pets, get_legs 去更新 pets 及 legs 属性,相当麻烦.

class Animal():

    def __init__(self, cats=0, dogs=0, fishes=0):
        self.cats = cats
        self.dogs = dogs
        self.fishes = fishes
        self.get_pets()
        self.get_legs()

    def get_pets(self):
        self.pets = self.cats + self.dogs + self.fishes

    def get_legs(self):
        self.legs = (self.cats + self.dogs)*3

    def set_pets(self, animal, number):
        self.__setattr__(animal, number)
        self.get_pets()
        self.get_legs()

一个好的方式就是使用 @cached_property,这样函数就变成属性,代码又变得简单,重要是只有在用到时才会调用该函数。

from functools import cached_property

class Animal():

    def __init__(self, cats=0, dogs=0, fishes=0):
        self.cats = cats
        self.dogs = dogs
        self.fishes = fishes

    @cached_property
    def pets(self):
        return self.cats + self.dogs + self.fishes

    @cached_property
    def legs(self):
        return (self.cats + self.dogs)*4

    def set_pets(self, animal, number):
        self.__setattr__(animal, number)

@lru_cache (maxsize=128) 缓存函数的调用结果#

有时候我们常常会重复调用相同参数的函数,如果每次都要重新处理一次,速度上就会变得很慢;因此这里将调用过的函数结果存在缓存中,下次如果再次调用,直接从缓存中取出结果就可以了,这样会使得速度变得很快.

下面的范例是计算所有 N!, N:0~1000 的结果,计算方式采用递归,也就是 N! = N * (N-1)!, f1 和 f2 两个函数内容完全一样,只不过 f2 采用了 @lru_cache (maxsize=1200), f1 用了约 0.2 秒,f2 却只用了约 0.001 秒,速度差了 200 倍啊!

from functools import lru_cache
from datetime import datetime
import sys

sys.setrecursionlimit(2000)

def f1(n):
    if n < 2:
        return 1
    return n*f1(n-1)

@lru_cache(maxsize=1200)
def f2(n):
    if n < 2:
        return 1
    return n*f2(n-1)

now = datetime.now()
for i in range(1001):
    factorial = f1(i)
print(datetime.now()-now)
# 0:00:00.202456

now = datetime.now()
for i in range(1001):
    factorial = f2(i)
print(datetime.now()-now)
# 0:00:00.000997

f2.cache_info()  # 缓存讯息
f2.cache_clear() # 清除缓存

partial (func, /, *args, **keywords) 定义一个新的子函数#

通常我们建立一个新的函数,可能带有一些参数,当我们常会用到该函数,而某些参数值是固定的,用起来代码还是那么长,而且不容易与原函数区隔;如果另外要定义新的函数也挺麻烦的。这里可以提供一个简单的方式来使用.

partial 中的 func 为你想简化的函数,后面给上指定的参数值,返回一新的函数.

from functools import partial

int2 = partial(int, base=2)
int8 = partial(int, base=8)
int16 = partial(int, base=16)

int2('101')  # int('101', base= 2) => 5
int8('101')  # int('101', base= 8) => 65
int16('101') # int('101', base=16) => 257

partialmethod(func, /, args, *keywords) 建立一个类的新函数#

这个方法类似 partial, 但用在类的函数定义

from functools import partialmethod

class Animal():
    def __init__(self, dogs=0, cats=0):
        self.dogs = dogs
        self.cats = cats

    def set(self, kind, number):
        self.__setattr__(kind, number)

    set_dogs = partialmethod(set, "dogs")
    set_cats = partialmethod(set, "cats")

reduce (function, iterable [, initializer]) 累积处理函数#

这个函数本来是 python 的内建函数,后来移到内建模块 functools 中,基本算法就是取前两个元素运算,其结果再与下一个元素再运算,一直到没有元素。比如累加,累乘,等等…

from functools import reduce

reduce(lambda x, y:x+y, range(1, 11)) # 1+2+...+10
reduce(lambda x, y:x*y, range(1, 11)) # 1*2*...*10 (10!)

singledispatch 建立一个可修改的通用函数#

建立一个可根据第一个参数类别,分开不同函数处理的通用函数,内容有点复杂,不好三言两语就说清楚,所以只提供以下范例.

from functools import singledispatch

@singledispatch
def print_value(arg):
    print(f'argument value is {arg}')

@print_value.register(int)
@print_value.register(float)
def _(arg:int):
    print(f'half value of {arg} is {arg/2}')

@print_value.register
def _(arg:complex):
    print(f'complex value is {arg}')

def nothing(arg):
    print('argument is None')
print_value.register(type(None), nothing)

>>> print_value(10)
half value of 10 is 5.0
>>> print_value(12.5)
half value of 12.5 is 6.25
>>> print_value(3-2j)
complex value is (3-2j)
>>> print_value("Hello")
argument value is Hello
>>> print_value(None)
argument is None

>>> print_value.dispatch(float) # 参数为 float 时所调用的函数
<function _ at 0x0000000001756700>

>>> print_value.registry # 可以字典的形式来使用
mappingproxy({<class 'complex'>: <function _ at 0x00000000017561F0>,
              <class 'float'>: <function _ at 0x0000000001756700>,
              <class 'int'>: <function _ at 0x0000000001756700>,
              <class 'NoneType'>: <function nothing at 0x0000000001756670>,
              <class 'object'>: <function print_value at 0x0000000001746B80>})
本作品采用《CC 协议》,转载必须注明作者和本文链接
Jason Yang