写点什么

你还在用命令式编程?Python 函数式编程让你的代码更优雅!

作者:高端章鱼哥
  • 2023-07-25
    福建
  • 本文字数:9574 字

    阅读完需:约 31 分钟

你还在用命令式编程?Python函数式编程让你的代码更优雅!

Python 支持函数式编程,函数式编程是一种编程范式,它将计算机程序视为数学函数的组合。

一、lambda 表达式


lambda 表达式是 Python 语言中的一个重要特性,它可以用于定义简单的匿名函数。lambda 表达式通常用于高阶函数、列表推导式、字典推导式和装饰器等场景。需要注意的是,lambda 表达式通常只适用于简单的函数定义,复杂的函数定义通常需要使用 def 语句来定义。

lambda 表达式的定义

lambda 表达式是一种匿名函数,可以在需要使用函数的地方定义一个简单的函数。lambda 表达式的语法如下:


lambda arguments: expression1.
复制代码


其中,arguments 表示函数的参数列表,可以有多个参数,用逗号分隔;expression 表示函数的返回值表达式,可以是任意的表达式。

以下是一个使用 lambda 表达式的示例代码:


my_list = [1, 2, 3, 4, 5]result = map(lambda x: x*2, my_list)print(list(result))
复制代码


在上面的代码中,我们使用 lambda 表达式定义了一个匿名函数,并将其传递给了 map()函数。

lambda 表达式的应用场景

lambda 表达式通常用于定义一次性使用的简单函数。例如,在使用 map()、reduce()、filter()等高阶函数时,我们可以使用 lambda 表达式来定义映射、归约和过滤的函数。

以下是一个使用 lambda 表达式的示例代码:


my_list = [1, 2, 3, 4, 5]result = filter(lambda x: x%2==0, my_list)print(list(result))
复制代码


在上面的代码中,我们使用 lambda 表达式定义了一个函数,用于过滤列表中的偶数元素,并将其传递给了 filter()函数。

lambda 表达式的局限性

lambda 表达式通常只适用于简单的函数定义,复杂的函数定义通常需要使用 def 语句来定义。lambda 表达式只能包含一个表达式,并且该表达式的结果将作为函数的返回值。在 lambda 表达式中不能使用语句或赋值操作符。

以下是一个不能使用 lambda 表达式的示例代码:


def my_function():    print("My function")    return 1
my_lambda = lambda: (print("My lambda"), 1)[1]result = my_lambda()print(result)
复制代码


在上面的代码中,我们定义了一个函数 my_function(),该函数包含了打印语句和返回语句。我们尝试使用 lambda 表达式来定义一个相同的函数,但是由于 lambda 表达式只能包含一个表达式,因此我们使用了一个三元表达式来模拟返回语句。

lambda 表达式的高级用法

lambda 表达式可以与其他 Python 的语言特性结合使用,例如列表推导式、字典推导式和装饰器等。

以下是一个使用 lambda 表达式和列表推导式的示例代码:


my_list = [1, 2, 3, 4, 5]result = [(lambda x: x*2)(x) for x in my_list]print(result)
复制代码


在上面的代码中,我们使用 lambda 表达式和列表推导式创建了一个新的列表,该列表包含了原列表中每个元素的两倍。

二、Python 的高阶函数


高阶函数是 Python 函数式编程中的重要概念,它可以使代码更加灵活,并且可以减少代码的重复。Python 中常用的高阶函数包括 map()、reduce()、filter()等。函数可以作为参数传递给其他函数,也可以作为返回值返回给调用者。需要注意的是,高阶函数通常需要使用 lambda 表达式来定义函数,lambda 表达式可以用于定义简单的匿名函数。

高阶函数的定义

高阶函数是指可以接受函数作为参数或返回函数作为结果的函数。Python 中内置了一些高阶函数,包括 map()、reduce()、filter()等。

以下是一个使用 map()函数的示例代码:


my_list = [1, 2, 3, 4, 5]result = map(lambda x: x*2, my_list)print(list(result))
复制代码


在上面的代码中,我们使用 map()函数将一个列表中的元素乘以 2,并使用 list()函数将结果转换为列表。

常用的高阶函数

Python 中常用的高阶函数包括:

  • map()函数:接受一个函数和一个序列作为参数,将函数应用到序列中的每个元素,并返回一个新的序列。

以下是一个使用 map()函数的示例代码:


my_list = [1, 2, 3, 4, 5]result = map(lambda x: x*2, my_list)print(list(result))
复制代码


在上面的代码中,我们使用 map()函数将一个列表中的元素乘以 2,并使用 list()函数将结果转换为列表。

  • reduce()函数:接受一个函数和一个序列作为参数,使用函数将序列中的元素归约为一个单独的值。

以下是一个使用 reduce()函数的示例代码:


from functools import reduce
my_list = [1, 2, 3, 4, 5]result = reduce(lambda x, y: x+y, my_list)print(result)
复制代码


在上面的代码中,我们使用 reduce()函数将一个列表中的元素累加,并返回累加的结果。

  • filter()函数:接受一个函数和一个序列作为参数,使用函数过滤出序列中符合条件的元素,并返回一个新的序列。

以下是一个使用 filter()函数的示例代码:


my_list = [1, 2, 3, 4, 5]result = filter(lambda x: x%2==0, my_list)print(list(result))
复制代码


在上面的代码中,我们使用 filter()函数过滤了一个列表中的偶数元素,并使用 list()函数将结果转换为列表。

函数作为参数

在 Python 中,函数可以作为参数传递给其他函数。这种用法可以使代码更加灵活,并且可以减少代码的重复。

以下是一个使用函数作为参数的示例代码:


def my_function(x):    return x*2
def apply_function(f, lst): return [f(x) for x in lst]
my_list = [1, 2, 3, 4, 5]result = apply_function(my_function, my_list)print(result)
复制代码


在上面的代码中,我们定义了一个函数 my_function(),用于将一个数乘以 2。然后我们定义了一个函数 apply_function(),该函数接受一个函数和一个列表作为参数,将函数应用于列表中的每个元素,并返回一个新的列表。最后,我们将 my_function()函数和 my_list 列表传递给 apply_function()函数,并将其结果保存到 result 变量中。

函数作为返回值

在 Python 中,函数也可以作为返回值返回给调用者。这种用法可以使代码更加灵活,并且可以根据不同的情况返回不同的函数。

以下是一个使用函数作为返回值的示例代码:


def get_math_function(operation):    if operation == '+':        return lambda x, y: x+y    elif operation == '-':        return lambda x, y: x-y    elif operation == '*':        return lambda x, y: x*y    elif operation == '/':        return lambda x, y: x/y
my_function = get_math_function('*')result = my_function(2, 3)print(result)
复制代码


在上面的代码中,我们定义了一个函数 get_math_function(),该函数根据参数返回不同的函数。然后我们调用 get_math_function()函数,传递了参数'*',并将返回的函数保存到 my_function 变量中。最后,我们调用 my_function()函数,将 2 和 3 作为参数传递进去,并将结果保存到 result 变量中。

三、functools 模块


functools 模块是 Python 标准库中的一个模块,提供了一些高阶函数和函数式编程工具。该模块可以用于实现函数柯里化、偏函数、缓存等功能。functools 模块中常用的函数包括 partial()函数、lru_cache()函数、wraps()函数、cmp_to_key()函数等。需要注意的是,functools 模块中的函数通常需要和其他函数一起使用,以便实现更加复杂的功能。

functools 模块的介绍

functools 模块是 Python 标准库中的一个模块,提供了一些高阶函数和函数式编程工具。该模块可以用于实现函数柯里化、偏函数、缓存等功能。

以下是一个使用 functools 模块的示例代码:


import functools
def my_function(x, y): return x*y
my_partial = functools.partial(my_function, y=2)result = my_partial(3)print(result)
复制代码


在上面的代码中,我们使用 functools 模块中的 partial()函数创建了一个偏函数 my_partial,该偏函数将 my_function 函数的第二个参数固定为 2。然后我们调用 my_partial()函数,将 3 作为 my_function()函数的第一个参数传递进去,并将结果保存到 result 变量中。

partial()函数

partial()函数是 functools 模块中的一个函数,用于创建偏函数。偏函数是指将一个函数的部分参数固定,返回一个新的函数。

以下是一个使用 partial()函数的示例代码:


import functools
def my_function(x, y): return x*y
my_partial = functools.partial(my_function, y=2)result = my_partial(3)print(result)
复制代码


在上面的代码中,我们使用 partial()函数创建了一个偏函数 my_partial,该偏函数将 my_function 函数的第二个参数固定为 2。然后我们调用 my_partial()函数,将 3 作为 my_function()函数的第一个参数传递进去,并将结果保存到 result 变量中。

lru_cache()函数

lru_cache()函数是 functools 模块中的一个函数,用于创建一个缓存,可以缓存函数的调用结果,避免重复计算。

以下是一个使用 lru_cache()函数的示例代码:


import functools
@functools.lru_cache()def my_function(x): print("Calculating...") return x*x
result = my_function(2)print(result)
result = my_function(2)print(result)
复制代码


在上面的代码中,我们使用 lru_cache()函数创建了一个缓存,用于缓存 my_function()函数的调用结果。然后我们调用 my_function()函数,将 2 作为参数传递进去,并将结果保存到 result 变量中。在第二次调用 my_function()函数时,由于之前已经计算过了,所以直接从缓存中获取结果,不再进行计算。

wraps()函数

wraps()函数是 functools 模块中的一个函数,用于定义一个装饰器,该装饰器用于将被装饰函数的__name__、doc、__module__等属性复制到装饰器函数中。

以下是一个使用 wraps()函数的示例代码:


import functools
def my_decorator(func): @functools.wraps(func) def wrapper(*args, **kwargs): print("Before...") result = func(*args, **kwargs) print("After...") return result return wrapper
@my_decoratordef my_function(x): """ This is my function. """ return x*x
result = my_function(2)print(result)print(my_function.__name__)print(my_function.__doc__)
复制代码


在上面的代码中,我们定义了一个装饰器 my_decorator,该装饰器用于在被装饰函数执行前后打印一些信息。然后我们使用 wraps()函数将被装饰函数的属性复制到装饰器函数中。最后,我们使用 my_decorator 装饰了一个函数 my_function,并调用该函数。

cmp_to_key()函数

cmp_to_key()函数是 functools 模块中的一个函数,用于将旧式的比较函数转换为键函数。在 Python 2.x 中,比较函数用于比较两个元素的大小;在 Python 3.x 中,比较函数已经被移除,取而代之的是键函数。

以下是一个使用 cmp_to_key()函数的示例代码:


import functools
def my_compare(x, y): if x < y: return -1 elif x > y: return 1 else: return 0
my_list = [5, 3, 2, 8, 7]my_key = functools.cmp_to_key(my_compare)my_list.sort(key=my_key)print(my_list)
复制代码

在上面的代码中,我们定义了一个旧式的比较函数 my_compare,用于比较两个元素的大小。然后我们使用 cmp_to_key()函数将该函数转换为键函数 my_key。最后,我们使用 my_list.sort()函数,并将 my_key 作为参数传递进去,对 my_list 进行排序。

四、Python 生成器


生成器是一种特殊的迭代器,可以动态地生成数据,可以通过函数或生成器表达式来创建。生成器具有惰性计算、无限序列、流式处理等优点,可以用于处理大量数据、生成无限序列、实现协程和异步编程等。需要注意的是,生成器只能迭代一次、不能使用切片操作、需要及时关闭等问题。

生成器的定义

生成器是一种特殊的迭代器,它可以在循环中动态地生成数据,而不是在一开始就生成所有数据。生成器可以通过函数或生成器表达式来创建。

以下是一个使用生成器表达式创建生成器的示例代码:


my_generator = (x*x for x in range(10))print(list(my_generator))1.2.
复制代码


在上面的代码中,我们使用生成器表达式创建了一个生成器 my_generator,该生成器可以动态地生成 0 到 9 的平方,并使用 list()函数将其转换为列表。

生成器的工作原理

生成器的工作原理可以简单地描述为:每次调用生成器的__next__()方法时,它会执行到下一个 yield 语句,并返回该语句的值。当所有的 yield 语句都执行完毕后,生成器会自动抛出 StopIteration 异常,表示迭代结束。

以下是一个使用 yield 语句创建生成器的示例代码:


def my_generator():    for i in range(10):        yield i*i
gen = my_generator()print(list(gen))1.2.3.4.5.6.
复制代码


在上面的代码中,我们使用 yield 语句在函数中创建了一个生成器。每次调用生成器的__next__()方法时,它会执行到下一个 yield 语句,并返回该语句的值。最后,我们使用 list()函数将生成器转换为列表。

生成器的优点

生成器具有以下优点:

  • 惰性计算:生成器不会一开始就生成所有数据,而是在需要时才生成数据,可以节省大量的内存空间。

  • 无限序列:生成器可以用来生成无限序列,例如斐波那契数列、素数序列等。

  • 可以用于流式处理:生成器可以用于流式处理大量数据,例如读取大文件、网络数据等。

以下是一个使用生成器处理大文件的示例代码:


def read_file(file_path):    with open(file_path) as f:        for line in f:            yield line.strip()
for line in read_file("large_file.txt"): process(line)1.2.3.4.5.6.7.
复制代码


在上面的代码中,我们定义了一个生成器 read_file(),用于读取大文件,每次返回一行数据。然后我们使用 for 循环遍历生成器,对每行数据进行处理。

生成器的应用场景

生成器可以用于以下场景:

  • 处理大量数据,例如大文件、网络数据等。

  • 生成无限序列,例如斐波那契数列、素数序列等。

  • 实现协程和异步编程,例如使用 asyncio 库实现异步 IO 操作。

  • 生成器还可以用于实现管道和过滤器模式,例如使用生成器实现 Unix 管道。

生成器的注意事项

生成器虽然有很多优点,但也需要注意以下事项:

  • 生成器只能迭代一次:生成器只能迭代一次,因为迭代完毕后会自动抛出 StopIteration 异常。

  • 生成器不能使用切片操作:由于生成器是惰性计算的,因此不能使用切片操作,否则会导致生成器提前终止。

  • 生成器需要及时关闭:生成器在使用完毕后需要及时关闭,否则可能会导致资源泄漏和内存泄漏等问题。

五、Python 装饰器


装饰器是一种 Python 语言的语法糖,用于修改或增强函数或类的功能。装饰器本身是一个函数,接收一个函数作为参数,并返回一个新的函数,新函数在调用原函数前后执行一些额外的操作,最后返回原函数的返回值。装饰器可以用于记录日志、计时器、缓存、权限控制、重试机制等场景。使用装饰器时需要注意函数的定义和调用规则、装饰器的参数、嵌套使用、保留原函数的元信息等问题。

装饰器的定义

装饰器是一种 Python 语言的语法糖,用于修改或增强函数或类的功能。装饰器可以在不修改原函数或类的情况下,动态地给它们添加额外的功能。

以下是一个使用装饰器增强函数功能的示例代码:


def my_decorator(func):    def wrapper():        print("Before function call")        func()        print("After function call")    return wrapper
@my_decoratordef my_function(): print("Inside function")
my_function()
复制代码


在上面的代码中,我们定义了一个装饰器 my_decorator,用于在函数调用前后打印一些信息。然后我们使用 @my_decorator 语法糖将装饰器应用到函数 my_function 上。

装饰器的工作原理

装饰器的工作原理可以简单地描述为:装饰器本身是一个函数,它接收一个函数作为参数,然后返回一个新的函数,新函数在调用原函数前后执行一些额外的操作,最后返回原函数的返回值。

以下是一个使用装饰器增强函数功能的示例代码:


def my_decorator(func):    def wrapper():        print("Before function call")        func()        print("After function call")    return wrapper
@my_decoratordef my_function(): print("Inside function")
my_function()
复制代码


在上面的代码中,装饰器 my_decorator 接收一个函数作为参数,并返回一个新的函数 wrapper。在调用 my_function()函数时,实际上调用的是 wrapper()函数,该函数在调用原函数前后打印一些信息,并执行原函数。最后,wrapper()函数返回原函数的返回值。

装饰器的应用场景

装饰器可以用于以下场景:

  • 记录日志:可以使用装饰器记录函数的调用日志,例如记录函数的参数、返回值、执行时间等。

  • 计时器:可以使用装饰器实现一个计时器,用于计算函数的执行时间。

  • 缓存:可以使用装饰器实现一个缓存,缓存函数的调用结果,避免重复计算。

  • 权限控制:可以使用装饰器实现一个权限控制,限制只有特定的用户或角色才能调用函数。

  • 重试机制:可以使用装饰器实现一个重试机制,当函数调用失败时,自动重试多次。

以下是一个使用装饰器实现缓存功能的示例代码:


import functools
def cache(func): cache_dict = {} @functools.wraps(func) def wrapper(*args, **kwargs): key = args + tuple(kwargs.items()) if key not in cache_dict: cache_dict[key] = func(*args, **kwargs) return cache_dict[key] return wrapper
@cachedef my_function(x, y): print("Calculating...") return x*y
print(my_function(2, 3))print(my_function(2, 3))
复制代码


在上面的代码中,我们定义了一个 cache 装饰器,用于缓存函数的调用结果。然后我们使用 @cache 语法糖将装饰器应用到函数 my_function 上。

装饰器的注意事项

使用装饰器时需要注意以下事项:

  • 装饰器本身也是一个函数,因此需要遵循函数的定义和调用规则。

  • 装饰器可以接收参数,但是需要在装饰器内部再定义一层函数来接收参数。

  • 装饰器可以嵌套使用,但是需要注意函数调用顺序。

  • 装饰器可以使用 functools.wraps()函数来保留原函数的元信息,例如函数名、文档字符串等。

六、Python 列表推导式和字典推导式


列表推导式和字典推导式是一种简洁而强大的 Python 语法,用于生成新的列表和字典。它们的工作原理都是通过一个 for 循环迭代一个可迭代对象,对每个元素进行操作,并将结果添加到一个新的列表或字典中。它们可以用于筛选数据、转换数据、生成新列表或字典等场景。使用列表推导式和字典推导式时需要注意代码的可读性、if 语句的位置和条件、for 语句的顺序等问题。

列表推导式的定义

列表推导式是一种简洁而强大的 Python 语法,用于生成新的列表。列表推导式通常使用一行代码就可以完成复杂的列表操作。

以下是一个使用列表推导式生成新列表的示例代码:


my_list = [x*x for x in range(10)]print(my_list)
复制代码


在上面的代码中,我们使用列表推导式生成一个新的列表,该列表包含 0 到 9 的平方。

列表推导式的工作原理

列表推导式的工作原理可以简单地描述为:通过一个 for 循环迭代一个可迭代对象,对每个元素进行操作,并将结果添加到一个新的列表中。

以下是一个使用列表推导式生成新列表的示例代码:


my_list = [x*x for x in range(10)]print(my_list)
复制代码


在上面的代码中,for 循环迭代 0 到 9 的整数,对每个整数进行平方操作,并将结果添加到一个新的列表中。

列表推导式的应用场景

列表推导式可以用于以下场景:

  • 筛选数据:可以使用列表推导式根据条件筛选列表中的数据。

  • 转换数据:可以使用列表推导式将列表中的数据进行转换,例如将字符串列表转换为整数列表。

  • 生成新列表:可以使用列表推导式生成新的列表,例如生成斐波那契数列、素数序列等。

以下是一个使用列表推导式将列表中的字符串转换为整数的示例代码:


my_list = ["1", "2", "3", "4", "5"]new_list = [int(x) for x in my_list]print(new_list)
复制代码


在上面的代码中,我们使用列表推导式将字符串列表 my_list 中的元素转换为整数,并将结果存储到新的列表 new_list 中。

字典推导式的定义

字典推导式是一种简洁而强大的 Python 语法,用于生成新的字典。字典推导式通常使用一行代码就可以完成复杂的字典操作。

以下是一个使用字典推导式生成新字典的示例代码:


my_dict = {x: x*x for x in range(10)}print(my_dict)
复制代码


在上面的代码中,我们使用字典推导式生成一个新的字典,该字典包含 0 到 9 的整数及其平方。

字典推导式的工作原理

字典推导式的工作原理可以简单地描述为:通过一个 for 循环迭代一个可迭代对象,对每个元素进行操作,并将结果添加到一个新的字典中。

以下是一个使用字典推导式生成新字典的示例代码:


my_dict = {x: x*x for x in range(10)}print(my_dict)
复制代码


在上面的代码中,for 循环迭代 0 到 9 的整数,对每个整数进行平方操作,并将结果添加到一个新的字典中。

字典推导式的应用场景

字典推导式可以用于以下场景:

  • 筛选数据:可以使用字典推导式根据条件筛选字典中的数据。

  • 转换数据:可以使用字典推导式将字典中的数据进行转换,例如将字典中的字符串值转换为整数值。

  • 生成新字典:可以使用字典推导式生成新的字典,例如将一个列表转换为字典,其中列表元素作为字典的键,另一个可迭代对象中的元素作为字典的值。

以下是一个使用字典推导式将字典中的字符串值转换为整数值的示例代码:


my_dict = {"a": "1", "b": "2", "c": "3", "d": "4", "e": "5"}new_dict = {k: int(v) for k, v in my_dict.items()}print(new_dict)
复制代码


在上面的代码中,我们使用字典推导式将字典 my_dict 中的字符串值转换为整数值,并将结果存储到新的字典 new_dict 中。

列表推导式和字典推导式的注意事项

使用列表推导式和字典推导式时需要注意以下事项:

  • 列表推导式和字典推导式可以嵌套使用,但是需要注意代码的可读性。

  • 列表推导式和字典推导式可以使用 if 语句进行筛选,需要注意 if 语句的位置和条件。

  • 列表推导式和字典推导式可以使用多个 for 语句进行嵌套,需要注意 for 语句的顺序。

七、Python 中的函数式编程库


Python 中的函数式编程库有很多,每个库都有其特点和适用场景。其中,functools 和 itertools 是 Python 标准库的一部分,可以直接导入使用,功能相对简单;而 toolz、fn.py 和 PyMonad 需要安装,提供了一些高级函数式编程功能,适用于一些复杂的函数式编程场景。使用函数式编程库时,需要根据具体场景选择适当的库,以达到最佳的编程效果。


以下是一些常用的函数式编程库及其功能作用、使用区别、使用场景、优缺点等介绍:

functools

  • 功能作用:提供了一些高阶函数,例如 partial、reduce、wraps 等,可以方便地操作函数。

  • 使用区别:functools 是 Python 标准库的一部分,无需安装,可以直接导入使用。

  • 使用场景:可以用于函数的柯里化、函数的装饰器、函数的元信息保留等场景。

  • 优点:无需安装,使用方便,功能丰富。

  • 缺点:功能相对简单,不适用于一些复杂的函数式编程场景。

itertools

  • 功能作用:提供了一些迭代器函数,例如 product、permutations、combinations 等,可以方便地生成迭代器。

  • 使用区别:itertools 是 Python 标准库的一部分,无需安装,可以直接导入使用。

  • 使用场景:可以用于生成排列组合、笛卡尔积、循环迭代等场景。

  • 优点:无需安装,使用方便,功能丰富。

  • 缺点:只提供了一些迭代器函数,不适用于一些复杂的函数式编程场景。

toolz

  • 功能作用:提供了一些高阶函数,例如 curry、compose、pipe 等,可以方便地操作函数。

  • 使用区别:toolz 需要安装,可以使用 pip 命令安装,例如 pip install toolz。

  • 使用场景:可以用于函数的柯里化、函数的组合、懒计算等场景。

  • 优点:提供了一些高级函数式编程功能,适用于一些复杂的函数式编程场景。

  • 缺点:需要安装,使用稍微复杂一些。

fn.py

  • 功能作用:提供了一些高阶函数,例如 curry、compose、zip_with 等,可以方便地操作函数。

  • 使用区别:fn.py 需要安装,可以使用 pip 命令安装,例如 pip install fn.

  • 使用场景:可以用于函数的柯里化、函数的组合、懒计算、惰性序列等场景。

  • 优点:提供了一些高级函数式编程功能,适用于一些复杂的函数式编程场景。

  • 缺点:需要安装,使用稍微复杂一些。

PyMonad

  • 功能作用:提供了一些基本的单子类型,例如 Maybe、Either、State 等,可以方便地实现单子模式。

  • 使用区别:PyMonad 需要安装,可以使用 pip 命令安装,例如 pip install PyMonad。

  • 使用场景:可以用于实现单子模式、函数式编程中的异常处理、状态管理等场景。

  • 优点:提供了一些基本的单子类型,方便实现单子模式。

  • 缺点:功能相对简单,不适用于一些复杂的函数式编程场景。

发布于: 23 小时前阅读数: 23
用户头像

还未添加个人签名 2023-06-19 加入

还未添加个人简介

评论

发布
暂无评论
你还在用命令式编程?Python函数式编程让你的代码更优雅!_Python_高端章鱼哥_InfoQ写作社区