MK
摩柯社区 - 一个极简的技术知识社区
AI 面试

Python高阶函数与lambda表达式

2021-02-113.4k 阅读

Python高阶函数

在Python编程中,高阶函数(Higher - order function)是一种强大而重要的概念。它极大地增强了Python语言的表达能力,为解决复杂问题提供了简洁高效的编程方式。

什么是高阶函数

简单来说,高阶函数是指满足以下至少一个条件的函数:

  1. 接受一个或多个函数作为参数。
  2. 返回一个函数作为结果。

在Python中,函数是一等公民(First - class citizen)。这意味着函数可以像普通数据类型(如整数、字符串、列表等)一样被传递、赋值、作为参数传递给其他函数以及作为函数的返回值。这种特性使得高阶函数成为可能。

接受函数作为参数的高阶函数

一个经典的例子是 sorted 函数。sorted 函数用于对可迭代对象进行排序,它可以接受一个 key 参数,这个 key 参数就是一个函数。key 函数会作用于可迭代对象的每个元素,根据 key 函数返回值来进行排序。

students = [
    {'name': 'Alice', 'age': 20},
    {'name': 'Bob', 'age': 18},
    {'name': 'Charlie', 'age': 22}
]

# 根据年龄对学生列表进行排序
sorted_students = sorted(students, key=lambda student: student['age'])
print(sorted_students)

在上述代码中,sorted 函数接受了一个 lambda 表达式作为 key 参数。这个 lambda 表达式定义了如何从每个学生字典中提取用于排序的关键字(即年龄)。

另一个常用的接受函数作为参数的高阶函数是 map 函数。map 函数接受一个函数和一个或多个可迭代对象作为参数,它会将这个函数依次应用到可迭代对象的每个元素上,并返回一个迭代器,其中包含函数应用后的结果。

numbers = [1, 2, 3, 4, 5]
squared_numbers = list(map(lambda x: x ** 2, numbers))
print(squared_numbers)

这里,map 函数将 lambda 表达式 lambda x: x ** 2 应用到 numbers 列表的每个元素上,从而得到一个新的列表,其中每个元素都是原列表对应元素的平方。

filter 函数也是类似的高阶函数。它接受一个函数和一个可迭代对象作为参数,会过滤掉可迭代对象中使得函数返回 False 的元素,并返回一个迭代器,其中包含符合条件(即函数返回 True)的元素。

numbers = [1, 2, 3, 4, 5]
even_numbers = list(filter(lambda x: x % 2 == 0, numbers))
print(even_numbers)

在这段代码中,filter 函数使用 lambda 表达式 lambda x: x % 2 == 0 来过滤出 numbers 列表中的偶数。

返回函数的高阶函数

Python中也有很多函数返回一个函数。例如,functools.partial 函数。partial 函数用于创建一个新的可调用对象,这个新对象会固定原函数的某些参数,返回一个简化了参数列表的新函数。

import functools


def add(a, b):
    return a + b


add_five = functools.partial(add, 5)
result = add_five(3)
print(result)

在上述代码中,functools.partial 函数将 add 函数的第一个参数固定为 5,返回了一个新的函数 add_fiveadd_five 函数只需要一个参数 b,在调用 add_five(3) 时,实际上是调用 add(5, 3),所以结果为 8

lambda表达式

lambda 表达式,也称为匿名函数,是Python中一种简洁的定义函数的方式。它允许在需要函数的地方快速定义一个简单的函数,而无需使用常规的 def 语句。

lambda表达式的语法

lambda 表达式的基本语法如下:

lambda arguments: expression
  • arguments 是函数的参数,可以有多个,用逗号分隔。
  • expression 是函数的返回值,它只能是一个表达式,不能包含多条语句。

例如,定义一个简单的 lambda 函数来计算两个数的和:

add = lambda a, b: a + b
result = add(3, 5)
print(result)

这里,lambda a, b: a + b 定义了一个接受两个参数 ab,并返回它们之和的函数。然后将这个函数赋值给变量 add,通过调用 add(3, 5) 得到计算结果。

lambda表达式与高阶函数的结合使用

如前面 sortedmapfilter 函数的例子所示,lambda 表达式与高阶函数结合使用可以极大地简化代码。它提供了一种简洁的方式来定义在高阶函数中使用的回调函数。

再比如,使用 reduce 函数(在Python 3中,reduce 函数被移动到了 functools 模块中)与 lambda 表达式来计算列表元素的乘积:

from functools import reduce


numbers = [1, 2, 3, 4, 5]
product = reduce(lambda x, y: x * y, numbers)
print(product)

在这个例子中,reduce 函数会将 lambda 表达式 lambda x, y: x * y 依次应用到 numbers 列表的元素上。它首先将列表的前两个元素作为 xy 传递给 lambda 表达式,得到一个结果,然后将这个结果与列表的下一个元素作为新的 xy 再次传递给 lambda 表达式,如此类推,直到处理完所有元素,最终得到列表元素的乘积。

lambda表达式的局限性

虽然 lambda 表达式非常简洁方便,但它也有一些局限性。由于 lambda 表达式只能包含一个表达式,不能包含多条语句,这使得它只能用于定义非常简单的函数。如果函数逻辑比较复杂,需要多条语句来实现,使用 def 语句定义常规函数会更加合适。

例如,下面这样复杂的函数逻辑就不适合用 lambda 表达式实现:

def complex_function(x):
    if x > 10:
        return x * 2
    else:
        return x + 10

这种情况下,使用 def 语句可以更清晰地表达函数的逻辑。

lambda表达式在实际应用中的场景

  1. 数据处理和转换:在处理数据集合时,lambda 表达式与高阶函数结合可以方便地对数据进行各种转换。例如,从一个包含字典的列表中提取特定字段的值。
students = [
    {'name': 'Alice', 'age': 20},
    {'name': 'Bob', 'age': 18},
    {'name': 'Charlie', 'age': 22}
]
ages = list(map(lambda student: student['age'], students))
print(ages)
  1. 排序和过滤:在对数据进行排序或过滤时,lambda 表达式可以简洁地定义排序关键字或过滤条件。前面 sortedfilter 函数的例子已经很好地说明了这一点。
  2. 事件处理:在图形用户界面(GUI)编程或其他事件驱动的编程场景中,lambda 表达式可以方便地定义事件处理函数。例如,在Tkinter库中:
import tkinter as tk


def create_button(root):
    button = tk.Button(root, text="Click me", command=lambda: print("Button clicked"))
    button.pack()


root = tk.Tk()
create_button(root)
root.mainloop()

这里,lambda 表达式定义了按钮被点击时执行的操作,即打印 "Button clicked"。

深入理解lambda表达式的作用域

lambda 表达式的作用域遵循Python的一般作用域规则。它可以访问其定义所在的外部作用域中的变量,但不能在其内部重新绑定外部作用域的变量(除非使用 nonlocal 关键字,这在 lambda 表达式中很少使用)。

x = 10
func = lambda y: x + y
result = func(5)
print(result)

在这个例子中,lambda 表达式 lambda y: x + y 可以访问外部作用域中的变量 x。当调用 func(5) 时,它会将 x 的值(即 10)与参数 y(即 5)相加,返回结果 15

然而,如果尝试在 lambda 表达式内部修改外部作用域的变量,会导致错误:

x = 10
func = lambda: x + 1  # 这是可以的,只是读取x
# func = lambda: x = x + 1  # 这会导致语法错误,不能在lambda内部重新绑定x

lambda表达式与常规函数的性能比较

在性能方面,对于简单的操作,lambda 表达式和常规函数的性能差异通常可以忽略不计。然而,对于复杂的函数逻辑,由于 lambda 表达式的限制,它可能需要更多的嵌套或复杂的表达式来实现相同功能,这可能会在一定程度上影响性能。

为了进行性能测试,可以使用 timeit 模块。例如,比较计算两个数之和的 lambda 表达式和常规函数的执行时间:

import timeit


def add_func(a, b):
    return a + b


add_lambda = lambda a, b: a + b


print(timeit.timeit(lambda: add_func(3, 5), number = 1000000))
print(timeit.timeit(lambda: add_lambda(3, 5), number = 1000000))

在大多数情况下,这两者的执行时间差异非常小,几乎可以忽略不计。但对于更复杂的函数逻辑和大规模的计算,性能差异可能会稍微明显一些。

lambda表达式在函数式编程中的角色

lambda 表达式是Python支持函数式编程风格的重要组成部分。函数式编程强调使用纯函数(即不产生副作用且对于相同输入总是返回相同输出的函数),lambda 表达式非常适合定义这种纯函数。通过与高阶函数结合,如 mapfilterreduce,可以实现函数式编程中的一些常见操作,如映射、过滤和归约。

例如,使用函数式编程风格计算列表中所有偶数的平方和:

from functools import reduce


numbers = [1, 2, 3, 4, 5]
result = reduce(lambda acc, num: acc + num ** 2 if num % 2 == 0 else acc, numbers, 0)
print(result)

这里,reduce 函数与 lambda 表达式一起实现了对列表中偶数平方的累加操作。lambda 表达式作为纯函数,根据输入的元素和当前的累加结果决定是否进行平方并累加操作。

如何正确使用lambda表达式以提高代码可读性

虽然 lambda 表达式简洁,但过度使用或使用不当可能会降低代码的可读性。为了提高代码可读性,在使用 lambda 表达式时应注意以下几点:

  1. 保持简单:只在函数逻辑非常简单,一眼就能看明白的情况下使用 lambda 表达式。例如,简单的数学计算、提取字典中的某个字段等。
  2. 添加注释:如果 lambda 表达式的逻辑不是非常直观,添加注释来解释其功能。
  3. 避免嵌套过深:尽量避免在 lambda 表达式中进行过多的嵌套。如果嵌套层次过多,应考虑使用常规函数来提高代码的清晰度。

例如,下面的代码虽然使用 lambda 表达式实现了一个相对复杂的逻辑,但通过添加注释,使得代码更易读:

# 计算列表中所有大于10且为偶数的元素的立方和
numbers = [5, 12, 15, 18, 20]
result = reduce(lambda acc, num: acc + num ** 3 if num > 10 and num % 2 == 0 else acc, numbers, 0)
print(result)

lambda表达式在不同Python版本中的变化

在Python的发展过程中,lambda 表达式的基本语法和功能保持相对稳定。然而,随着Python对函数式编程支持的不断完善,lambda 表达式与其他新特性的结合使用方式可能会有所变化。

例如,在Python 3中,mapfilterreduce 函数的行为略有改变。mapfilter 函数返回的是迭代器而不是列表,这在与 lambda 表达式结合使用时,需要注意迭代器的特性。而 reduce 函数被移动到了 functools 模块中,使用时需要先导入。

这些变化旨在提高Python的性能和内存使用效率,同时也鼓励开发者使用更符合函数式编程范式的方式来编写代码。

实际项目中lambda表达式的应用案例

  1. 数据分析:在使用Pandas库进行数据分析时,lambda 表达式经常用于对数据帧(DataFrame)中的数据进行处理。例如,根据某一列的值创建新的列:
import pandas as pd


data = {'col1': [1, 2, 3, 4, 5]}
df = pd.DataFrame(data)
df['new_col'] = df['col1'].apply(lambda x: x * 10 if x > 3 else x)
print(df)

这里,apply 方法类似于 map 函数,将 lambda 表达式应用到 col1 列的每个元素上,根据条件创建了新的列 new_col

  1. 机器学习:在机器学习算法的实现中,lambda 表达式可用于定义损失函数、激活函数等。例如,在简单的线性回归模型中定义损失函数:
import numpy as np


def linear_regression_loss(y_true, y_pred):
    return np.mean((y_true - y_pred) ** 2)


# 使用lambda表达式定义相同的损失函数
lambda_loss = lambda y_true, y_pred: np.mean((y_true - y_pred) ** 2)

虽然在实际的机器学习库中,损失函数通常已经有预定义的实现,但通过 lambda 表达式可以快速自定义一些简单的损失函数用于实验或特定需求。

  1. Web开发:在Web开发框架(如Flask)中,lambda 表达式可以用于定义简单的路由处理函数。例如:
from flask import Flask


app = Flask(__name__)


@app.route('/')
def index():
    return "Hello, World!"


# 使用lambda表达式定义一个简单的路由
app.route('/about')(lambda: "This is an about page")


if __name__ == '__main__':
    app.run()

这里,使用 lambda 表达式快速定义了一个处理 /about 路由的简单函数。

总结lambda表达式和高阶函数的关系

高阶函数为Python编程带来了强大的抽象能力和代码复用性,而 lambda 表达式则为在高阶函数中快速定义简单的回调函数提供了便利。它们相互配合,使得Python在处理数据集合、实现函数式编程风格等方面表现出色。通过深入理解和熟练运用高阶函数与 lambda 表达式,开发者可以编写出更加简洁、高效且富有表现力的Python代码。无论是在数据处理、算法实现还是Web开发等各种领域,它们都有着广泛的应用场景,是Python开发者不可或缺的工具。

希望通过本文的详细介绍,读者能对Python的高阶函数和 lambda 表达式有更深入的理解,并在实际编程中灵活运用它们,提升编程效率和代码质量。在不断实践和探索中,还会发现更多高阶函数与 lambda 表达式的奇妙用法,为Python编程之旅增添更多乐趣。

以上就是关于Python高阶函数与 lambda 表达式的详细介绍,包括它们的概念、用法、结合方式、实际应用以及相关注意事项等内容。通过丰富的代码示例,希望能帮助读者更好地掌握这两个重要的Python特性。