Skip to content

Python map() 函数:用示例转换可迭代对象

Updated on

转换列表中的每个元素是 Python 编程中最常见的操作之一。你有一个需要转换为整数的字符串列表,一列需要四舍五入的价格,一组需要转换为小写的文件名。本能反应是写一个 for 循环,创建空列表,逐个追加结果,然后返回列表。这样做可以工作,但冗长、容易出错,而且将实际的转换逻辑埋在了样板代码中。

Python 的内置 map() 函数消除了这些样板代码。它接受一个函数和一个或多个可迭代对象,将函数应用到每个元素上,并返回结果的迭代器。一行代替五行。代码读起来就像你想要什么的描述,而不是如何做。

📚

本指南涵盖 Python map 函数的每个实用方面:基本语法、map 与 lambda 和内置函数的组合、处理多个可迭代对象、与列表推导式的性能比较,以及你将在生产代码中使用的实际数据处理模式。

map() 的基本语法

map() 函数的签名很简单:

map(function, iterable, *iterables)
  • function -- 一个接受一个参数的可调用对象(如果传递多个可迭代对象则接受更多)。
  • iterable -- 其元素将传递给 function 的序列。
  • *iterables -- 可选的额外可迭代对象。提供时,function 必须接受相应数量的参数。

map() 返回一个 map 对象,它是一个惰性迭代器。它不会立即计算所有结果,而是在你消费时逐个生成值。

这是最简单的示例:

numbers = [1, 2, 3, 4, 5]
squared = map(lambda x: x ** 2, numbers)
 
print(squared)
# <map object at 0x...>
 
print(list(squared))
# [1, 4, 9, 16, 25]

map 对象不会直接显示结果。你需要将它转换为列表、元组或其他集合,或者在循环中迭代它。

map() 与内置函数

map() 最简洁的用法之一是与 Python 的内置函数配合使用。不需要 lambda -- 直接传递函数名即可。

使用 int、float、str 进行类型转换

# 将字符串转换为整数
string_numbers = ["10", "20", "30", "40"]
integers = list(map(int, string_numbers))
print(integers)
# [10, 20, 30, 40]
 
# 将整数转换为浮点数
values = [1, 2, 3, 4]
floats = list(map(float, values))
print(floats)
# [1.0, 2.0, 3.0, 4.0]
 
# 将数字转换为字符串
ids = [101, 102, 103]
string_ids = list(map(str, ids))
print(string_ids)
# ['101', '102', '103']

使用 len 获取长度

words = ["Python", "map", "function", "tutorial"]
lengths = list(map(len, words))
print(lengths)
# [6, 3, 8, 8]

使用 str.strip 去除空白

raw_inputs = ["  hello  ", "  world ", " python  "]
cleaned = list(map(str.strip, raw_inputs))
print(cleaned)
# ['hello', 'world', 'python']

其他有用的内置函数组合

# abs() 获取绝对值
numbers = [-5, 3, -1, 7, -2]
absolutes = list(map(abs, numbers))
print(absolutes)
# [5, 3, 1, 7, 2]
 
# round() 进行四舍五入(单参数形式)
prices = [19.995, 4.123, 99.876]
rounded = list(map(round, prices))
print(rounded)
# [20, 4, 100]
 
# bool() 检测真值
values = [0, 1, "", "hello", None, [], [1]]
truthy = list(map(bool, values))
print(truthy)
# [False, True, False, True, False, False, True]

当内置函数已经能做你需要的事情时,直接将它传递给 map()。没有理由用 lambda 包装它。

map() 与 Lambda 函数

Lambda 函数释放了 map() 的全部威力。它们让你无需创建命名函数就能定义内联转换。

# 对每个数字求平方
numbers = [1, 2, 3, 4, 5]
squared = list(map(lambda x: x ** 2, numbers))
print(squared)
# [1, 4, 9, 16, 25]
 
# 摄氏度转华氏度
celsius = [0, 20, 37, 100]
fahrenheit = list(map(lambda c: round(c * 9/5 + 32, 1), celsius))
print(fahrenheit)
# [32.0, 68.0, 98.6, 212.0]
 
# 从电子邮件地址中提取域名
emails = ["alice@gmail.com", "bob@company.org", "carol@university.edu"]
domains = list(map(lambda e: e.split("@")[1], emails))
print(domains)
# ['gmail.com', 'company.org', 'university.edu']
 
# 格式化名字
names = ["alice smith", "bob jones", "carol white"]
formatted = list(map(lambda n: n.title(), names))
print(formatted)
# ['Alice Smith', 'Bob Jones', 'Carol White']

模式是一致的:map(lambda x: transform(x), iterable)。保持 lambda 主体为单一、清晰的表达式。如果转换需要多个步骤,请改用命名函数。

map() 与多个可迭代对象

当你传递多个可迭代对象时,map() 会从每个可迭代对象中取一个元素并行调用函数。函数必须接受与可迭代对象数量相同的参数。当最短的可迭代对象耗尽时,迭代停止。

# 将两个列表的对应元素相加
list_a = [1, 2, 3, 4]
list_b = [10, 20, 30, 40]
sums = list(map(lambda a, b: a + b, list_a, list_b))
print(sums)
# [11, 22, 33, 44]
 
# 计算加权分数
scores = [85, 92, 78, 95]
weights = [0.3, 0.3, 0.2, 0.2]
weighted = list(map(lambda s, w: round(s * w, 1), scores, weights))
print(weighted)
# [25.5, 27.6, 15.6, 19.0]
 
# 组合名字和姓氏
first_names = ["Ada", "Grace", "Alan"]
last_names = ["Lovelace", "Hopper", "Turing"]
full_names = list(map(lambda f, l: f"{f} {l}", first_names, last_names))
print(full_names)
# ['Ada Lovelace', 'Grace Hopper', 'Alan Turing']

三个或更多可迭代对象

# 从三个维度列表计算体积
lengths = [2, 3, 4]
widths = [5, 6, 7]
heights = [8, 9, 10]
 
volumes = list(map(lambda l, w, h: l * w * h, lengths, widths, heights))
print(volumes)
# [80, 162, 280]

处理不等长度

map() 在最短的可迭代对象处停止。没有错误,没有填充 -- 它只是停止:

a = [1, 2, 3, 4, 5]
b = [10, 20, 30]
result = list(map(lambda x, y: x + y, a, b))
print(result)
# [11, 22, 33]
# 'a' 中的元素 4 和 5 被静默丢弃

如果你需要显式处理不等长度,请使用 itertools.zip_longest

from itertools import zip_longest
 
a = [1, 2, 3, 4, 5]
b = [10, 20, 30]
result = list(map(lambda pair: pair[0] + (pair[1] or 0), zip_longest(a, b, fillvalue=0)))
print(result)
# [11, 22, 33, 4, 5]

map() vs 列表推导式

这是最重要的比较。map() 和列表推导式都能转换序列,但各有不同的优势。

特性map()列表推导式
语法map(func, iterable)[func(x) for x in iterable]
可读性(简单转换)使用内置函数时高所有情况下都高
可读性(复杂转换)较低(嵌套 lambda)较高(可多行)
过滤支持无(需要单独的 filter()有(内置 if 子句)
返回类型惰性迭代器(map 对象)即时列表
大数据内存占用低(惰性求值)高(完整列表在内存中)
速度(内置函数)更快(无 Python 级别调用)稍慢
速度(lambda 函数)相当相当或稍快
嵌套转换链接不便嵌套自然
调试较难(惰性,无中间列表)较容易(即时结果)

性能基准测试

import timeit
 
data = list(range(100_000))
 
# 使用内置函数的 map
time_map_builtin = timeit.timeit(
    'list(map(str, data))',
    globals={'data': data},
    number=100
)
 
# 使用内置函数的列表推导式
time_comp_builtin = timeit.timeit(
    '[str(x) for x in data]',
    globals={'data': data},
    number=100
)
 
# 使用 lambda 的 map
time_map_lambda = timeit.timeit(
    'list(map(lambda x: x * 2, data))',
    globals={'data': data},
    number=100
)
 
# 列表推导式等价版
time_comp_expr = timeit.timeit(
    '[x * 2 for x in data]',
    globals={'data': data},
    number=100
)
 
print(f"map + 内置函数:  {time_map_builtin:.4f}s")
print(f"推导式:          {time_comp_builtin:.4f}s")
print(f"map + lambda:    {time_map_lambda:.4f}s")
print(f"推导式:          {time_comp_expr:.4f}s")
# 典型结果:
# map + 内置函数:  ~0.85s(更快)
# 推导式:          ~1.05s
# map + lambda:    ~0.95s
# 推导式:          ~0.80s(更快)

关键洞察:map() 与内置函数(如 intstrfloat)搭配更快,因为它避免了每次迭代时创建 Python 级别的函数调用。但 map() 与 lambda 搭配时大约与列表推导式速度相同,或稍慢一些,因为两者都涉及每个元素一次 Python 级别的函数调用。

map() vs 生成器表达式

如果你不需要将完整结果列表保存在内存中,map() 和生成器表达式都提供惰性求值:

# map 对象 - 惰性
mapped = map(lambda x: x ** 2, range(1_000_000))
 
# 生成器表达式 - 惰性
generated = (x ** 2 for x in range(1_000_000))
 
# 两者都只在迭代时消耗内存
for value in mapped:
    if value > 100:
        break

区别是风格上的。生成器表达式对大多数 Python 开发者来说读起来更自然,并支持使用 if 子句进行过滤。当你已有命名函数要应用时使用 map();否则使用生成器表达式。

# 有命名函数时 map 很简洁
import math
roots = map(math.sqrt, range(10))
 
# 对于表达式,生成器很简洁
roots = (x ** 0.5 for x in range(10))

将 map 对象转换为 list、tuple 和 set

map 对象是一个迭代器。要具体化结果,需要显式转换:

numbers = [1, 2, 3, 2, 4, 3, 5]
 
# 转为列表(保持顺序,允许重复)
as_list = list(map(lambda x: x * 10, numbers))
print(as_list)
# [10, 20, 30, 20, 40, 30, 50]
 
# 转为元组(不可变序列)
as_tuple = tuple(map(lambda x: x * 10, numbers))
print(as_tuple)
# (10, 20, 30, 20, 40, 30, 50)
 
# 转为集合(去重,无序)
as_set = set(map(lambda x: x * 10, numbers))
print(as_set)
# {10, 20, 30, 40, 50}

重要: map 对象只能消费一次。转换为列表后,再次迭代不会产生任何结果:

mapped = map(str, [1, 2, 3])
first = list(mapped)
second = list(mapped)
print(first)   # ['1', '2', '3']
print(second)  # [] -- 已经耗尽

map() 与自定义函数

对于 lambda 难以处理的复杂转换,定义常规函数并传递给 map()

def parse_temperature(reading):
    """将温度读数字符串转换为标准化字典。"""
    value, unit = float(reading[:-1]), reading[-1]
    if unit == 'F':
        celsius = round((value - 32) * 5 / 9, 1)
    elif unit == 'C':
        celsius = value
    elif unit == 'K':
        celsius = round(value - 273.15, 1)
    else:
        raise ValueError(f"未知单位: {unit}")
    return {"original": reading, "celsius": celsius}
 
readings = ["98.6F", "37.0C", "310.0K", "72.0F"]
parsed = list(map(parse_temperature, readings))
 
for entry in parsed:
    print(f"{entry['original']} -> {entry['celsius']}°C")
# 98.6F -> 37.0°C
# 37.0C -> 37.0°C
# 310.0K -> 36.9°C
# 72.0F -> 22.2°C

这种模式保持 map() 调用的简洁,同时将复杂逻辑移到可测试、有文档的函数中。

def normalize_record(record):
    """标准化用户记录以供数据库插入。"""
    return {
        "name": record["name"].strip().title(),
        "email": record["email"].strip().lower(),
        "age": int(record["age"]),
        "active": record.get("active", True),
    }
 
raw_records = [
    {"name": "  alice smith ", "email": "ALICE@Email.COM", "age": "30"},
    {"name": "bob jones", "email": "Bob@Work.ORG  ", "age": "25"},
]
 
clean_records = list(map(normalize_record, raw_records))
for r in clean_records:
    print(r)
# {'name': 'Alice Smith', 'email': 'alice@email.com', 'age': 30, 'active': True}
# {'name': 'Bob Jones', 'email': 'bob@work.org', 'age': 25, 'active': True}

链接 map() 调用

由于 map() 返回迭代器,你可以链接多个 map() 调用来构建转换管道。每个步骤一次处理一个元素,无需创建中间列表:

raw_data = ["  42  ", " 17 ", "  89  ", " 3  "]
 
# 链接:去除空白 -> 转换为 int -> 值翻倍
result = map(str.strip, raw_data)
result = map(int, result)
result = map(lambda x: x * 2, result)
 
print(list(result))
# [84, 34, 178, 6]

为了可读性,你可以嵌套调用(从内到外读):

raw_data = ["  42  ", " 17 ", "  89  ", " 3  "]
 
result = list(map(lambda x: x * 2, map(int, map(str.strip, raw_data))))
print(result)
# [84, 34, 178, 6]

嵌套版本紧凑但更难阅读。顺序版本对多步管道更清晰。两者都不创建中间列表 -- 三个 map 对象都是惰性求值的。

数据处理管道中的 map()

实际数据处理通常涉及读取、清理、转换和聚合数据。map() 自然适合这种模式。

处理日志条目

log_lines = [
    "2026-02-10 INFO User logged in",
    "2026-02-10 ERROR Database timeout",
    "2026-02-10 WARNING Disk space low",
    "2026-02-10 INFO File uploaded",
]
 
def parse_log(line):
    parts = line.split(" ", 2)
    return {"date": parts[0], "level": parts[1], "message": parts[2]}
 
parsed = list(map(parse_log, log_lines))
errors = list(filter(lambda entry: entry["level"] == "ERROR", parsed))
print(errors)
# [{'date': '2026-02-10', 'level': 'ERROR', 'message': 'Database timeout'}]

批量文件重命名逻辑

import os
 
filenames = ["Report Q1.PDF", "data export.CSV", "NOTES 2026.TXT"]
 
def sanitize_filename(name):
    base, ext = os.path.splitext(name)
    return base.strip().lower().replace(" ", "_") + ext.lower()
 
cleaned = list(map(sanitize_filename, filenames))
print(cleaned)
# ['report_q1.pdf', 'data_export.csv', 'notes_2026.txt']

数值数据管道

import math
 
raw_measurements = ["3.14159", "2.71828", "1.41421", "1.73205"]
 
# 管道:解析 -> 平方 -> 取对数 -> 四舍五入到3位小数
pipeline = map(lambda x: round(x, 3),
           map(math.log,
           map(lambda x: x ** 2,
           map(float, raw_measurements))))
 
print(list(pipeline))
# [2.292, 2.003, 0.693, 1.099]

何时使用 map() vs 列表推导式:决策指南

使用此快速参考来决定哪个工具适合你的情况:

使用 map() 当:

  • 应用单个内置函数:map(int, strings)map(str.strip, lines)
  • 已有一个执行转换的命名函数
  • 需要惰性求值且不需要完整列表在内存中
  • 传递多个可迭代对象进行逐元素操作

使用列表推导式当:

  • 转换是简单表达式:[x * 2 for x in numbers]
  • 需要在同一步骤中过滤:[x for x in items if x > 0]
  • 逻辑涉及多个条件或嵌套操作
  • 需要结果立即作为列表
  • 可读性比微优化更重要

使用生成器表达式当:

  • 需要惰性求值但想要推导式语法的可读性
  • 结果只消费一次(传递给 sum()max()join() 等)
# map -- 类型转换最简洁
integers = list(map(int, ["1", "2", "3"]))
 
# 列表推导式 -- 转换 + 过滤最简洁
even_squares = [x ** 2 for x in range(20) if x % 2 == 0]
 
# 生成器表达式 -- 单次消费最简洁
total = sum(x ** 2 for x in range(1000))

在 RunCell 中实验 map()

当你使用 map() 构建数据转换管道时,交互式测试每个阶段至关重要。你需要查看中间结果、检查边缘情况,并验证链接的 map 产生预期输出。

RunCell (opens in a new tab) 是一个直接在 Jupyter notebook 中运行的 AI 代理。它理解你的 notebook 上下文 -- 内存中的变量、DataFrame 和导入 -- 帮助你逐步构建和调试 map() 管道:

  • 可视化中间结果。 让 RunCell 展示链接 map() 的每个阶段产生什么,而不会破坏你的管道。
  • 建议向量化替代方案。 如果你在 pandas Series 上使用 map(),RunCell 可以推荐快 10-100 倍的原生 pandas 操作。
  • 在 map 和推导式之间转换。 描述转换,RunCell 生成两个版本供你选择更易读的那个。
  • 调试边缘情况。 将意外输入(None 值、空字符串、混合类型)送入你的 map 管道,准确查看它在哪里出错。

FAQ

Python 的 map() 函数做什么?

map() 函数将给定函数应用于一个或多个可迭代对象(如列表、元组或字符串)中的每个项目,并返回结果的迭代器。它是一个内置函数,可以在不编写显式循环的情况下实现函数式数据转换。语法是 map(function, iterable),返回的 map 对象是惰性的 -- 它按需计算结果而不是一次性计算所有结果。

Python 中 map() 比 for 循环快吗?

是的,map() 通常比使用 append() 构建列表的等效 for 循环更快。速度优势来自于 map() 在解释器层面用 C 实现,避免了 Python 级别的循环迭代和方法调用的开销。当将 map()intstr 等内置函数一起使用时,性能提升最显著 -- 通常快 20-40%。使用 lambda 时差异缩小,因为 lambda 本身引入了 Python 级别的函数调用。

如何将 map 对象转换为列表?

list() 包装 map() 调用:result = list(map(int, strings))。你也可以转换为其他集合:元组用 tuple(map(...)),集合用 set(map(...))。记住 map 对象是一次性迭代器。一旦被消费(通过转换为列表或迭代),它就会耗尽,无法重用。

map() 可以处理多个列表吗?

可以。在函数后传递多个可迭代对象:map(func, list1, list2, list3)。函数必须接受与可迭代对象数量相同的参数。例如,list(map(lambda a, b: a + b, [1, 2], [10, 20])) 返回 [11, 22]。迭代在最短的可迭代对象处停止,因此不等长的列表不会引发错误 -- 多余的元素会被静默忽略。

应该使用 map() 还是列表推导式?

当将单个现有函数(内置或命名的)应用于可迭代对象时使用 map(),特别是对于像 map(int, strings) 这样的类型转换。当转换涉及表达式、需要使用 if 子句过滤或可读性是优先考虑时,使用列表推导式。在现代 Python 中,列表推导式是大多数开发者的默认选择,但 map() 与内置函数搭配对于简单的单函数转换仍然更快且更简洁。

总结

Python 的 map() 函数是一个用于特定任务的精确工具:在不编写循环的情况下将单个函数应用于可迭代对象的每个元素。它与 intstrfloatlen 等内置函数搭配时表现最佳,产生最易读、最快速的代码。与 lambda 函数搭配,它能干净地处理内联转换。与多个可迭代对象搭配,它处理原本需要 zip() 和循环的逐元素操作。

有效使用 map() 的规则:

  • 直接传递内置函数:map(int, strings) 而不是 map(lambda x: int(x), strings)
  • lambda 仅用于简单的单表达式转换。
  • 当转换需要错误处理、多个步骤或文档时,切换到命名函数。
  • 当需要过滤或表达式内联更清晰时,优先使用列表推导式。
  • 记住 map() 返回惰性的一次性迭代器 -- 需要重用或检查结果时转换为 list()
  • 链接 map() 调用构建多步管道,无需创建中间列表。

map() 不是列表推导式的替代品,列表推导式也不是 map() 的替代品。它们有重叠但服务于不同的优势。最好的 Python 代码在自然适合的地方使用各自:map() 用于应用现有函数,列表推导式用于表达式和过滤,生成器表达式用于惰性单次消费。

📚