欢迎光临
我们一直在努力

Python 基础语法(五):列表与元组

上一章讲完循环以后,我们已经可以重复处理一组数据了。不过,前面的例子大多使用字符串或者 range(),真正写程序时,还需要把多个数据放在一起保存。例如,一组商品名称、一批考试成绩,或者一个坐标中的多个数值。

Python 中最常用的两种序列类型是列表和元组。列表可以在创建后继续增加、删除和修改元素,元组创建以后不能直接修改。这一章会把它们的创建、索引、切片、常用操作、复制以及打包和解包讲清楚。

在这里插入图片描述

一、创建列表

列表使用方括号 [] 创建,元素之间使用逗号分隔:

languages = ["Python", "Java", "Go"]
scores = [95, 88, 76]

列表中的元素按照写入顺序保存,同一个列表也可以放入不同类型的数据:

user = ["Ivan", 20, True]

这种写法语法上没有问题。不过,如果列表中的元素完全没有统一含义,后面的代码会比较难处理。大多数情况下,一个列表会保存同一类数据。

空列表可以写成:

items = []

也可以使用 list() 创建:

items = list()

list() 还可以把其他可迭代对象转换成列表:

letters = list("Python")
numbers = list(range(1, 6))

print(letters)
print(numbers)

输出结果为:

['P', 'y', 't', 'h', 'o', 'n']
[1, 2, 3, 4, 5]

len() 可以取得列表中的元素数量:

languages = ["Python", "Java", "Go"]
print(len(languages))

结果为 3。

二、通过索引访问元素

列表和字符串一样,索引从 0 开始:

languages = ["Python", "Java", "Go"]

print(languages[0])
print(languages[1])
print(languages[2])

输出结果为:

Python
Java
Go

负数索引从列表末尾开始计算,-1 表示最后一个元素:

print(languages[1])
print(languages[2])

输出结果为:

Go
Java

访问不存在的位置会出现 IndexError:

print(languages[3])

如果列表可能为空,不能直接访问 items[0]。可以先判断列表中是否有内容:

items = []

if items:
print(items[0])
else:
print("列表为空")

三、列表切片

切片可以从列表中取出一段数据,写法与字符串相同:

列表[开始位置:结束位置:步长]

切片包含开始位置,不包含结束位置:

letters = ["P", "y", "t", "h", "o", "n"]

print(letters[1:4])

输出结果为:

['y', 't', 'h']

在这里插入图片描述

开始位置和结束位置都可以省略:

numbers = [0, 1, 2, 3, 4, 5]

print(numbers[:3])
print(numbers[3:])
print(numbers[:])

输出结果为:

[0, 1, 2]
[3, 4, 5]
[0, 1, 2, 3, 4, 5]

第三个参数表示步长:

print(numbers[::2])
print(numbers[::1])

输出结果为:

[0, 2, 4]
[5, 4, 3, 2, 1, 0]

切片返回的是一个新列表。修改切片得到的结果,不会直接改变原列表:

numbers = [10, 20, 30, 40]
part = numbers[1:3]

part[0] = 200

print(numbers)
print(part)

输出结果为:

[10, 20, 30, 40]
[200, 30]

这里先讨论一层列表。列表中还包含其他列表时,复制关系会复杂一些,后面会单独说明。

四、修改列表中的元素

字符串和元组创建以后不能直接修改,列表可以。给某个索引重新赋值,就能替换这个位置的元素:

languages = ["Python", "Java", "Go"]
languages[1] = "Rust"

print(languages)

输出结果为:

['Python', 'Rust', 'Go']

也可以使用切片一次替换多个元素:

numbers = [1, 2, 3, 4, 5]
numbers[1:4] = [20, 30]

print(numbers)

输出结果为:

[1, 20, 30, 5]

右侧元素的数量不必和被替换的数量相同,所以切片赋值也可能改变列表长度。

在这里插入图片描述

五、向列表中添加元素

1. append() 在末尾添加一个元素

languages = ["Python", "Java"]
languages.append("Go")

print(languages)

输出结果为:

['Python', 'Java', 'Go']

append() 每次只添加一个元素。如果传入的是一个列表,整个列表会作为一个元素放进去:

numbers = [1, 2]
numbers.append([3, 4])

print(numbers)

输出结果为:

[1, 2, [3, 4]]

2. extend() 一次添加多个元素

extend() 会依次取出可迭代对象中的元素,再添加到原列表末尾:

numbers = [1, 2]
numbers.extend([3, 4])

print(numbers)

输出结果为:

[1, 2, 3, 4]

append([3, 4]) 会增加一个列表元素,extend([3, 4]) 会增加两个整数元素,这两种结果不要混在一起。

在这里插入图片描述

extend() 也可以接收字符串,不过它会把字符串逐个拆成字符:

letters = ["A"]
letters.extend("BC")

print(letters)

结果为 ['A', 'B', 'C']。

3. insert() 在指定位置插入

languages = ["Python", "Go"]
languages.insert(1, "Java")

print(languages)

输出结果为:

['Python', 'Java', 'Go']

insert(1, "Java") 表示把 Java 放到索引 1,原来位于这个位置及其后面的元素会向后移动。

六、删除列表中的元素

1. 使用 del 按索引删除

languages = ["Python", "Java", "Go"]
del languages[1]

print(languages)

del 也可以删除一段切片:

numbers = [0, 1, 2, 3, 4, 5]
del numbers[1:4]

print(numbers)

输出 [0, 4, 5]。

2. remove() 按值删除

languages = ["Python", "Java", "Go"]
languages.remove("Java")

print(languages)

列表中存在多个相同元素时,remove() 只删除第一个:

numbers = [10, 20, 10, 30]
numbers.remove(10)

print(numbers)

输出 [20, 10, 30]。

删除不存在的值会出现 ValueError。如果这是一种正常情况,可以先使用 in 判断:

if "Java" in languages:
languages.remove("Java")

3. pop() 删除并返回元素

pop() 默认取出最后一个元素:

languages = ["Python", "Java", "Go"]
last_language = languages.pop()

print(last_language)
print(languages)

输出结果为:

Go
['Python', 'Java']

也可以传入索引:

language = languages.pop(0)

remove() 根据值删除,并且不返回被删除的元素;pop() 根据索引删除,并返回被删除的元素。

在这里插入图片描述

4. clear() 清空列表

languages = ["Python", "Java", "Go"]
languages.clear()

print(languages)

结果为 []。

七、查找、统计与成员判断

in 和 not in 可以判断列表中是否包含某个元素:

languages = ["Python", "Java", "Go"]

print("Python" in languages)
print("Rust" not in languages)

index() 返回指定元素第一次出现的位置:

print(languages.index("Java"))

结果为 1。元素不存在时,index() 会出现 ValueError。

count() 用来统计元素出现的次数:

numbers = [10, 20, 10, 30, 10]
print(numbers.count(10))

结果为 3。

数值列表还可以使用 min()、max() 和 sum():

scores = [86, 92, 75, 98]

print(min(scores))
print(max(scores))
print(sum(scores))
print(sum(scores) / len(scores))

最后一行计算平均值。列表为空时不能直接相除,需要先判断 len(scores) 是否大于 0。

八、列表排序与反转

1. sort() 修改原列表

numbers = [30, 10, 40, 20]
numbers.sort()

print(numbers)

输出结果为:

[10, 20, 30, 40]

降序排列可以设置 reverse=True:

numbers.sort(reverse=True)

sort() 会直接修改原列表,并且返回 None。不要写成:

# 错误用法
result = numbers.sort()

这时 result 得到的是 None,不是排序后的列表。

2. sorted() 返回新列表

numbers = [30, 10, 40, 20]
result = sorted(numbers)

print(numbers)
print(result)

输出结果为:

[30, 10, 40, 20]
[10, 20, 30, 40]

原列表需要保留时使用 sorted(),允许直接改变原列表时可以使用 sort()。

在这里插入图片描述

3. 使用 key 指定排序规则

words = ["banana", "Kiwi", "apple"]
words.sort(key=str.lower)

print(words)

这里排序时会先按每个字符串的小写结果比较,所以不会直接受到原始大小写顺序影响。

reverse() 只负责把现有顺序反过来,并不进行大小排序:

numbers = [10, 30, 20]
numbers.reverse()

print(numbers)

结果为 [20, 30, 10],不是降序排列。

九、遍历列表

前面已经学过 for 循环,可以直接依次取出列表中的元素:

languages = ["Python", "Java", "Go"]

for language in languages:
print(language)

需要同时取得索引时,使用 enumerate():

for index, language in enumerate(languages, start=1):
print(index, language)

输出结果为:

1 Python
2 Java
3 Go

遍历列表时直接删除其中的元素,可能跳过数据:

numbers = [1, 2, 3, 4]

for number in numbers:
if number % 2 == 0:
numbers.remove(number)

需要筛选数据时,更稳妥的方式是生成一个新列表:

numbers = [1, 2, 3, 4]
result = []

for number in numbers:
if number % 2 != 0:
result.append(number)

print(result)

下一章后面还会单独讲列表推导式,到时候可以把这段筛选写得更简洁。

十、列表赋值与复制

下面的代码并没有创建两个列表:

a = [10, 20, 30]
b = a

b.append(40)

print(a)
print(b)

输出结果为:

[10, 20, 30, 40]
[10, 20, 30, 40]

b = a 只是让两个变量指向同一个列表对象,所以通过 b 修改列表,a 看到的内容也会变化。

需要复制一层列表时,可以使用 copy():

a = [10, 20, 30]
b = a.copy()

b.append(40)

print(a)
print(b)

输出结果为:

[10, 20, 30]
[10, 20, 30, 40]

在这里插入图片描述

完整切片也能创建一个新的一层列表:

b = a[:]

不过,copy() 和切片都只复制列表的外层。如果列表里面还放着其他可变对象,内层对象仍然可能被两个列表共同引用:

a = [[1, 2], [3, 4]]
b = a.copy()

b[0].append(100)

print(a)
print(b)

两个结果中的第一个内层列表都会出现 100。这一章先记住这个现象,可变对象、不可变对象、浅拷贝和深拷贝会放到后面的专门章节中详细讲。

十一、嵌套列表

列表中的元素还可以是列表,这种结构称为嵌套列表:

matrix = [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9],
]

访问元素时需要使用两次索引:

print(matrix[0])
print(matrix[0][1])

输出结果为:

[1, 2, 3]
2

第一个索引选择外层列表中的一行,第二个索引再选择这一行中的元素。

可以使用嵌套循环遍历:

for row in matrix:
for number in row:
print(number, end=" ")

print()

创建重复的嵌套列表时,不要直接写:

# 容易出问题的写法
rows = [[0, 0, 0]] * 3

这三行会引用同一个内层列表。修改一行时,其他行也会一起变化:

rows[0][0] = 1
print(rows)

应该让每一行分别创建:

rows = []

for _ in range(3):
rows.append([0, 0, 0])

推导式章节会再介绍更简洁的创建方式。

十二、创建元组

元组使用圆括号 () 创建:

point = (10, 20)
languages = ("Python", "Java", "Go")

空元组可以写成:

empty_tuple = ()

只有一个元素的元组必须保留逗号:

value1 = (10)
value2 = (10,)

print(type(value1))
print(type(value2))

输出结果为:

<class 'int'>
<class 'tuple'>

决定它是不是元组的是逗号,不是圆括号。圆括号只是让结构更容易看清。

在这里插入图片描述

也可以使用 tuple() 转换其他可迭代对象:

numbers = tuple([10, 20, 30])
letters = tuple("ABC")

print(numbers)
print(letters)

十三、访问和切片元组

元组同样是有顺序的,所以索引和切片写法与列表相同:

languages = ("Python", "Java", "Go")

print(languages[0])
print(languages[1])
print(languages[0:2])

输出结果为:

Python
Go
('Python', 'Java')

元组切片得到的仍然是元组。

len()、in、not in、index() 和 count() 也可以用于元组:

numbers = (10, 20, 10, 30)

print(len(numbers))
print(20 in numbers)
print(numbers.index(20))
print(numbers.count(10))

十四、元组不能直接修改

元组创建以后,不能替换、增加或者删除其中的元素:

point = (10, 20)

# TypeError
point[0] = 100

元组没有 append()、remove() 和 pop() 这些会改变内容的方法。

如果确实需要改变,可以转换成列表,修改后再转回元组:

point = (10, 20)

values = list(point)
values[0] = 100
point = tuple(values)

print(point)

不过,如果数据经常需要修改,一开始就使用列表会更合适。

元组本身不能修改,不代表它里面的所有对象都不能修改。例如,元组中包含列表时,仍然可以修改这个列表的内容:

data = ("scores", [80, 90])
data[1].append(100)

print(data)

输出结果为:

('scores', [80, 90, 100])

不能改变的是元组中保存的引用位置,内层列表仍然是可变对象。

十五、元组的打包与解包

把多个值放进一个元组称为打包:

user = "Ivan", 20, "Python"

print(user)

输出结果为:

('Ivan', 20, 'Python')

把元组中的值分别交给多个变量称为解包:

name, age, language = user

print(name)
print(age)
print(language)

在这里插入图片描述

普通解包时,变量数量必须和元素数量一致,否则会出现 ValueError。

不需要其中某个值时,通常使用下划线接收:

name, _, language = user

星号变量可以接收剩余的多个元素,结果会是列表:

numbers = (10, 20, 30, 40, 50)

first, *middle, last = numbers

print(first)
print(middle)
print(last)

输出结果为:

10
[20, 30, 40]
50

交换变量时使用的写法,本质上也会用到打包和解包:

a = 10
b = 20

a, b = b, a

十六、列表与元组之间转换

list() 可以把元组转换成列表,tuple() 可以把列表转换成元组:

languages_tuple = ("Python", "Java", "Go")
languages_list = list(languages_tuple)

scores_list = [90, 85, 96]
scores_tuple = tuple(scores_list)

print(languages_list)
print(scores_tuple)

转换会创建新的外层对象,不会把原对象直接变成另一种类型。

十七、列表和元组怎么选

列表和元组都是有顺序的序列,也都支持索引、切片和循环遍历。区别主要在于数据创建以后是否需要改变。

对比项列表 list元组 tuple
写法 [1, 2, 3] (1, 2, 3)
是否有顺序
能否修改元素 可以 不可以
能否增删元素 可以 不可以
常见用途 需要持续变化的一组数据 创建后保持固定的一组数据

商品列表、任务列表和用户输入记录通常会不断变化,适合使用列表。坐标、RGB 颜色值和函数一次返回的多个固定结果,可以使用元组。

不需要为了“性能更高”把所有列表都改成元组。首先应该根据数据是否需要修改来选择,代码含义清楚更重要。

十八、综合示例:整理学生成绩

下面使用列表保存多名学生,每名学生的信息使用元组表示。程序会过滤无效成绩,再按照分数从高到低排序:

records = [
("张三", 86),
("李四", 95),
("王五", 72),
("赵六", 105),
]

valid_records = []

for name, score in records:
if score < 0 or score > 100:
print(f"忽略无效成绩:{name} {score}")
continue

valid_records.append((name, score))

valid_records.sort(key=lambda item: item[1], reverse=True)

print("成绩排名:")

for rank, (name, score) in enumerate(valid_records, start=1):
print(f"{rank}. {name}{score}")

输出结果为:

忽略无效成绩:赵六 105
成绩排名:
1. 李四:95
2. 张三:86
3. 王五:72

外层使用列表,是因为学生记录可能继续增加或删除。每条记录使用元组,是因为姓名和成绩在这次排序过程中作为一条完整记录处理。

排序中的 lambda item: item[1] 表示取出每条记录索引为 1 的成绩作为排序依据。匿名函数会在后面的函数章节中详细讲,这里先看懂它负责“按成绩排序”即可。

十九、常见错误

1. 索引超出范围

items = [10, 20, 30]
print(items[3])

有效索引只有 0、1 和 2。

2. 混淆 append() 和 extend()

items = [1, 2]
items.append([3, 4])

结果是 [1, 2, [3, 4]]。如果需要 [1, 2, 3, 4],应该使用 extend()。

3. 接收 sort() 的返回值

numbers = [3, 1, 2]
numbers = numbers.sort()

执行以后 numbers 会变成 None。sort() 直接修改原列表,不返回排序后的列表。

4. 误以为赋值会复制列表

b = a

这只会增加一个指向同一列表的变量。需要复制一层列表时,可以使用 a.copy()。

5. 忘记单元素元组的逗号

value = (10)

这仍然是整数。单元素元组应该写成 (10,)。

6. 解包数量不一致

point = (10, 20, 30)
x, y = point

三个元素不能直接交给两个普通变量。可以增加变量,或者使用星号变量接收剩余内容。

7. 使用乘法创建嵌套列表

rows = [[0, 0]] * 3

三个位置引用的是同一个内层列表,修改其中一行会影响其他行。

二十、练习

练习一:管理待办事项

创建一个待办列表,依次完成添加任务、修改任务、删除任务和输出全部任务。

练习二:统计成绩

给定一个成绩列表,输出最高分、最低分、总分和平均分,并统计 60 分以下的成绩有多少个。

练习三:列表去重

给定列表 [1, 2, 2, 3, 1, 4],在不使用集合的情况下,生成一个不包含重复元素的新列表,并保留原来的出现顺序。

练习四:排序商品

使用元组保存商品名称和价格,再把多条商品记录放进列表中,按照价格从低到高输出。

练习五:矩阵转置

给定一个三行三列的嵌套列表,使用嵌套循环把行和列交换,生成一个新的列表。

这一章把列表和元组的常用操作讲完了。下一章继续学习字典与集合,字典通过键保存和查找数据,集合更适合处理不重复元素以及交集、并集和差集。

赞(0)
未经允许不得转载:171主机测评 » Python 基础语法(五):列表与元组
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址