
你是不是经常遇到这种麻烦事?比如:
lst[1], lst[3],重复又无聊;name、age、city这 3 个键的值,写循环的时候总担心漏写键名;今天咱们就聊个能解决这些问题的 “小工具”——Python 自带operator模块里的itemgetter。它不仅能让数据提取更高效,还能大幅提高代码复用性,不用再写一堆重复代码。接下来咱们从基础到实战,把它讲透!
在讲用法之前,得先明确一个关键点:itemgetter 不是直接帮你拿数据的,而是帮你 “造一个专门拿数据的工具”。
itemgetter是 Python 标准库operator里的一个函数,不用额外安装,直接导入就能用:
# 第一步:导入itemgetter(必须先做这步)
from operator import itemgetter你可以把itemgetter理解成一个 “工具工厂”—— 给它传一些参数(比如索引、键名),它就会返回一个 “获取器”(一个可调用的对象)。这个 “获取器” 专门用来从列表、字典这些可迭代对象里拿指定的数据。
举个最简单的例子,你想从列表里拿第 2 个元素(索引是 1):
from operator import itemgetter
# 1. 用itemgetter造一个“拿索引1元素”的工具
get_index_1 = itemgetter(1) # 这里不是拿数据,是造工具!
# 2. 用这个工具去提取具体列表的数据
lst1 = [10, 20, 30, 40]
lst2 = [100, 200, 300, 400]
print(get_index_1(lst1)) # 输出:20(用工具提lst1的索引1)
print(get_index_1(lst2)) # 输出:200(用同一个工具提lst2的索引1)看到没?造一次工具,能反复用在不同列表上 —— 这就是 “复用性” 的第一步!
itemgetter最常用的场景是处理列表 / 元组和字典,咱们分别讲,每个场景都给能运行的代码。
列表和元组都是按 “索引” 取值的,itemgetter传索引就能搞定,支持单个索引和多个索引。
比如每次都要拿列表的第 3 个元素(索引 2):
from operator import itemgetter
# 造工具:拿索引2的元素
get_idx2 = itemgetter(2)
# 批量处理多个列表
list_a = [1, 3, 5, 7]
list_b = [2, 4, 6, 8]
list_c = [10, 20, 30, 40]
# 用同一个工具提取
print(get_idx2(list_a)) # 输出:5
print(get_idx2(list_b)) # 输出:6
print(get_idx2(list_c)) # 输出:30如果要同时拿多个索引(比如索引 1 和 3),普通方法得写两行代码,用itemgetter一行搞定,还能复用:
from operator import itemgetter
# 造工具:同时拿索引1和3的元素(顺序和传参一致)
get_idx1_idx3 = itemgetter(1, 3)
# 用工具提取
lst1 = [10, 20, 30, 40]
lst2 = [100, 200, 300, 400]
print(get_idx1_idx3(lst1)) # 输出:(20, 40)(返回元组,顺序是1→3)
print(get_idx1_idx3(lst2)) # 输出:(200, 400)这里要注意:提取多个元素时,返回的是元组,不管原数据是列表还是元组。
字典是按 “键名” 取值的,itemgetter传键名就行,同样支持单个和多个键名 —— 这是工作中最常用的场景之一(比如处理批量用户数据)。
比如每次都要从用户字典里拿name的值:
from operator import itemgetter
# 造工具:拿"name"键的值
get_name = itemgetter("name")
# 批量处理多个用户字典
user1 = {"name": "张三", "age": 25, "city": "北京"}
user2 = {"name": "李四", "age": 30, "city": "上海"}
user3 = {"name": "王五", "age": 28, "city": "广州"}
print(get_name(user1)) # 输出:张三
print(get_name(user2)) # 输出:李四
print(get_name(user3)) # 输出:王五如果要拿name、age、city三个键,普通方法要写三行,itemgetter一次搞定:
from operator import itemgetter
# 造工具:同时拿"name"、"age"、"city"三个键
get_user_info = itemgetter("name", "age", "city")
# 批量提取
user1 = {"name": "张三", "age": 25, "city": "北京"}
user2 = {"name": "李四", "age": 30, "city": "上海"}
# 输出的元组顺序和传键名的顺序一致
print(get_user_info(user1)) # 输出:('张三', 25, '北京')
print(get_user_info(user2)) # 输出:('李四', 30, '上海')只要一个对象支持__getitem__方法(简单说就是能通过obj[key]或obj[index]取值的),itemgetter都能处理。比如:
举个字符串的例子:
from operator import itemgetter
# 造工具:拿字符串的索引1和3的字符
get_char = itemgetter(1, 3)
str1 = "Python"
str2 = "Java"
print(get_char(str1)) # 输出:('y', 'h')(str1[1]是y,str1[3]是h)
print(get_char(str2)) # 输出:('a', 'a')(str2[1]是a,str2[3]是a)前面咱们提了 “复用性”,这里专门讲实战场景 —— 怎么用itemgetter减少重复代码,提高效率。
假设你有 100 个订单字典,每个都要提取order_id(订单号)、total_amount(总金额)、status(状态)这三个字段,还要筛选出 “已支付” 的订单。
# 假设有100个订单的列表
orders = [
{"order_id": "1001", "total_amount": 299, "status": "已支付", "buyer": "张三"},
{"order_id": "1002", "total_amount": 599, "status": "未支付", "buyer": "李四"},
{"order_id": "1003", "total_amount": 199, "status": "已支付", "buyer": "王五"},
# ... 还有97个订单
]
# 提取目标字段:每个订单都要写三行,重复!
processed_orders = []
for order in orders:
# 重复代码:每次都要写order["xxx"]
order_id = order["order_id"]
total = order["total_amount"]
status = order["status"]
processed_orders.append( (order_id, total, status) )
# 筛选已支付的:又要循环一次
paid_orders = [order for order in processed_orders if order[2] == "已支付"]
print(paid_orders)from operator import itemgetter
orders = [
{"order_id": "1001", "total_amount": 299, "status": "已支付", "buyer": "张三"},
{"order_id": "1002", "total_amount": 599, "status": "未支付", "buyer": "李四"},
{"order_id": "1003", "total_amount": 199, "status": "已支付", "buyer": "王五"},
# ... 还有97个订单
]
# 1. 只造一次工具:提取order_id、total_amount、status
get_order_info = itemgetter("order_id", "total_amount", "status")
# 2. 批量提取:用map配合获取器,一行搞定(不用循环)
processed_orders = list(map(get_order_info, orders))
# 3. 筛选已支付:直接用列表推导式
paid_orders = [order for order in processed_orders if order[2] == "已支付"]
print(paid_orders) # 输出:[('1001', 299, '已支付'), ('1003', 199, '已支付')]这里的map(get_order_info, orders)就是把 “获取器” 应用到每个订单上,比手动循环简洁多了 —— 而且如果后面还要处理其他订单列表,直接用get_order_info这个工具就行,不用再写提取逻辑!
itemgetter另一个高频场景是配合sorted函数排序 —— 比如按列表里的某个元素排序,或按字典的某个键排序,比用 lambda 表达式更高效、更简洁。
比如有一个学生列表,每个元素是(姓名, 成绩),要按 “成绩” 从高到低排序:
from operator import itemgetter
students = [
("张三", 85),
("李四", 92),
("王五", 78),
("赵六", 95)
]
# 按“成绩”排序(成绩在索引1的位置)
# key=itemgetter(1):用索引1的值作为排序依据
sorted_by_score = sorted(students, key=itemgetter(1), reverse=True)
print(sorted_by_score) # 输出:[('赵六', 95), ('李四', 92), ('张三', 85), ('王五', 78)]比如有一个商品列表,每个商品是字典,要按 “价格” 从低到高排序:
from operator import itemgetter
products = [
{"name": "手机", "price": 5999, "stock": 100},
{"name": "耳机", "price": 799, "stock": 200},
{"name": "平板", "price": 3299, "stock": 50}
]
# 按“price”键排序(key=itemgetter("price"))
sorted_by_price = sorted(products, key=itemgetter("price"))
print(sorted_by_price)
# 输出:
# [
# {'name': '耳机', 'price': 799, 'stock': 200},
# {'name': '平板', 'price': 3299, 'stock': 50},
# {'name': '手机', 'price': 5999, 'stock': 100}
# ]可能有人会问:用 lambda 也能实现排序啊,比如key=lambda x: x[1],为啥要用itemgetter?
咱们用表格对比一下:
维度 | itemgetter 写法 | lambda 写法 | 优势分析 |
|---|---|---|---|
单个索引 / 键 |
|
| 代码差不多,itemgetter 更短 |
多个索引 / 键 |
|
| itemgetter 更简洁,不易写错 |
效率 | 底层 C 实现,速度快 | Python 层面计算,速度慢 | 数据量大时(比如 10 万条),itemgetter 快 20%-50% |
复用性 | 可创建获取器反复用 | 每次都要写新的 lambda | 批量处理时,itemgetter 复用性强 |
举个效率测试的例子(处理 10 万条数据):
from operator import itemgetter
import time
# 生成10万条测试数据:(随机数1, 随机数2)
data = [ (i, i*2) for i in range(100000) ]
# 用itemgetter排序
start1 = time.time()
sorted(data, key=itemgetter(1))
end1 = time.time()
print(f"itemgetter耗时:{end1 - start1:.4f}秒")
# 用lambda排序
start2 = time.time()
sorted(data, key=lambda x: x[1])
end2 = time.time()
print(f"lambda耗时:{end2 - start2:.4f}秒")运行结果大概是:
itemgetter耗时:0.0123秒
lambda耗时:0.0185秒数据量越大,itemgetter的效率优势越明显!
用itemgetter时,新手很容易踩坑,这里总结 5 个最常见的问题,每个都给错误示例和解决方法。
from operator import itemgetter
# 错误:以为itemgetter(1)能直接拿数据,其实它只是个工具
result = itemgetter(1)([10,20,30]) # 虽然能运行,但如果要复用就麻烦
# 更错的写法:result = itemgetter(1, [10,20,30]) # 会报TypeErroritemgetter的第一个参数是 “索引 / 键名”,不是数据!正确的逻辑是:先造工具(传索引 / 键名),再用工具传数据。
from operator import itemgetter
# 1. 先造工具(传索引/键名)
get_idx1 = itemgetter(1)
# 2. 再用工具传数据
result = get_idx1([10,20,30])
print(result) # 输出:20from operator import itemgetter
get_user = itemgetter("name", "gender") # 要拿"gender"键
user = {"name": "张三", "age": 25} # 但user里没有"gender"
get_user(user) # 报错:KeyError: 'gender'itemgetter会直接按键名取值,和dict[key]一样,键不存在就报错 —— 它不像dict.get(key)那样能返回默认值。
如果键可能不存在,先用dict.get()处理,或者加判断:
from operator import itemgetter
# 方法1:先给字典补全默认值
user = {"name": "张三", "age": 25}
# 给没有的键加默认值(比如gender默认是"未知")
user.setdefault("gender", "未知")
get_user = itemgetter("name", "gender")
print(get_user(user)) # 输出:('张三', '未知')
# 方法2:用lambda配合get(适合不想改原字典的情况)
get_user_safe = lambda x: (x.get("name", ""), x.get("gender", "未知"))
print(get_user_safe(user)) # 输出:('张三', '未知')from operator import itemgetter
# 嵌套字典:user里的"info"键对应另一个字典
user = {
"name": "张三",
"info": {"age": 25, "city": "北京"}
}
# 错误:想直接拿"info"里的"age",但itemgetter不支持嵌套
get_age = itemgetter("info", "age")
get_age(user) # 输出:({'age': 25, 'city': '北京'}, 'age')——不是想要的结果itemgetter只支持 “一层” 取值,不能直接处理user["info"]["age"]这种嵌套结构。
用 lambda 表达式处理嵌套,或者自己写一个小函数:
# 方法1:用lambda
get_age = lambda x: x["info"]["age"]
print(get_age(user)) # 输出:25
# 方法2:自己写函数(适合复杂逻辑)
def get_user_age(user):
return user.get("info", {}).get("age", 0) # 加了默认值,更安全
print(get_user_age(user)) # 输出:25from operator import itemgetter
# 想先拿"age",再拿"name"
get_info = itemgetter("name", "age") # 传参顺序是name→age
user = {"name": "张三", "age": 25}
result = get_info(user)
print(result) # 输出:('张三', 25)——不是想要的(25, 张三)itemgetter返回的元组顺序,和你传参数的顺序完全一致 —— 你传("name", "age"),就先返回 name,再返回 age。
调整传参顺序即可:
get_info = itemgetter("age", "name") # 先传age,再传name
result = get_info(user)
print(result) # 输出:(25, '张三')from operator import itemgetter
# 尝试用itemgetter提取整数的“元素”(整数不支持索引/键)
get_idx0 = itemgetter(0)
get_idx0(123) # 报错:TypeError: 'int' object is not subscriptable只有支持__getitem__方法的对象(比如列表、元组、字典、字符串)才能用itemgetter—— 像整数、浮点数、布尔值这些不支持索引 / 键取值的对象,用了就会报错。
先确认对象是否支持obj[key]或obj[index]取值,再用itemgetter。比如整数可以先转成字符串:
get_idx0 = itemgetter(0)
num_str = str(123) # 转成字符串
print(get_idx0(num_str)) # 输出:'1'面试时,面试官可能会问itemgetter相关的问题,这里总结 6 个最常见的,帮你用大白话回答清楚。
主要有 3 个区别:
itemgetter(1,3)比lambda x: (x[1],x[3])短,还不容易写错。get_info = itemgetter("name","age"),后面处理 10 个字典都能用;lambda 每次都要重新写,重复代码多。所以如果是批量处理数据、需要排序,或者提取多个元素,用 itemgetter 更好;如果是简单的单次取值,或者要处理嵌套结构,用 lambda 更灵活。
itemgetter 返回的是一个 “获取器对象”(其实是个可调用的函数),不是直接返回数据。
比如get_idx1 = itemgetter(1),这里的get_idx1是个工具,你得把具体的数据传给它(比如get_idx1([10,20,30])),它才会返回数据(20)。
简单说:itemgetter 是 “造工具” 的,不是 “直接拿数据” 的。
只要一个对象支持__getitem__方法(就是能通过obj[key]或obj[index]取值的),itemgetter 都能处理。
常见的有:
lst[1]);dict["name"]);str[2]);__getitem__的自定义对象(比如自己写的类,实现了__getitem__方法)。像整数、浮点数这种不支持索引 / 键取值的,就不能用。
会直接报KeyError,和用dict[key]取值一样。
解决方法有两种:
dict.setdefault(key, 默认值),把可能不存在的键加上默认值;dict.get(),比如lambda x: x.get(key, 默认值),这样键不存在时会返回默认值,不会报错。很简单,把 itemgetter 作为sorted函数的key参数就行。
比如有个商品列表,每个商品是字典,要按 “price” 排序:
from operator import itemgetter
products = [{"name": "手机", "price": 5999}, {"name": "耳机", "price": 799}]
# 按price从低到高排序,key=itemgetter("price")
sorted_products = sorted(products, key=itemgetter("price"))如果要按多个键排序(比如先按 price,再按库存),就传多个键名:key=itemgetter("price", "stock")。
不直接支持。itemgetter 只能处理一层取值,比如dict["info"]可以,但dict["info"]["age"]这种嵌套的就不行。
如果要处理嵌套,得用 lambda 表达式,比如lambda x: x["info"]["age"],或者自己写个小函数,专门处理嵌套逻辑。
用了这么多例子,咱们最后总结一下itemgetter的核心优势:
当然它也有局限性,比如不支持嵌套结构、键不存在会报错,但这些都有对应的解决方法。
如果你平时经常要提取列表 / 字典的元素、批量处理数据,或者需要排序,一定要试试itemgetter—— 能帮你少写很多代码,还能提高效率!
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。