云计算百科
云计算领域专业知识百科平台

Python基础:列表常用方法之查找排序与反转

一、开篇:找到它、排好它、翻过来

前面两篇文章我们详细拆解了列表的增加和删除方法。这一篇是列表方法三部曲的终章,我们要搞定三类操作:查找(在哪里?有几个?)、排序(从小到大、从大到小、按自定义规则)、反转(倒过来)。

⌨️ 这三类操作看似简单,但实际开发中的坑可不少。index() 找不到元素会报错怎么办?sort() 和 sorted() 有什么区别?如何用 key 参数实现自定义排序?列表中有字典怎么按某个字段排序?reverse() 和 reversed() 和 [::-1] 到底该用哪个?

这篇文章我会把这些问题一次性讲清楚,让你在写列表查找排序代码时心里有底、手上不慌。

二、查找——index() 和 count()

2.1 index()——查找元素的位置

fruits = ['苹果', '香蕉', '橘子', '葡萄', '香蕉', '西瓜']

# index(x):查找x第一次出现的索引
print(fruits.index('香蕉')) # 1
print(fruits.index('橘子')) # 2
print(fruits.index('西瓜')) # 5

# index(x, start):从start位置开始找
print(fruits.index('香蕉', 2)) # 4(跳过索引0-1,从索引2开始找)

# index(x, start, end):在[start, end)范围内找
print(fruits.index('香蕉', 1, 3)) # 1(在索引1-2范围内找到了)
# print(fruits.index('香蕉', 2, 4)) # ValueError(在索引2-3范围内没有)

# 元素不存在时抛出ValueError
try:
fruits.index('柠檬')
except ValueError as e:
print(f'错误:{e}') # '柠檬' is not in list

2.2 安全的查找方式

# 方式一:try/except包装
def safe_index(lst, value, default=–1):
"""安全查找,找不到返回默认值"""
try:
return lst.index(value)
except ValueError:
return default

fruits = ['苹果', '香蕉', '橘子']
print(safe_index(fruits, '香蕉')) # 1
print(safe_index(fruits, '柠檬')) # -1

# 方式二:用in先判断(但会遍历两次)
def safe_index_v2(lst, value, default=–1):
if value in lst:
return lst.index(value)
return default

# 方式三:用enumerate手动查找(支持更复杂的条件)
def find_first(lst, predicate):
"""查找第一个满足条件的元素的索引和值"""
for i, item in enumerate(lst):
if predicate(item):
return i, item
return –1, None

nums = [3, 7, 2, 9, 4, 6]
idx, val = find_first(nums, lambda x: x > 5)
print(f'第一个大于5的数:nums[{idx}] = {val}') # nums[1] = 7

# 方式四:查找所有匹配的位置
def find_all(lst, value):
"""找出所有匹配值的索引"""
return [i for i, x in enumerate(lst) if x == value]

data = [1, 3, 5, 3, 7, 3, 9]
print(find_all(data, 3)) # [1, 3, 5]

2.3 count()——统计出现次数

nums = [1, 2, 3, 2, 1, 2, 3, 2, 1, 2]

# count(x):统计x出现的次数
print(nums.count(1)) # 3
print(nums.count(2)) # 5
print(nums.count(3)) # 2
print(nums.count(999)) # 0(不存在的元素不会报错,返回0)

# count 的实用场景
# 找出现次数最多的元素(众数)
def mode(lst):
"""找出列表中出现次数最多的元素"""
if not lst:
return None
return max(set(lst), key=lst.count)

print(mode(nums)) # 2(出现了5次)

# 检查是否有重复元素
def has_duplicates(lst):
"""检查列表是否有重复元素"""
return len(lst) != len(set(lst))

print(has_duplicates([1, 2, 3, 4])) # False
print(has_duplicates([1, 2, 3, 2])) # True

# 统计所有元素的频率
from collections import Counter
counter = Counter(nums)
print(counter) # Counter({2: 5, 1: 3, 3: 2})
print(counter.most_common(2)) # [(2, 5), (1, 3)](出现最多的前2个)

三、排序——sort() vs sorted()

3.1 sort()——原地排序

# sort() 原地修改列表,不返回新列表
nums = [5, 2, 8, 1, 9, 3]

# 默认升序排序
nums.sort()
print(nums) # [1, 2, 3, 5, 8, 9]

# reverse=True 降序排序
nums.sort(reverse=True)
print(nums) # [9, 8, 5, 3, 2, 1]

# 字符串排序(按字典序)
words = ['banana', 'apple', 'cherry', 'date']
words.sort()
print(words) # ['apple', 'banana', 'cherry', 'date']

# 字符串按长度排序(使用key参数)
words.sort(key=len)
print(words) # ['date', 'apple', 'banana', 'cherry']

# 按长度降序
words.sort(key=len, reverse=True)
print(words) # ['banana', 'cherry', 'apple', 'date']

# sort() 返回 None
result = nums.sort()
print(result) # None(重要:sort()没有返回值)

⚠️ sort() 返回 None,这是一个常见的坑。不要在链式调用中使用它,也不要把它的返回值赋给变量。

3.2 sorted()——返回新列表

# sorted() 不会修改原列表,返回一个新的排序后的列表
nums = [5, 2, 8, 1, 9, 3]

# 升序
sorted_nums = sorted(nums)
print(f'原列表: {nums}') # [5, 2, 8, 1, 9, 3](不变)
print(f'排序后: {sorted_nums}') # [1, 2, 3, 5, 8, 9]

# 降序
sorted_desc = sorted(nums, reverse=True)
print(sorted_desc) # [9, 8, 5, 3, 2, 1]

# sorted() 是一个内置函数,可以用于任何可迭代对象
print(sorted('python')) # ['h', 'n', 'o', 'p', 't', 'y']
print(sorted({3, 1, 4, 1, 5})) # [1, 3, 4, 5](集合→排好序的列表)
print(sorted((3, 1, 4, 2))) # [1, 2, 3, 4](元组→排好序的列表)

# sorted配合生成器
gen = (x ** 2 for x in range(10, 0, –1))
print(sorted(gen)) # [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]

3.3 sort() vs sorted() 的选择

# 选择指南:

# 需要保留原列表 → sorted()
original = [3, 1, 4, 1, 5]
result = sorted(original)
# original 仍然是 [3, 1, 4, 1, 5]

# 不需要保留原列表,只需排序后的结果 → sort()(省内存)
data = [3, 1, 4, 1, 5]
data.sort() # data 现在是 [1, 1, 3, 4, 5]

# 需要对非列表的可迭代对象排序 → sorted()(唯一选择)
tuple_data = (3, 1, 4, 1, 5)
sorted_tuple = sorted(tuple_data) # tuple没有sort方法!

# 需要在链式操作中使用 → sorted()
# sorted可以嵌在表达式中
result = ','.join(sorted(set(names)))

四、key参数——自定义排序的灵魂

4.1 key参数的基本用法

# key参数接受一个函数,排序时用该函数的返回值来比较
# 而不是直接比较元素本身

# 按字符串长度排序
words = ['a', 'abcde', 'abc', 'ab', 'abcd']
words.sort(key=len)
print(words) # ['a', 'ab', 'abc', 'abcd', 'abcde']

# 按绝对值排序
nums = [–5, 3, –1, 4, –2]
nums.sort(key=abs)
print(nums) # [-1, -2, 3, 4, -5](按绝对值=1,2,3,4,5排序)

# 按字母排序(忽略大小写)
fruits = ['apple', 'Banana', 'cherry', 'Apple']
fruits.sort(key=str.lower)
print(fruits) # ['apple', 'Apple', 'Banana', 'cherry']

# 按字符串中最后一个字符排序
words = ['hello', 'world', 'python', 'java']
words.sort(key=lambda w: w[–1])
print(words) # ['java', 'world', 'python', 'hello']
# 按最后一个字母排: a < d < n < o

4.2 lambda 在key中的应用

# lambda是key参数的绝佳搭档

# 按绝对值排序
nums = [–5, 3, –10, 4, –2]
nums.sort(key=lambda x: abs(x))
print(nums) # [-2, 3, 4, -5, -10]

# 按元组中第二个元素排序
pairs = [(1, 3), (2, 1), (4, 2), (3, 4)]
pairs.sort(key=lambda p: p[1])
print(pairs) # [(2, 1), (4, 2), (1, 3), (3, 4)]

# 按字典中某个字段排序
users = [
{'name': '小明', 'age': 25},
{'name': '小红', 'age': 23},
{'name': '小刚', 'age': 26},
]
users.sort(key=lambda u: u['age'])
print(users)
# [{'name': '小红', 'age': 23}, {'name': '小明', 'age': 25}, {'name': '小刚', 'age': 26}]

# 按多个字段排序——返回元组实现多级排序
students = [
('小明', 85, 2),
('小红', 92, 1),
('小刚', 85, 1),
('小李', 92, 2),
]
# 先按成绩降序,再按班级升序
students.sort(key=lambda s: (–s[1], s[2]))
print(students)
# [('小红', 92, 1), ('小李', 92, 2), ('小明', 85, 2), ('小刚', 85, 1)]

# 更复杂:按条件排序
# 将偶数排前面,奇数排后面,各自内部升序
nums = [1, 2, 3, 4, 5, 6, 7, 8, 9]
nums.sort(key=lambda x: (x % 2, x))
print(nums) # [2, 4, 6, 8, 1, 3, 5, 7, 9]
# 排序键是元组 (x%2, x)
# 偶数: (0, 偶数) 奇数: (1, 奇数)
# 0<1 所以偶数排在奇数前面
# 组内按x本身排序

4.3 使用operator模块替代lambda

from operator import itemgetter, attrgetter, methodcaller

# itemgetter:等价于 lambda x: x[n]
# 但性能更好(纯C实现)

students = [
('小明', 85, 'A班'),
('小红', 92, 'B班'),
('小刚', 78, 'A班'),
]

# 按成绩排序(元组的第1个索引)
students.sort(key=itemgetter(1))
print(students) # [('小刚', 78, 'A班'), ('小明', 85, 'A班'), ('小红', 92, 'B班')]

# 多级排序:先按班级(2),再按成绩(1)
students.sort(key=itemgetter(2, 1))
print(students)
# [('小刚', 78, 'A班'), ('小明', 85, 'A班'), ('小红', 92, 'B班')]

# attrgetter:按对象属性排序
class Student:
def __init__(self, name, score):
self.name = name
self.score = score

def __repr__(self):
return f'Student({self.name}, {self.score})'

students = [
Student('小明', 85),
Student('小红', 92),
Student('小刚', 78),
]
students.sort(key=attrgetter('score'))
print(students)
# [Student(小刚, 78), Student(小明, 85), Student(小红, 92)]

# methodcaller:按方法调用结果排序
words = ['hello', 'World', 'PYTHON', 'Code']
words.sort(key=methodcaller('lower'))
print(words) # ['Code', 'hello', 'PYTHON', 'World']

💡 性能建议:对于简单的字段访问(如 x[1] 或 x.name),优先用 itemgetter 或 attrgetter 而不是 lambda——它们在C层面实现,通常更快。

4.4 稳定排序与多级排序

# Python的sort是稳定的(Timsort算法)
# 稳定排序意味着:相等元素的相对顺序在排序后保持不变

# 利用稳定性实现多级排序
data = [
('小明', 'A班', 85),
('小红', 'B班', 92),
('小刚', 'A班', 78),
('小李', 'B班', 88),
('小王', 'A班', 95),
]

# 需求:先按班级排序,再按成绩降序
# 策略:从最不重要的条件开始排序

# 先按成绩降序(次要条件)
data.sort(key=lambda x: x[2], reverse=True)
# 再按班级排序(主要条件)——稳定性保证同班级内成绩顺序不变
data.sort(key=lambda x: x[1])

for student in data:
print(student)
# ('小王', 'A班', 95)
# ('小明', 'A班', 85)
# ('小刚', 'A班', 78)
# ('小红', 'B班', 92)
# ('小李', 'B班', 88)

# 同样的结果也可以用元组key一次完成
data2 = [
('小明', 'A班', 85),
('小红', 'B班', 92),
('小刚', 'A班', 78),
('小李', 'B班', 88),
('小王', 'A班', 95),
]
data2.sort(key=lambda x: (x[1], –x[2]))
print(data2)
# 结果相同

五、反转——reverse()、reversed() 和 [::-1]

5.1 reverse()——原地反转

# reverse() 原地反转列表,不返回新列表
nums = [1, 2, 3, 4, 5]
nums.reverse()
print(nums) # [5, 4, 3, 2, 1]

# 字符串列表
fruits = ['苹果', '香蕉', '橘子']
fruits.reverse()
print(fruits) # ['橘子', '香蕉', '苹果']

# reverse() 返回 None
result = [1, 2, 3].reverse()
print(result) # None

5.2 reversed()——返回反向迭代器

# reversed() 不修改原列表,返回一个反向迭代器
nums = [1, 2, 3, 4, 5]

rev = reversed(nums)
print(rev) # <list_reverseiterator object at 0x…>
print(type(rev)) # <class 'list_reverseiterator'>

# 转为列表
print(list(rev)) # [5, 4, 3, 2, 1]
print(list(rev)) # [](迭代器已耗尽!)

# 原列表不变
print(nums) # [1, 2, 3, 4, 5]

# reversed() 常用于反向遍历
for num in reversed(nums):
print(num, end=' ') # 5 4 3 2 1
print()

# reversed() 也适用于其他序列
print(''.join(reversed('hello'))) # olleh

5.3 [::-1]——切片反转

# [::-1] 创建反转后的新列表
nums = [1, 2, 3, 4, 5]
reversed_nums = nums[::–1]
print(reversed_nums) # [5, 4, 3, 2, 1]
print(nums) # [1, 2, 3, 4, 5](原列表不变)

# 三种反转方式的对比:
nums = [1, 2, 3, 4, 5]

# 1. reverse():原地反转,修改原列表,O(1)额外内存
nums.reverse()
print(nums) # [5, 4, 3, 2, 1]

# 2. reversed():返回迭代器,不创建新列表
nums = [1, 2, 3, 4, 5]
rev_iter = reversed(nums)
for x in rev_iter:
print(x, end=' ') # 5 4 3 2 1

# 3. [::-1]:创建新列表,O(n)内存
nums = [1, 2, 3, 4, 5]
new_list = nums[::–1]

# 选择建议:
# – 需要修改原列表 → reverse()
# – 只需要反向遍历,不关心顺序 → reversed()
# – 需要保留原列表且需要新列表 → [::-1] 或 list(reversed(lst))

六、实战:查找排序的综合应用

6.1 排行榜系统

class Leaderboard:
"""排行榜——综合运用排序和查找"""

def __init__(self):
self._entries = [] # [(name, score), …]

def add_score(self, name, score):
self._entries.append((name, score))
# 每次添加后按分数降序排列
self._entries.sort(key=lambda x: x[1], reverse=True)

def get_rank(self, name):
"""获取某人的排名(从1开始)"""
for i, (n, s) in enumerate(self._entries):
if n == name:
return i + 1
return –1

def get_top_n(self, n):
"""获取前N名"""
return self._entries[:n]

def get_around_player(self, name, radius=2):
"""获取某人前后radius名的玩家"""
rank = self.get_rank(name)
if rank == –1:
return []
idx = rank – 1
start = max(0, idx – radius)
end = min(len(self._entries), idx + radius + 1)
return self._entries[start:end]

def display(self):
"""显示排行榜"""
for i, (name, score) in enumerate(self._entries, 1):
print(f'{i:2d}. {name:<6s} {score:>5d}分')

lb = Leaderboard()
lb.add_score('小明', 850)
lb.add_score('小红', 920)
lb.add_score('小刚', 780)
lb.add_score('小李', 950)
lb.add_score('小王', 880)
lb.add_score('小张', 830)

print('=== 排行榜 ===')
lb.display()

print(f'\\n小李排名: 第{lb.get_rank("小李")}名')
print(f'前3名: {lb.get_top_n(3)}')
print(f'小刚周围的玩家: {lb.get_around_player("小刚", 1)}')

6.2 优先级任务队列

class PriorityTaskQueue:
"""用sort+key实现的优先级任务队列"""

def __init__(self):
self._tasks = []

def add_task(self, name, priority, deadline):
"""添加任务
priority: 越小越紧急(1=最高优先级)
deadline: 截止时间(时间戳)
"""

self._tasks.append({
'name': name,
'priority': priority,
'deadline': deadline,
})

def get_next_task(self):
"""获取下一个要执行的任务"""
if not self._tasks:
return None
# 按优先级排序(priority小的优先),同优先级按deadline排序
self._tasks.sort(key=lambda t: (t['priority'], t['deadline']))
return self._tasks.pop(0)

def find_tasks_by_priority(self, priority):
"""查找所有指定优先级的任务"""
return [t for t in self._tasks if t['priority'] == priority]

def pending_count(self):
return len(self._tasks)

queue = PriorityTaskQueue()
queue.add_task('发送邮件', 2, 1000)
queue.add_task('数据库备份', 1, 900)
queue.add_task('生成报表', 2, 850)
queue.add_task('清理日志', 3, 1200)

print('按优先级处理任务:')
while queue.pending_count() > 0:
task = queue.get_next_task()
print(f' 处理: {task["name"]} (优先级{task["priority"]})')

6.3 价格排序与过滤系统

def sort_and_filter_products(products, sort_by='price', reverse=False,
min_price=None, max_price=None,
keyword=None, categories=None):
"""
综合排序与过滤的商品列表处理
"""

result = products[:] # 浅拷贝,不影响原数据

# 价格过滤
if min_price is not None:
result = [p for p in result if p['price'] >= min_price]
if max_price is not None:
result = [p for p in result if p['price'] <= max_price]

# 关键词搜索
if keyword:
keyword = keyword.lower()
result = [p for p in result
if keyword in p['name'].lower()
or keyword in p.get('description', '').lower()]

# 分类过滤
if categories:
result = [p for p in result if p['category'] in categories]

# 排序
sort_keys = {
'price': lambda p: p['price'],
'sales': lambda p: p.get('sales', 0),
'rating': lambda p: p.get('rating', 0),
'name': lambda p: p['name'],
}
key_func = sort_keys.get(sort_by, lambda p: p['price'])
result.sort(key=key_func, reverse=reverse)

return result

products = [
{'name': 'Python编程书', 'price': 59.9, 'sales': 1500, 'rating': 4.8, 'category': '图书'},
{'name': '机械键盘', 'price': 299, 'sales': 800, 'rating': 4.5, 'category': '外设'},
{'name': '显示器支架', 'price': 159, 'sales': 450, 'rating': 4.3, 'category': '外设'},
{'name': 'USB数据线', 'price': 19.9, 'sales': 3000, 'rating': 4.6, 'category': '配件'},
{'name': 'Python进阶', 'price': 69.9, 'sales': 1200, 'rating': 4.9, 'category': '图书'},
]

# 按价格升序
result = sort_and_filter_products(products, sort_by='price')
print('按价格升序:')
for p in result:
print(f' {p["name"]}: ¥{p["price"]}')

# 按评分降序
result = sort_and_filter_products(products, sort_by='rating', reverse=True)
print('\\n按评分降序:')
for p in result:
print(f' {p["name"]}: {p["rating"]}分')

# 价格在50-200之间的外设类商品
result = sort_and_filter_products(
products, sort_by='sales', reverse=True,
min_price=50, max_price=200, categories=['外设']
)
print('\\n外设类(¥50-200),按销量排序:')
for p in result:
print(f' {p["name"]}: ¥{p["price"]}, 销量{p["sales"]}')

七、Timsort——Python排序算法简介

7.1 Timsort的特点

# Python的sort使用Timsort(由Tim Peters于2002年设计)
# 它结合了归并排序和插入排序的优点

# 核心特点:
# 1. 稳定排序:相等元素的相对顺序不变
# 2. O(n log n) 时间复杂度(最坏情况)
# 3. O(n) 时间复杂度(最好情况——已排序数据)
# 4. 对部分有序的数据特别高效

# 演示稳定性
data = [
('小明', 'A班', 85),
('小刚', 'A班', 78),
('小红', 'B班', 92),
]

# 先按成绩排序
data.sort(key=lambda x: x[2])
print('按成绩排序后:')
for item in data:
print(f' {item}')

# 再按班级排序
data.sort(key=lambda x: x[1])
print('再按班级排序后(稳定性保证同班内成绩顺序不变):')
for item in data:
print(f' {item}')
# 同班级的学生会按照之前排序确定的顺序排列

7.2 自定义比较器(cmp_to_key)

# Python 3中移除了cmp参数,用functools.cmp_to_key替代
from functools import cmp_to_key

# 自定义比较函数
def compare_by_vowel_count(a, b):
"""按元音字母数量比较"""
vowels = set('aeiouAEIOU')
count_a = sum(1 for c in a if c in vowels)
count_b = sum(1 for c in b if c in vowels)
return count_a – count_b # 返回负数表示a<b,正数a>b,0表示相等

words = ['hello', 'world', 'python', 'programming', 'beautiful', 'sky']
words.sort(key=cmp_to_key(compare_by_vowel_count))
print(words) # ['sky', 'world', 'python', 'hello', 'beautiful', 'programming']
# sky(1个元音?) world(1), python(1), hello(2), beautiful(5), programming(3)
# 实际按元音数升序排列

# 虽然cmp_to_key能用,但通常用key+lambda更高效
# 上面的例子可以改写为:
words.sort(key=lambda w: sum(1 for c in w if c in 'aeiouAEIOU'))

八、性能与最佳实践

# 1. 排序了大量数据时,sort()比sorted()省内存
import time

n = 1000000
data = list(range(n, 0, –1)) # 降序列表

start = time.perf_counter()
data.sort()
t1 = time.perf_counter() – start
print(f'sort()原地排序: {t1:.3f}秒')

data = list(range(n, 0, –1))
start = time.perf_counter()
sorted_data = sorted(data)
t2 = time.perf_counter() – start
print(f'sorted()创建新列表: {t2:.3f}秒')

# 2. 频繁查找时,考虑转为set
# 对于"是否存在"的查询,set是O(1),list的in是O(n)
data_list = list(range(10000))
data_set = set(data_list)

start = time.perf_counter()
for _ in range(10000):
_ = 9999 in data_list
t_list = time.perf_counter() – start
print(f'list in查询: {t_list:.4f}秒')

start = time.perf_counter()
for _ in range(10000):
_ = 9999 in data_set
t_set = time.perf_counter() – start
print(f'set in查询: {t_set:.4f}秒')
print(f'set快 {t_list / t_set:.0f} 倍')

# 3. key函数会被调用N次,应尽量简单
# 不推荐:在key中做复杂计算
# 推荐:预先计算好排序键
data = ['hello', 'world', 'python', 'programming']
# 如果排序键计算很昂贵
word_lengths = {w: len(w) for w in data}
data.sort(key=word_lengths.get) # 字典查找是O(1)

# 4. 部分排序用heapq
import heapq
scores = [85, 92, 78, 95, 88, 76, 91, 89, 94, 83]
# 只需要前3名
top3 = heapq.nlargest(3, scores)
print(f'前3名: {top3}') # [95, 94, 92]

bottom3 = heapq.nsmallest(3, scores)
print(f'后3名: {bottom3}') # [76, 78, 83]

九、本篇小结

✅ 列表查找、排序、反转核心方法速查:

查找:

  • index(x):查找值x第一次出现的位置,找不到抛ValueError
  • count(x):统计值x出现的次数,不存在返回0(不报错)
  • 安全查找:用 try/except 或先 in 判断

排序:

  • lst.sort():原地排序,返回None,省内存
  • sorted(lst):返回新排序列表,原列表不变,可用于任何可迭代对象
  • key参数:自定义排序逻辑,配合lambda或operator模块
  • Timsort:稳定、O(n log n)、对部分有序数据特别高效

反转:

  • lst.reverse():原地反转,O(1)额外内存
  • reversed(lst):返回反向迭代器,懒计算
  • lst[::-1]:创建反转的新列表

📝 列表方法三部曲到此结束。增加(4种)、删除(6种)、查找排序反转——这三大类方法覆盖了Python列表的绝大部分日常操作。下一篇我们将进入列表的另一个重要领域:列表推导式的高级应用与性能优化。

赞(0)
未经允许不得转载:网硕互联帮助中心 » Python基础:列表常用方法之查找排序与反转
分享到: 更多 (0)

评论 抢沙发

评论前必须登录!