16.函数式编程

予早 2024-10-05 10:53:18
Categories: Tags:

函数式编程

函数式编程,FP,Functional Programming,编程范式之一,函数式编程将函数视为一等公民,即像其他对象一样可以被分配给变量、可以被修改、可以作为函数函数或返回值。Python标准库中itertools(迭代器工具)functools(函数工具)operator(标准运算符替代函数)与函数式编程相关。

https://docs.python.org/zh-cn/3/library/functional.html

柯里化,Currying

柯里化是指将函数参数列表中某些参数值固定并生成一个接受剩余非固定参数的函数,偏函数是函数柯里化后的结果。

int_base8 = functools.partial(int, base=8)
print(int_base8('123'))  # # 83

模仿partial,使用partial包装的函数是没有__name____doc__等属性的,可以使用update_wrapper将被包装函数的相关属性拷贝到新的函数中去,或者使用warps装饰器可以达到同样效果,warps装饰器是在update_wrapper上的又一层封装

import functools


def partial(func, *args, **kwargs):
    def inner(*new_args, **new_kwargs):
        return func(*args, *new_args, **{**kwargs.copy(), **new_kwargs})

    return functools.update_wrapper(inner, func)


i = partial(int, base=2)

print(i("11"))  # 3
print(i.__name__)  # 不使用update_wrapper会是inner
print(i.__doc__)  # 不使用update_wrapper会是None


def partial1(func, *args, **kwargs):
    @functools.wraps(func)
    def inner(*new_args, **new_kwargs):
        return func(*args, *new_args, **{**kwargs.copy(), **new_kwargs})

    return inner


i1 = partial1(int, base=2)
print(i1.__name__)

柯里化在回调函数上的应用

若某一个函数要留一个参数作为回调函数参数,在回调函数不定和回调函数参数不定的情况下,可以将回调函数参数视为无参函数,使用partial将原有的有参回调函数转为无参回调函数。

import functools
import time


def test(callback):
    current_time = time.time()
    print('exec code and test something ...')
    callback()


def show_globals():
    print(globals())


def print_msg(msg):
    print(msg)


test(show_globals)
test(functools.partial(print_msg, 'hello world'))

reduce

将一个元素序列归纳为一个元素

reduce(function, iterable, initial=None)

import functools

l = [1, 2, 3, 4]
print(functools.reduce(lambda x, y: x + y, l, 10))
# 20

cmp_to_key

比较风格转换

lru_cache

基于Least-recently-used cache算法将函数调用结果缓存到内存,以相同参数进行下次调用时直接返回结果而不再执行

缓存结果示例

import functools
import time


@functools.lru_cache
def get_current_time(x):
    time.sleep(1)
    return time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())


# 因为缓存了时间结果所以同参数调用函数结果相同
print(get_current_time(1))  # 2023-09-30 21:32:52
print(get_current_time(2))  # 2023-09-30 21:32:53
print(get_current_time(1))  # 2023-09-30 21:32:52

斐波那契数列计算

import functools

@functools.lru_cache(maxsize=30)  # maxsize参数告诉lru_cache缓存最近多少个返回值
def fib(n):
    if n < 2:
        return n
    return fib(n - 1) + fib(n - 2)


print([fib(n) for n in range(10)])
fib.cache_clear()  # 清空缓存

singledispatch

单分发器,可用于实现泛型函数

from functools import singledispatch


@singledispatch
def func(text):
    print(text)


@func.register(int)
def _(text):
    print(text)


@func.register(list)
def _(text):
    for k, v in enumerate(text):
        print(k, v)


@func.register(float)
@func.register(tuple)
def _(text):
    print('float, tuple')


func('Hello World!')  # Hello World!
func(123)  # 123
func(['a', 'b', 'c'])
# 0 a
# 1 b
# 2 c
func(1.23)
# float, tuple
print(func.registry)  # 所有的泛型函数
# {<class 'object'>: <function func at 0x00000159DC8E0900>, <class 'int'>: <function _ at 0x00000159DC915940>,
# <class 'list'>: <function _ at 0x00000159DC9159E0>, <class 'tuple'>: <function _ at 0x00000159DC915B20>,
# <class 'float'>: <function _ at 0x00000159DC915B20>}
print(func.registry[int])  # 获取int的泛型函数
# <function _ at 0x000001F842AE5940>

函数组合

toolz,三方库

import toolz


def add(a):
    a += 1
    return a


double_add = toolz.compose(add, add)

print(double_add(1))  # 3

from toolz.curried import pipe, map, filter, get