python

关注公众号 jb51net

关闭
首页 > 脚本专栏 > python > Python列表查找排序与反转

Python基础指南之列表查找、排序与反转的常用方法

作者:星河耀银海

本文详细解析Python列表的查找、排序与反转操作,涵盖index()、count()安全查找方法,sort()与sorted()的区别及使用key参数自定义排序,通过实战案例展示综合应用,助你掌握列表三大核心操作,提升代码效率与安全性

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

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

这三类操作看似简单,但实际开发中的坑可不少。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),优先用 itemgetterattrgetter 而不是 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]

九、本篇小结

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

查找:

排序:

反转:

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

到此这篇关于Python基础指南之列表查找、排序与反转的常用方法的文章就介绍到这了,更多相关Python列表查找排序与反转内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文