Python中的列表操作

149 阅读8分钟

在Python中,列表(list)是一种非常强大且灵活的数据结构,它允许你存储和操作一系列的项目。以下是一些常见的列表操作处理,包括创建、访问、修改、添加、删除、遍历、排序和列表推导等。
1,list的相关操作示例如下:

# 空列表
empty_list = []
empty_list1 = list()
print(empty_list)  # 输出 []
print(empty_list1)  # 输出 []

# 初始化列表
mixed = [1, "hello", 3.14, True]
print(mixed)  # 输出 [1, 'hello', 3.14, True]
# 正向索引
print(mixed[2])  # 输出 3.14
# 反向索引
print(mixed[-2])  # 输出 3.14

# 末尾添加元素
mixed.append("apple")
print(mixed)  # 输出 [1, 'hello', 3.14, True, 'apple']

# 插入到指定位置
# 在Python中,list.insert(index, element) 方法用于在列表的指定位置插入一个元素。这里的 index 参数指定了新元素应该被插入的位置。
mixed.insert(3, "fruits")
print(mixed)  # 输出 [1, 'hello', 3.14, 'fruits', True, 'apple']

# 使用 mixed.insert(-1, "color") 时,-1 作为索引表示列表的倒数第一个元素的位置之前,不是在列表的最后
mixed.insert(-1, "color")
print(mixed)  # 输出 [1, 'hello', 3.14, 'fruits', True, 'color', 'apple']

# 合并列表,列表末尾追加多个元素
mixed.extend(['Jim', 'Jacky', 'Lucy'])
# 下面的代码将输出 [1, 'hello', 3.14, 'fruits', True, 'color', 'apple', 'Jim', 'Jacky', 'Lucy']
print(mixed)

# 按值删除,若值不存在会报错,捕捉异常
try:
    mixed.remove("banana")
except ValueError:
    print("valueError, not in list")

# 按索引删除
popped = mixed.pop(1)  # 删除索引1的元素并返回
print(popped)  # 输出 hello
print(f"before call del:{mixed}")
# 上面的代码输出 before call del:[1, 3.14, 'fruits', True, 'color', 'apple', 'Jim', 'Jacky', 'Lucy']
del mixed[0]  # 删除索引0的元素
# 下面的代码输出 after call del:[3.14, 'fruits', True, 'color', 'apple', 'Jim', 'Jacky', 'Lucy']
print(f"after call del:{mixed}")

# 修改元素
mixed[-1] = "Lily"
print(mixed)  # 输出 [3.14, 'fruits', True, 'color', 'apple', 'Jim', 'Jacky', 'Lily']

# 查找元素
if "lucy" in mixed:
    print("lucy is in list")
else:
    print("lucy is not in list")

try:
    pos0 = mixed.index(3.14)
    print(pos0)  # 输出 0
    pos1 = mixed.index('lucy')
    print(pos1)  # 出现异常,下面的代码不再执行
    pos2 = mixed.index('Jacky')
    print(pos2)
except ValueError:
    print("call index occurs ValueError")
# 上面的代码输出 call index occurs ValueError
# 由于在获取lucy的索引时出现异常,所以不执行查找Jacky的索引

# 清空列表
mixed.clear()  # 列表变为空列表 []

# 列表推导式
squares = [x ** 2 for x in range(10)]
print(squares)  # 输出 [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

# 排序与反转
nums = [3, 1, 4, 1, 5, 9, 2]

# 原地排序(修改原列表)
nums.sort()
print(nums)  # 输出 [1, 1, 2, 3, 4, 5, 9]
nums.sort(reverse=True)  # 降序排序
print(nums)  # 输出 [9, 5, 4, 3, 2, 1, 1]

# 生成新排序列表,原列表不变
sorted_nums = sorted(nums)
print(nums)  # 输出 [9, 5, 4, 3, 2, 1, 1]
print(sorted_nums)  # 输出 [1, 1, 2, 3, 4, 5, 9]

# 反转列表
print(f"before reverse:{nums}")  # 输出 before reverse:[9, 5, 4, 3, 2, 1, 1]
# 对列表本身进行逆序
nums.reverse()
print(f"call reverse,list:{nums}")  # 输出 call reverse,list:[1, 1, 2, 3, 4, 5, 9]
reverse1 = nums[::-1]
print(f"call [::-1]:{reverse1}")  # 输出 call [::-1]:[9, 5, 4, 3, 2, 1, 1]
# nums[::-1]并未对nums本身进行逆序
print(f"now nums:{nums}")  # 输出 now nums:[1, 1, 2, 3, 4, 5, 9]

# 切片操作
nums = [0, 1, 2, 3, 4, 5]
print(nums[:4])  # 输出 [0, 1, 2, 3]
print(nums[1:4])  # 输出 [1, 2, 3](左闭右开)
print(nums[::2])  # 输出 [0, 2, 4](步长为2print(nums[::-1])  # 输出 [5, 4, 3, 2, 1, 0](反转列表)

# 生成偶数列表
evens = [x for x in range(10) if x % 2 == 0]
print(f"even num:{evens}")  # 输出 even num:[0, 2, 4, 6, 8]

# 嵌套循环
pairs = [(x, y) for x in [1, 2] for y in [3, 4]]  # [(1,3), (1,4), (2,3), (2,4)]
print(f"nest loop:{pairs}")  # 输出 nest loop:[(1, 3), (1, 4), (2, 3), (2, 4)]

# 长度与统计
length = len(nums)  # 列表长度
print(length)  # 输出 6
count = nums.count(1)  # 元素1出现的次数
print(count)  # 输出 1

# 列表合并
combined = [1, 2] + [3, 4]
print(combined)  # 输出 [1, 2, 3, 4]

# 生成数字列表
numbers = list(range(5))
print(numbers)  # 输出 [0, 1, 2, 3, 4]


# 在Python中,列表(list)是一种可变的数据结构,可以包含多个元素,这些元素可以是数字、字符串、甚至是其他列表。
# 当我们想要复制一个列表时,有两种主要的拷贝方式:浅拷贝(shallow copy)和深拷贝(deep copy)。
import copy

# 浅拷贝:创建一个新列表,但新列表中的元素是对原列表中元素的引用。如果原列表包含可变对象,那么这些对象在新列表和原列表中共享。
# 下面的代码中,ba 的一个浅拷贝。ba 是两个不同的列表对象,但是 b 中的子列表 [1, 2][3, 4] 仍然是 a 中对应子列表的引用。
# 因此,如果你修改了 b 中某个子列表的内容,这个修改也会反映在 a 中对应的子列表上。
a = [[1, 2], [3, 4]]
b = a.copy()  # 浅拷贝:子列表仍是引用
print(f"copy:{b}")
b[0][1] = 99
print(f"modify b check a:{a}")  # 输出 modify b check a:[[1, 99], [3, 4]]
a[1][1] = 199
print(f"modify a check b:{b}")  # 输出 modify a check b:[[1, 99], [3, 199]]

# 深拷贝:递归地复制原列表中的所有元素,包括任何嵌套的可变对象。新列表和原列表在内存中是完全独立的。
# 下面代码中,ba 的一个深拷贝。ba 是完全不同的列表对象,并且 b 中的子列表也是 a 中对应子列表的完全独立的副本。
# 因此,修改 b 中的任何元素都不会影响 ab = copy.deepcopy(a)
print(f"deep copy:{b}")

b[0][1] = 109
print(f"deep copy, modify b check b:{b}")  # 输出 deep copy, modify b check b:[[1, 109], [3, 4]]
print(f"deep copy, modify b check a:{a}")  # 输出 deep copy, modify b check a:[[1, 99], [3, 4]]

a[0][0] = 999
print(f"deep copy, modify a check a:{a}")  # 输出 deep copy, modify a check a:[[999, 99], [3, 199]]
print(f"deep modify a check b:{b}")  # deep modify a check b:[[1, 109], [3, 199]]

# 空列表判断:if not my_list: 比 len(my_list) == 0 更简洁。
my_list = []
# 空列表在布尔上下文中被视为 False
if not my_list:
    print("empty")
else:
    print("not empty")
# 上面的代码将输出 empty

2,双端队列deque
deque(双端队列)在头部和尾部插入/删除操作的时间复杂度均为 O(1),而普通列表的头部插入/删除操作时间复杂度为 O(n)。
适用于需要高频操作队列头部或尾部的场景(如队列、滑动窗口、BFS算法等)。

from collections import deque

# 初始化 deque
d = deque()  # 空队列
print(d)  # 输出 deque([])
d = deque([1, 2, 3])
print(d)  # 输出 deque([1, 2, 3])

# 添加元素,尾部添加
d.append(4)
print(d)  # 输出 deque([1, 2, 3, 4])
# 头部添加
d.appendleft(0)
print(d)  # 输出 deque([0, 1, 2, 3, 4])

# 删除元素,尾部删除
d.pop()
print(d)  # 输出 deque([0, 1, 2, 3])
# 头部删除
d.popleft()
print(d)  # 输出 deque([1, 2, 3])

# 移动操作
# 向右循环移动1步
d.rotate(1)  # 向右循环移动1步 → deque([3, 1, 2])
print(d)  # 输出 deque([3, 1, 2])
# 向左循环移动1步
d.rotate(-1)
print(d)  # 输出 deque([1, 2, 3])
# 向右循环移动2步
d.rotate(2)
print(d)  # deque([2, 3, 1])

# 性能对比
import time
from collections import deque


def test_list(n):
    lst = []
    for i in range(n):
        lst.insert(0, i)  # 头部插入(时间复杂度 O(n))


def test_deque(n):
    dq = deque()
    for i in range(n):
        dq.appendleft(i)  # 头部插入(时间复杂度 O(1))


# 测试 10,000 次操作
start = time.time()
test_list(10_000)
print("List time:", time.time() - start)  # 约 0.009秒

start = time.time()
test_deque(10_000)
print("Deque time:", time.time() - start)  # 约 0.001秒

# 上面的输出如下
# List time: 0.009096384048461914
# Deque time: 0.0010247230529785156


# 限制队列最大长度
d = deque(maxlen=3)  # 最多保存3个元素
d.append(1)  # deque([1])
d.append(2)  # deque([1, 2])
d.append(3)  # deque([1, 2, 3])
print(d)  # 输出 deque([1, 2, 3], maxlen=3)
# 自动移除头部
d.append(4)
print(d)  # deque([2, 3, 4], maxlen=3)

# 从collections模块导入deque类,deque是一个双端队列,支持快速的从两端添加或删除元素
from collections import deque
# 导入threading模块,该模块提供了线程相关的类和函数,用于多线程编程。
import threading

dq = deque()


def worker():
    # 在worker函数内部,使用一个for循环遍历从0999的整数。
    for i in range(1000):
        # 将循环变量i添加到deque对象dq的末尾。由于deque是线程安全的,多个线程可以同时调用append方法而不会导致数据损坏。
        dq.append(i)


# 创建一个空列表threads,用于存储将要启动的线程对象
threads = []
# 使用一个for循环来创建和启动4个线程。
for _ in range(4):
    # 创建一个线程对象t,其目标函数是之前定义的worker函数。
    t = threading.Thread(target=worker)
    # 将新创建的线程对象t添加到threads列表中
    threads.append(t)
    # 启动线程t,使其开始执行worker函数
    t.start()

for t in threads:
    # 对每个线程调用join方法,这会阻塞当前线程(即主线程),直到被调用的线程执行完毕。这确保了主线程在所有工作线程完成之前不会继续执行。
    t.join()

# 在所有线程都执行完毕后,打印deque对象dq的长度。由于每个线程都向dq中添加了1000个元素,总共有4个线程,因此dq的长度应该是4000print(len(dq))  # 正确输出 4000(无竞争问题)

综上,在随机访问(如 lst[500])或中间位置操作时优先使用List 在高频头尾插入/删除操作(性能关键场景)时优先使用 deque.