内置函数
处理学生数据时,我们经常要完成批量转换、条件筛选、数据配对和排序。如果每次都从头编写循环,代码会比较重复。如何直接复用已有函数,把这些处理步骤连接起来?
Python 已经提供了很多内置函数。本节先解决五类常见问题:
map():批量转换数据filter():筛选数据zip():按位置配对数据sorted():排序数据eval():计算字符串表达式
还会学习适合编写简单匿名函数的 lambda 表达式。
下面的列表用于速查,不要求背诵。完整接口以 Python 3.14 内置函数文档为准。
- A:
abs()、aiter()、all()、anext()、any()、ascii() - B:
bin()、bool()、breakpoint()、bytearray()、bytes() - C:
callable()、chr()、classmethod()、compile()、complex() - D:
delattr()、dict()、dir()、divmod() - E:
enumerate()、eval()、exec() - F:
filter()、float()、format()、frozenset() - G:
getattr()、globals() - H:
hasattr()、hash()、help()、hex() - I:
id()、input()、int()、isinstance()、issubclass()、iter() - L:
len()、list()、locals() - M:
map()、max()、memoryview()、min() - N:
next() - O:
object()、oct()、open()、ord() - P:
pow()、print()、property() - R:
range()、repr()、reversed()、round() - S:
set()、setattr()、slice()、sorted()、staticmethod()、str()、sum()、super() - T:
tuple()、type() - V:
vars() - Z:
zip() - _:
__import__()
上一节已经知道,函数可以作为参数传给另一个函数。接下来的 map()、filter() 和 sorted() 都会用到这个特点。
map():批量转换
map() 会把指定函数依次应用到每个元素上:
map(function, iterable)function:处理单个元素的函数iterable:提供原始数据的可遍历对象
例如,把一组数字统一转换成字符串:
numbers = [1, 2, 3, 4, 5]
result = map(str, numbers)
print(result)
print(list(result))运行结果中的第一行会显示一个 map 对象,第二行才是转换后的列表:
<map object at ...>
['1', '2', '3', '4', '5']Python 3.14 中,map() 返回迭代器,不会直接返回列表。为了查看全部结果,可以使用 list() 转换。
自定义转换:
下面判断 0 到 4 中的每个数是不是奇数:
def is_odd(number):
return number % 2 == 1
result = map(is_odd, range(5))
print(list(result))运行结果:
[False, True, False, True, False]map() 保留原来的元素数量,只把每个元素转换成函数返回的新结果。
再看一个使用内置函数 abs() 的例子:
numbers = [1, -3, 5, 6, -2, 4]
print(list(map(abs, numbers)))运行结果:
[1, 3, 5, 6, 2, 4]filter():条件筛选
filter() 会保留判断结果为真的元素:
filter(function, iterable)传入的函数负责判断一个元素是否应该保留。
def is_odd(number):
return number % 2 == 1
result = filter(is_odd, range(10))
print(list(result))运行结果:
[1, 3, 5, 7, 9]filter() 和 map() 一样返回迭代器,因此示例中使用 list() 查看全部结果。
案例:筛选三位回文数
回文数从左向右读和从右向左读都一样,例如 121、232。
def is_palindrome(number):
text = str(number)
return text == text[::-1]
result = list(filter(is_palindrome, range(100, 1000)))
print("回文数数量:", len(result))
print("前 10 个:", result[:10])运行结果:
回文数数量: 90
前 10 个: [101, 111, 121, 131, 141, 151, 161, 171, 181, 191]这里仍然筛选了全部三位回文数,只截取前 10 个展示,避免输出过长。
zip():数据配对
zip() 会按照位置,把多个可遍历对象中的元素配对:
names = ["张三", "李四", "王五"]
scores = [88, 92, 85]
result = zip(names, scores)
print(list(result))运行结果:
[('张三', 88), ('李四', 92), ('王五', 85)]zip() 返回一个迭代器。每次配对得到一个元组,使用 list() 后才会得到由元组组成的列表。
长度不一致:
如果数据长度不同,zip() 默认配对到最短的数据用完为止:
names = ["张三", "李四", "王五"]
scores = [88, 92]
print(list(zip(names, scores)))运行结果:
[('张三', 88), ('李四', 92)]王五 没有对应的成绩,因此不会出现在结果中。
sorted():数据排序
sorted() 会返回一个新的排序列表,不会修改原数据:
sorted(iterable, *, key=None, reverse=False)| 参数 | 作用 |
|---|---|
iterable | 要排序的可遍历对象 |
key | 指定从每个元素中取什么值比较 |
reverse | 是否倒序,默认为 False |
默认顺序与倒序:
scores = [75, 92, 88, 60]
print(sorted(scores))
print(sorted(scores, reverse=True))
print(scores)运行结果:
[60, 75, 88, 92]
[92, 88, 75, 60]
[75, 92, 88, 60]最后一行说明原列表 scores 没有改变。
key 排序规则
学生数据是元组时,默认会先比较第一个元素:
students = [("Bob", 75), ("Adam", 92), ("Lisa", 88)]
print(sorted(students))运行结果:
[('Adam', 92), ('Bob', 75), ('Lisa', 88)]如果要按照成绩排序,可以编写一个函数,让它返回元组中的成绩:
def get_score(student):
return student[1]
students = [("Bob", 75), ("Adam", 92), ("Lisa", 88)]
print(sorted(students, key=get_score))运行结果:
[('Bob', 75), ('Lisa', 88), ('Adam', 92)]sorted() 会把每个学生元组交给 get_score(),再使用返回的成绩比较大小。
key 还可以接收内置函数。例如,按照字符串长度排序:
animals = ["Dog", "Cat", "Rabbit"]
print(sorted(animals, key=len))
print(sorted(animals, key=str.lower))运行结果:
['Dog', 'Cat', 'Rabbit']
['Cat', 'Dog', 'Rabbit']lambda 表达式
如果一个函数逻辑很短,而且只使用一次,可以使用 lambda 表达式:
lambda 参数: 表达式lambda 会创建函数,但不需要使用 def 编写函数名和函数体。
下面两种写法效果相同:
def add(number1, number2):
return number1 + number2
add_lambda = lambda number1, number2: number1 + number2
print(add(12, 34))
print(add_lambda(12, 34))运行结果:
46
46lambda 的冒号后面只能写一个表达式,这个表达式的结果就是返回值。
filter() 中的 lambda:
numbers = range(10)
result = filter(lambda number: number % 2 == 1, numbers)
print(list(result))运行结果:
[1, 3, 5, 7, 9]筛选非空字符串也可以使用 lambda:
texts = ["A", "", "B", None, "C", ""]
result = filter(lambda text: text and text.strip(), texts)
print(list(result))运行结果:
['A', 'B', 'C']sorted() 中的 lambda:
前面的成绩排序函数只有一行,可以改成 lambda:
students = [("Bob", 75), ("Adam", 92), ("Lisa", 88)]
result = sorted(students, key=lambda student: student[1])
print(result)运行结果:
[('Bob', 75), ('Lisa', 88), ('Adam', 92)]如果函数逻辑较长、需要重复使用,仍然优先使用 def 定义具名函数。
案例:成绩排行榜
需求:把学生姓名和成绩处理成按成绩倒序排列的排行榜,只保留及格学生,并生成可直接输出的文本。
功能分析:依次使用 zip() 配对、filter() 筛选、sorted() 排序和 map() 转换。
具体步骤:
- 把学生姓名和成绩配对
- 只保留不低于
60分的学生 - 按照成绩从高到低排序
- 生成方便输出的文本
names = ["张三", "李四", "王五", "赵六"]
scores = [88, 52, 95, 76]
students = zip(names, scores)
passed_students = filter(lambda student: student[1] >= 60, students)
ranked_students = sorted(
passed_students,
key=lambda student: student[1],
reverse=True,
)
ranking = map(
lambda student: f"{student[0]}:{student[1]} 分",
ranked_students,
)
print(list(ranking))运行结果:
['王五:95 分', '张三:88 分', '赵六:76 分']这段代码按照数据流逐步处理:配对、筛选、排序、转换。每一步的结果都会交给下一步。
列表推导式还是函数工具
简单转换和筛选使用列表推导式通常更直观。map()、filter() 的优势是可以直接接收已有函数,并且能够把多个处理步骤连接起来。根据代码是否容易阅读来选择,不必强行使用某一种写法。
eval() 与安全边界
当表达式保存在字符串中时,可以使用 eval() 将它当成 Python 表达式计算并返回结果:
eval(source, globals=None, locals=None)source:要计算的字符串或已编译代码对象globals:计算时使用的全局命名空间locals:计算时使用的局部命名空间
初学阶段先看最基本的用法:
x = 2
expression = "x ** 2 + 2 * x + 1"
result = eval(expression)
print(result)运行结果:
9不要计算不可信字符串
eval() 会执行字符串中的 Python 表达式。如果内容来自用户输入、网络请求或其他不可信来源,可能执行危险代码。真实项目中不要直接把这些内容交给 eval()。
小结
本节围绕五个问题掌握内置函数:
- 如何批量转换或筛选数据?使用
map()和filter()。 - 如何按位置配对数据?使用
zip(),长度不同时以最短数据为准。 - 如何排序且不修改原数据?使用
sorted(),并通过key指定排序依据。 - 如何编写只使用一次的短函数?使用
lambda表达式。 - 如何计算字符串表达式?使用
eval(),但不能处理不可信内容。
练习
- 使用
map()把数字列表中的所有元素转换成字符串 - 使用
filter()筛选1到20之间的偶数 - 使用
zip()把姓名列表和成绩列表配对 - 使用
sorted()和lambda按照元组的第二个元素倒序排列 - 修改成绩排行榜,只保留不低于
80分的学生