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
讨论数量: 0
(= ̄ω ̄=)··· 暂无内容!

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!