Python 学习笔记 4:字符串、列表、元组、集合与字典


一、背景

上一篇整理了 Python 中的数字类型和常用运算符。

这一篇继续整理 Python 中最常用的几类容器对象:

  1. 字符串
  2. 列表
  3. 元组
  4. 集合
  5. 字典

对应的练习代码主要包括:

19序列简介.py
20序列内建函数.py
21字符串简介.py
22字符串的转移字符.py
23字符串运算.py
24字符串格式化.py
25字符串内建函数.py
26列表简介.py
27列表的方法与函数.py
28列表推导式.py
29元组.py
30集合.py
31字典.py

这些类型是 Python 中非常基础的内容,后面写循环、函数、文件处理、JSON 处理、爬虫解析时都会频繁用到。

二、先理解序列

在 Python 中,字符串、列表、元组都属于序列。

序列的特点是:元素有顺序,可以通过索引访问,也可以通过切片取出一段内容。

例如:

s = "123,456"

print(s[0])      # 1
print(s[0:3])    # 123
print(s[-1])     # 6
print(len(s))    # 7

索引有两种写法:

写法 说明
正向索引 0 开始
反向索引 -1 开始,表示最后一个元素

需要注意:索引访问超出范围会报错,切片超出范围通常不会报错。

s = "hello"

print(s[10:20])  # ''
# print(s[10])   # IndexError: string index out of range

三、序列常用操作

序列常见操作主要包括成员判断、连接、重复、切片和遍历。

1. 成员判断

s = "123,456"

print("123" in s)      # True
print("789" not in s)  # True

2. 连接和重复

s1 = "123,456"
s2 = ",789"

print(s1 + s2)  # 123,456,789
print(s2 * 3)   # ,789,789,789

列表也可以使用 + 连接:

nums1 = [1, 2, 3]
nums2 = [4, 5]

print(nums1 + nums2)  # [1, 2, 3, 4, 5]

3. 切片

切片语法如下:

seq[start:end:step]

含义是从 start 开始,到 end 结束,但是不包含 end 位置的元素。

s = "123,456"

print(s[0:2])   # 12
print(s[0:])    # 123,456
print(s[:-1])   # 123,45
print(s[:])     # 123,456
print(s[::2])   # 13,5

4. 遍历

s = "hello"

for item in s:
    print(item)

列表、元组也可以使用相同方式遍历。

四、序列常用内置函数

练习代码中整理了一些常见序列函数,实际使用中下面这些最常见。

函数 作用 示例
len() 获取长度 len([1, 2, 3])
enumerate() 同时获取索引和值 for i, v in enumerate(data)
max() 获取最大值 max([1, 2, 3])
min() 获取最小值 min([1, 2, 3])
sorted() 排序后返回新列表 sorted([3, 1, 2])
reversed() 反向迭代 list(reversed([1, 2, 3]))
sum() 求和 sum([1, 2, 3])
zip() 多个序列按位置组合 zip(names, ages)

例如按照年龄排序:

users = [
    {"name": "xiaoming", "age": 18},
    {"name": "xiaohong", "age": 15},
    {"name": "xiaogang", "age": 19},
]

print(sorted(users, key=lambda item: item["age"]))
print(sorted(users, key=lambda item: item["age"], reverse=True))

zip() 常用于把两个列表组合成一组数据:

names = ["xiaoming", "xiaohong", "xiaogang"]
ages = [18, 15, 19]

print(list(zip(names, ages)))
# [('xiaoming', 18), ('xiaohong', 15), ('xiaogang', 19)]

五、字符串

字符串可以使用单引号、双引号或三引号创建。

s1 = "hello"
s2 = 'hello'
s3 = """hello
world"""

单引号和双引号没有本质区别。三引号常用于多行字符串,也可以用于函数或类的文档说明。

1. 字符串不可变

字符串是不可变对象,不能通过索引直接修改某一个字符。

s = "hello"

# s[0] = "H"  # TypeError: 'str' object does not support item assignment

s = "Hello"
print(s)

这里不是修改了原来的字符串,而是让变量 s 指向了新的字符串对象。

2. 转义字符和原始字符串

常见转义字符如下:

写法 含义
\n 换行
\t 制表符
\\ 反斜杠
\" 双引号
\' 单引号

示例:

print("hello\npython")
print("name\tage")

如果不希望反斜杠被转义,可以使用原始字符串:

path = r"D:\data\python"
print(path)

3. 字符串格式化

早期代码中经常能看到 % 格式化:

name = "xiaoming"
age = 18

print("name=%s, age=%d" % (name, age))

Python3 中更常用的是 format() 和 f-string。

name = "xiaoming"
age = 18

print("name={}, age={}".format(name, age))
print(f"name={name}, age={age}")

如果需要控制小数位数:

price = 12.3456

print(f"{price:.2f}")  # 12.35

4. 字符串常用方法

字符串方法非常多,不需要一次性全部背下来,先掌握常用的。

方法 作用
strip() 去掉首尾空白字符
split() 按分隔符拆分字符串
join() 使用指定分隔符拼接字符串
replace() 替换字符串内容
find() 查找子串位置,找不到返回 -1
startswith() 判断是否以某内容开头
endswith() 判断是否以某内容结尾
lower() 转小写
upper() 转大写

示例:

line = "  spark,hive,flink  "

line = line.strip()
items = line.split(",")

print(items)               # ['spark', 'hive', 'flink']
print("|".join(items))      # spark|hive|flink
print(line.replace(",", ";"))
print(line.startswith("spark"))
print(line.endswith("flink"))

在 Python3 中,字符串默认是 Unicode 字符串。如果需要和字节数据互相转换,使用 encode()decode()

s = "你好"

data = s.encode("utf-8")
print(data)

text = data.decode("utf-8")
print(text)

六、列表

列表使用方括号定义,元素之间用逗号分隔。

nums = [1, 2, 3, 4, 5]
users = ["xiaoming", "xiaohong"]
mixed = [1, 3.14, "hello", True]

列表可以存放不同类型的数据,不过实际开发中,一个列表通常存放同一种业务含义的数据。

1. 列表索引和切片

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

print(nums[0])     # 1
print(nums[-1])    # 5
print(nums[0:3])   # [1, 2, 3]
print(nums[-3:-1]) # [3, 4]

2. 列表是可变对象

列表和字符串不同,列表可以直接修改元素。

nums = [1, 2, 3]

nums[0] = 100
nums.append(4)

print(nums)  # [100, 2, 3, 4]

也可以通过切片修改列表大小:

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

nums[0:2] = []
print(nums)  # [3, 4, 5]

3. 列表常用方法

方法 作用
append(obj) 在末尾追加一个元素
extend(seq) 在末尾追加另一个序列中的多个元素
insert(index, obj) 在指定位置插入元素
pop(index) 删除并返回指定位置元素,默认最后一个
remove(obj) 删除第一个匹配的元素
count(obj) 统计某个元素出现次数
index(obj) 获取某个元素第一次出现的位置
reverse() 原地反转列表
sort() 原地排序列表
clear() 清空列表
copy() 浅拷贝列表

示例:

nums = [1, 2, 3]

nums.append(4)
nums.extend([5, 6])
nums.insert(0, 100)

print(nums)  # [100, 1, 2, 3, 4, 5, 6]

last = nums.pop()
print(last)  # 6

nums.remove(100)
print(nums)  # [1, 2, 3, 4, 5]

排序示例:

nums = [1, 3, 4, 2]

nums.sort()
print(nums)  # [1, 2, 3, 4]

nums.sort(reverse=True)
print(nums)  # [4, 3, 2, 1]

需要注意:sort() 会修改原列表,sorted() 会返回一个新列表。

nums = [3, 1, 2]

new_nums = sorted(nums)

print(nums)      # [3, 1, 2]
print(new_nums)  # [1, 2, 3]

4. 不要把变量命名为 list

练习代码中有 list = [1, 2, 3] 这种写法。学习阶段能运行,但是不建议这样写。

因为 list 是 Python 内置类型名称,一旦被变量覆盖,后面再调用 list() 就可能出现问题。

建议写成:

nums = [1, 2, 3]
names = ["xiaoming", "xiaohong"]

5. 列表推导式

列表推导式用于快速生成列表。

nums = [2 * x + 1 for x in range(10) if x != 3]

print(nums)
# [1, 3, 5, 9, 11, 13, 15, 17, 19]

也可以写多层循环:

items = [(x, y) for x in [1, 2, 3] for y in ["a", "b"]]

print(items)
# [(1, 'a'), (1, 'b'), (2, 'a'), (2, 'b'), (3, 'a'), (3, 'b')]

列表推导式适合简单转换。如果逻辑太复杂,直接写 for 循环更清楚。

七、元组

元组使用圆括号定义。

t = (1, 2, 3)

print(t[0])   # 1
print(t[-1])  # 3

元组也是序列,可以索引和切片,但是元组本身不可变。

t = (1, 2, 3)

# t[0] = 100  # TypeError: 'tuple' object does not support item assignment

1. 单个元素的元组

定义只有一个元素的元组时,逗号不能省略。

t1 = (1)
t2 = (1,)

print(type(t1))  # <class 'int'>
print(type(t2))  # <class 'tuple'>

2. 元组中可以包含可变对象

元组不可变,指的是元组中保存的引用不能变。

如果元组中保存的是列表,列表本身仍然可以被修改。

t = ([1, 2, 3], [4, 5, 6])

t[1][0] = "hello"

print(t)
# ([1, 2, 3], ['hello', 5, 6])

3. 元组和列表的区别

类型 是否有序 是否可变 常见用途
列表 有序 可变 保存一组需要增删改的数据
元组 有序 不可变 保存结构固定的数据

比如一个坐标点可以用元组表示:

point = (10, 20)

而一组用户名称更适合用列表:

names = ["xiaoming", "xiaohong", "xiaogang"]

八、集合

集合使用大括号或 set() 创建。

s1 = {1, 2, 3, 4}
s2 = set("hello")

print(s1)
print(s2)

集合有两个重要特点:

  1. 元素不能重复
  2. 元素没有固定顺序

例如:

s = {1, 2, 3, 4, 4, 3, 2, 1}

print(s)
# {1, 2, 3, 4}

1. 空集合

空集合不能写成 {},因为 {} 表示空字典。

s = set()

print(type(s))  # <class 'set'>
print(type({})) # <class 'dict'>

2. 集合常用方法

方法 作用
add() 添加一个元素
update() 批量添加元素
remove() 删除指定元素,元素不存在时报错
discard() 删除指定元素,元素不存在不报错
pop() 随机删除并返回一个元素
clear() 清空集合

示例:

s = {1, 2, 3}

s.add(4)
s.update([5, 6])
s.discard(100)

print(s)

如果不确定元素是否存在,优先使用 discard()

3. 集合运算

集合很适合做去重和关系运算。

sa = {1, 2, 3, 4}
sb = {3, 4, 5, 6}

print(sa - sb)  # {1, 2}
print(sa & sb)  # {3, 4}
print(sa | sb)  # {1, 2, 3, 4, 5, 6}
print(sa ^ sb)  # {1, 2, 5, 6}

对应含义如下:

运算 含义
a - b a 中,但是不在 b
a & b 同时在 ab
`a b`
a ^ b ab 中,但是不同时在两者中

一个常见场景是列表去重:

names = ["a", "b", "a", "c"]

print(set(names))
# {'a', 'b', 'c'}

如果要保留原始顺序,可以使用字典:

names = ["a", "b", "a", "c"]

print(list(dict.fromkeys(names)))
# ['a', 'b', 'c']

九、字典

字典用于保存键值对。

user = {
    "name": "xiaoming",
    "age": 18,
}

字典的特点:

  1. 通过 key 查找 value
  2. key 不能重复
  3. value 可以重复

1. 创建字典

d1 = {"k1": "v1", "k2": "v2"}
d2 = dict([("k1", "v1"), ("k2", "v2")])

print(d1)
print(d2)

2. 访问和修改元素

user = {"name": "xiaoming", "age": 18}

print(user["name"])
print(user.get("age"))

user["city"] = "beijing"
user["age"] = 20

print(user)

[]get() 都可以取值,但是行为不同。

user = {"name": "xiaoming"}

# print(user["age"])       # KeyError: 'age'
print(user.get("age"))     # None
print(user.get("age", 0))  # 0

如果 key 可能不存在,使用 get() 更稳。

3. 字典常用方法

方法 作用
get(key, default) 获取值,不存在时返回默认值
keys() 获取所有 key
values() 获取所有 value
items() 获取所有 key/value
pop(key) 删除并返回指定 key 的 value
update(dict) 合并或更新字典
clear() 清空字典

示例:

user = {"name": "xiaoming", "age": 18}

print(user.keys())
print(user.values())
print(user.items())

user.update({"age": 20, "city": "beijing"})
print(user)

age = user.pop("age")
print(age)
print(user)

4. 遍历字典

遍历 key:

user = {"name": "xiaoming", "age": 18}

for key in user.keys():
    print(key, user.get(key))

同时遍历 key 和 value:

for key, value in user.items():
    print(key, value)

遍历 value:

for value in user.values():
    print(value)

实际开发中,items() 用得最多。

5. 字典推导式

字典推导式可以快速生成字典。

values = ["xiaoming", "xiaohong", "xiaogang"]

users = {index: value for index, value in enumerate(values)}

print(users)
# {0: 'xiaoming', 1: 'xiaohong', 2: 'xiaogang'}

也可以对已有字典做过滤:

scores = {
    "xiaoming": 90,
    "xiaohong": 75,
    "xiaogang": 88,
}

passed = {name: score for name, score in scores.items() if score >= 80}

print(passed)
# {'xiaoming': 90, 'xiaogang': 88}

十、几种容器类型对比

类型 写法 是否有序 是否可变 是否允许重复 常见用途
字符串 "abc" 有序 不可变 允许 文本处理
列表 [1, 2, 3] 有序 可变 允许 保存一组可变数据
元组 (1, 2, 3) 有序 不可变 允许 保存结构固定的数据
集合 {1, 2, 3} 无固定顺序 可变 不允许 去重、集合运算
字典 {"k": "v"} 有序 可变 key 不允许重复 保存键值对

这里的字典有序,是指 Python 3.7 之后语言层面保证字典会保留插入顺序。

十一、总结

这一篇主要整理了 Python 中最常用的几类容器对象。

需要重点记住:

  1. 字符串、列表、元组都属于序列,都支持索引、切片和遍历
  2. 字符串和元组不可变,列表可变
  3. 集合适合去重和集合运算
  4. 字典适合保存键值对,实际开发中非常常用
  5. listdictstrset 这些内置名称不要拿来当变量名

这些内容本身不难,但是会在后面的循环、函数、文件、JSON、爬虫解析中反复出现。


文章作者: hnbian
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 hnbian !
评论
  目录