operator模块:itemgetter/attrgetter/methodcaller高级用法
请介绍Python operator模块中itemgetter、attrgetter和methodcaller的用法和原理。与lambda相比,它们在性能和可读性上有什么优势?给出在排序、分组和数据库查询中的实际应用场景:如何用itemgetter替代lambda x: x[1]实现多级排序?如何用attrgetter替代lambda x: x.name?如何用methodcaller替代lambda x: x.strip()?
回答
屠龙少年
itemgetter:按索引/键获取元素,替代索引lambda。
from operator import itemgetter, attrgetter, methodcaller
data = [('Alice', 25, 'NY'), ('Bob', 30, 'LA'), ('Charlie', 25, 'SF')]
# 多级排序:先年龄后名字
sorted(data, key=itemgetter(1, 0))
# 等价于 lambda x: (x[1], x[0])
# 获取多列:一次返回元组
get_name_city = itemgetter(0, 2)
for row in data:
name, city = get_name_city(row)
print(name, city)
attrgetter:按属性名获取,替代属性访问lambda。
from dataclasses import dataclass
@dataclass
class Person:
name: str
age: int
city: str
people = [Person('Alice', 25, 'NY'), Person('Bob', 30, 'LA')]
# 按年龄排序
sorted(people, key=attrgetter('age'))
# 多级排序
sorted(people, key=attrgetter('city', 'age'))
# 等价于 lambda p: (p.city, p.age)
# 分组
from itertools import groupby
for city, group in groupby(sorted(people, key=attrgetter('city')), attrgetter('city')):
print(city, list(group))
methodcaller:调用对象方法,替代方法调用lambda。
strings = [' hello ', 'WORLD', ' Python ']
# 调用strip
processed = list(map(methodcaller('strip'), strings))
# 等价于 [s.strip() for s in strings]
# 带参数调用
methodcaller('replace', 'old', 'new') # 调用 s.replace('old', 'new')
性能对比:
| 方式 | 可读性 | 速度 |
|------|--------|------|
| lambda x: x[1] | 一般 | 较慢(Python级调用) |
| itemgetter(1) | 好 | 快(C级实现) |
| lambda x: x.name | 一般 | 较慢 |
| attrgetter('name') | 好 | 快 |
最佳实践:内置函数>operator模块>lambda,优先使用C级实现的operator函数提升性能。