一文了解Python数据结构

本文将详细介绍 Python 的六大数据结构,包括数值、字符串、列表、元组、集合、字典等。阅读本文预计需要 15 min。

一文了解Python数据结构

    • 1. 前言
    • 2. 数值
    • 3. 字符串
      • 3.1 创建字符串
      • 3.2 下标和切片
      • 3.3 字符串的常用操作
    • 4. 列表
      • 4.1 创建列表
      • 4.2 增加列表元素
      • 4.3 删除列表元素
      • 4.4 修改列表元素值
      • 4.5 查询列表元素
      • 4.6 列表的排序
      • 4.7 列表作为栈和队列使用
    • 5. 元组
    • 6. 集合
    • 7. 字典
      • 7.1 创建字典
      • 7.2 增加字典元素
      • 7.3 删除字典元素
      • 7.4 修改字典元素
      • 7.5 查询字典元素
      • 7.6 字典的遍历
    • 8. 小结
    • 9. 巨人的肩膀

1. 前言

数据结构对编程中非常重要的一部分,掌握 Python 数据结构非常必要。本文主要总结 Python 六大数据结构:

  • 数值
  • 字符串
  • 列表
  • 元组
  • 集合
  • 字典

2. 数值

Python 中有 3 种数值类型,分别是整型(int)、浮点型(float)、复数型(complex)。此外布尔型(bool)是整型的子类。通过下面的例子来直观感受一下:

# 多个变量赋值
a, b, c, d = 10, 6.66, 10+5j, False
print(f"a的类型为:{type(a)}")  # type(x) 可以查看 x 的类型
print(f"b的类型为:{type(b)}")
print(f"c的类型为:{type(c)}")
print(f"d的类型为:{type(d)}")

输出结果:
a的类型为:<class 'int'>
b的类型为:<class 'float'>
c的类型为:<class 'complex'>
d的类型为:<class 'bool'>

3. 字符串

文本信息是一种非常重要的信息。在 Python 中,字符串来充当这种角色。在 Python 中字符串是不可变对象,不可以被修改,要修改只能通过重新开辟内存空间,创建新的对象来实现。

这里需要说明一下,Python 中变量更像一个标签,这个标签指向存储了数据的地址,我们可以通过 id() 函数来查看对象的内存地址。

s = 'a'
print(f's 变量的值是:{s},s 变量的地址是:{id(s)}')
s += 'b'  # 等价于 s = s + 'b'
print(f's 变量的值是:{s},s 变量的地址是:{id(s)}')

结果输出:
s 变量的值是:a,s 变量的地址是:2014533253552
s 变量的值是:ab,s 变量的地址是:2014533384432

通过上面这个例子我们可以看到,字符串不可以被改变,一旦改变,内存地址就会发生变化。相当于重新赋值。

3.1 创建字符串

创建字符串有以下几种方式:单引号、双引号、三引号、str()函数。代码直观演示如下:

s1 = 'a'  # 单引号创建单行字符串
s2 = "b"  # 创建单行字符串
s3 = '''
这是三个单引号创建的
多行字符串!'''
s4 = """
这是三个双引号创建的
多行字符串!"""
s5 = str([1, 2, 3])  # [1, 2, 3] 是列表,后面会讲,这里是把列表转化为字符串
print(f'单引号创建的单行字符串内容是:{s1}')
print(f'双引号创建的单行字符串内容是:{s2}')
print(f'三个单引号创建的多行字符串内容是:{s3}')
print(f'三个双引号创建的多行字符串内容是:{s4}')
print(f'str() 函数创建的字符串内容是:{s5}')

输出结果:
单引号创建的单行字符串内容是:a
双引号创建的单行字符串内容是:b
三个单引号创建的多行字符串内容是:
这是三个单引号创建的
多行字符串!
三个双引号创建的多行字符串内容是:
这是三个双引号创建的
多行字符串!
str() 函数创建的字符串内容是:[1, 2, 3]

3.2 下标和切片

下标在 Python 中是一个编号,字符串、列表、元组都可以用下标来索引,我们可以通过下标来找到其对应的元素。注意:下标是从 0 开始编号的,用0-(n-1)索引。假如我们有一个字符串 s = 'abcd',现在我想得到这个字符串的第 1 个元素,那么我们可以通过 s[0] 得到第 1 个元素。此外 Python 还支持负数索引,用(-n)-(-1)来索引。s[-1]表示倒数第 1 个元素。测试如下:

In [1]: s = 'abcd'

In [2]: s[0]
Out[2]: 'a'

In [3]: s[-1]
Out[3]: 'd'

切片是指对操作的对象截取其中一部分的操作。字符串、列表、元组都支持切片操作。切片操作是一个浅拷贝操作,它只拷贝第一层对象。关于深拷贝和浅拷贝,留作以后探讨。切片的语法规则是:[开始:结束:步长],注意以下几点:

  1. 开始、结束、步长省略时,开始默认从头开始,结束默认到结尾,步长默认为 1。
  2. 结束取不到,如: s = '0123's[0:3] 截取的是s[0], s[1], s[2],截取不到 s[3],即取前不取后。
  3. 切片支持负数索引。

测试如下:

In [1]: s = '0123456789'

In [2]: s[0:3]
Out[2]: '012'

In [3]: s[-3:-1]
Out[3]: '78'

In [4]: s[0:10:2]
Out[4]: '02468'

3.3 字符串的常用操作

我们可以对字符串进行很多操作,比如首字母大写、查找、替换、拼接等等。在 Python 中内置了很多字符串操作的方法,我们可以通过 dir(str) 查看字符串的内置方法,在通过 help(str.function)查看该方法的用法。或者直接打开下载的 Python 官方文档检索查看 str 对象的方法。
这里主要列几个用的比较多的方法。

str.split(sep=None, maxsplit=-1) 方法常用来把字符串转化为列表(list),它将字符串 strsep 为分隔符,以 maxsplit 为分割次数,转化为列表。测试如下:

In [18]: s = 'Python is a great language!'

In [19]: s.split(' ')
Out[19]: ['Python', 'is', 'a', 'great', 'language!']

In [20]: s.split(' ', 1)
Out[20]: ['Python', 'is a great language!']

In [21]: s.split()
Out[21]: ['Python', 'is', 'a', 'great', 'language!']

str.join(iterable) 方法常用来把列表(list)转化为字符串,是一种常用高效的字符串拼接方法,它的含义是用 str去拼接iterable中的每个元素。这里需要注意:iterable 的每个元素必须都是字符串类型,否则会报 TypeError。测试如下:

my_list = ['Python', 'is', 'great!']
result = ' '.join(my_list)  # 用空格连接 my_list 中的各个元素
print(result)

输出结果:
Python is great!

如果将 my_list 更改为 my_list = ['Python', 'is', 'great!', 3],那么程序会报错,TypeError: sequence item 3: expected str instance, int found。因为 3int 类型。

此外还可以通过 + 实现字符串拼接功能。在字符串中,我们可以用 +*来增加字符串。其中 + 是拼接字符串,* 是重复字符串。测试如下:

name_info = '我的名字是Jock,'
age_info = '年龄是25岁!'
my_info = name_info + age_info  # 这里加号把两个字符串拼接在一起
print(f'name_info + age_info 结果是:{my_info}')
s = 'abc'
s *= 3  # 等价于 s = s * 3,即把 'abc' 重复 3 次
print(s)

输出结果:
name_info + age_info 结果是:我的名字是Jock,年龄是25岁!
abcabcabc

str.replace(old, new[, count]) 方法是实现将字符串 str 中所有的old 字符片段替换为 new 片段,如果给出了 count 参数,则只替换 count 次。测试如下:

In [11]: s = 'abcdabcdabcd'

In [12]: s.replace('a', 'f', 1)
Out[12]: 'fbcdabcdabcd'

In [13]: s.replace('a', 'f')
Out[13]: 'fbcdfbcdfbcd'

str.strip([chars]) 方法常用来处理字符串的边界,比如去除字符串两端的空格等,它将字符串 str 两侧 [chars] 列表中出现的字符都去除,如果省略 [chars] 则默认去除字符串 str 两侧的空格。测试如下:

In [22]:  '   Python   '.strip()
Out[22]: 'Python'

In [23]: 'www.example.com'.strip('cmowz.')
Out[23]: 'example

4. 列表

列表(list)是 Python 中非常重要且常用的数据类型。在 Python 中 list 是一个可变序列。这里的可变是指 list 的元素个数可以增加或减少。列表用 []包围,每个元素用 , 分隔。

list 非常强大,我们可以把 list 当做一个大容器,什么都可以往里面装,比如:字符串、字典、列表、元组等。此外列表也支持下标和切片操作,非常灵活强大。

4.1 创建列表

我们可以通过以下几种方式创建一个列表。

  1. list_a = [],创建一个空列表,推荐使用。
  2. list_b = list(),使用 list() 函数创建一个空列表。
  3. list_c = [x for x in range(10)],使用列表推导式创建一个列表。

4.2 增加列表元素

往列表中增加元素主要有以下几种方法:

  1. list.append(x):将元素 x 添加到 list 尾部,等价于 list[len(list):len(list)] = [x])
  2. list.extend(list_b):将 list_b 中所有的元素添加到 list 中,等价于 list += list_b
  3. list.insert(i, x):在 list 下标为 i 的位置插入 x,等价于 list[i:i] = [x]
  4. +:列表也可以直接拼接 list_a += list_b

测试如下:

list_a = [1, 2]
list_a.append(3)
print(f'list_a = [1, 2],list_a.append(3) 是:{list_a}')
list_b, list_c = [1, 2, 3], [4, 5, 6]
list_b.extend(list_c)
print(f'list_b = [1, 2, 3],list_c = [4, 5, 6], list_b.extend(list_c) 是:{list_b}')
list_d = [1, 2, 3]
list_d.insert(0, 5)
print(f'list_d = [1, 2, 3],list_d.insert(0, 5) 是:{list_d}')
list_e, list_f = [1, 2, 3], [4, 5, 6]
print(f'list_e = [1, 2, 3],list_f = [4, 5, 6], list_e + list_f 是:{list_e + list_f}'')

结果如下:
list_a = [1, 2],list_a.append(3) 是:[1, 2, 3]
list_b = [1, 2, 3],list_c = [4, 5, 6], list_b.extend(list_c) 是:[1, 2, 3, 4, 5, 6]
list_d = [1, 2, 3],list_d.insert(0, 5)是:[5, 1, 2, 3]
list_e = [1, 2, 3],list_f = [4, 5, 6], list_e + list_f 是:[1, 2, 3, 4, 5, 6]

这里提一下,append()、extend()、insert() 方法都是在原有的 list 上添加,不会开辟新的内存空间,而用 + 会开辟新的内存空间,增加开销。应该根据具体需求灵活进行选择。

4.3 删除列表元素

删除列表元素主要有以下方法:

  1. del list[index]:根据下标进行删除,删除 list 下标为 index 的元素。
  2. pop list[index]:根据下标进行删除,删除并返回 list 下标为 index 的元素。
  3. list.remove(x):根据值进行删除,删除 list 中 x 元素,一次只删除一个 x。
  4. del list[:]:删除 list 中的所有元素,list 变为空列表。
  5. list.clear():删除 list 中的所有元素,list 变为空列表。
  6. del list: 删除整个列表对象。

4.4 修改列表元素值

修改列表元素值的主要是通过下标来确定要修改的是哪个元素,然后才能修改。格式: list[index] = value 将 list[index] 的值修改为 value。测试如下:

In [37]: list_a = [1, 2, 3]

In [38]: list_a[0] = 4

In [39]: list_a
Out[39]: [4, 2, 3]

4.5 查询列表元素

查询列表元素的值有以下几种方法:

  1. list[index]:返回列表中下标是 index 的元素。
  2. in:判断某个元素是否在列表中,如果在,返回 True,否则返回 False。
  3. not in:判断某个元素是否在列表中,如果不在,返回 True,否则返回 False
  4. list.index(x):返回列表中元素 x 的下标。
  5. list.count(x);返回列表中元素 x 出现的次数。

4.6 列表的排序

列表可以通过下标索引,它是一个有序序列,所以我们可以对列表进行排序。排序的方法有以下两种:

  1. list.sort(*, key=None, reverse=False):稳定的原地排序。默认将 list 由小到大排序,reverse=True 可改为倒序,由大到小。
  2. sorted(list):对于原 list 没有改变,返回一个排好序的列表,默认是升序,reverse=True 可改为倒序,由大到小。
  3. list.reverse():将 list 翻转。

4.7 列表作为栈和队列使用

在 Python 中所有可变数据结构都遵循一个设计原则:原地修改可变对象是没有返回值的,或者说返回值是 None。比如 list 中的 sort、insert、remove 方法等。

列表可以作为栈(stack)使用,可以快速的实现(LIFO)后进先出,通过 pop() 和 append() 方法即可。用法如下:

>>> stack = [3, 4, 5]
>>> stack.append(6)
>>> stack.append(7)
>>> stack
[3, 4, 5, 6, 7]
>>> stack.pop()
7
>>> stack
[3, 4, 5, 6]
>>> stack.pop()
6
>>> stack.pop()
5
>>> stack
[3, 4]

同时列表也可以做队列(queue)使用,实现(FIFO)先进先出,通过 pop(0) 和 append() 方法实现。但是由于列表在末尾添加和删除一个元素是非常快的,但是在开头添加和删除一个元素速度比较慢,因为在列表的抬头添加或删除一个元素,都需要移动后面的所有元素。

这时候我们可以使用 collections.deque,它可以实现在开头和结尾都快速的删除和添加一个元素。用法如下:

>>> from collections import deque
>>> queue = deque(["Eric", "John", "Michael"])
>>> queue.append("Terry")           # Terry arrives
>>> queue.append("Graham")          # Graham arrives
>>> queue.popleft()                 # The first to arrive now leaves
'Eric'
>>> queue.popleft()                 # The second to arrive now leaves
'John'
>>> queue                           # Remaining queue in order of arrival
deque(['Michael', 'Terry', 'Graham'])

5. 元组

元组(tuple)是 Python 中一种不可变的序列。因为元组是不变的序列,所以我们不可以对元组进行修改。元组用 ()包围,每个元素用 , 分隔,只有一个元素时,也要有一个 ,

为什么有了列表,还要设计一个不可变的元组呢?因为可变,虽然灵活,但是存在风险,所以元组不可变,代码相对更加安全。

元组可以认为是一种特殊的列表,除了会改变自身的方法不通用之外,其他的通用。比如列表中的 append()、extend()、insert()等方法不能用于元组。

创建一个元组我们有以下几个方法:

  1. t = ():创建一个空元组,推荐。
  2. t = tuple():创建一个空元组。
  3. t = (1, ):创建只有一个元素的元组,注意这里一定要有逗号。

元组其实也是可以”改变“的。元组中的元素是不可以改变的,但是如果元组中的元素是可变对象,比如 list 的话,那么这时候这个可变对象是可以改变的。看下面的例子:

In [56]: t = (1, 2, 3)

In [57]: t[0] = 5
这里会报错:
TypeError: 'tuple' object does not support item assignment

In [58]: t = (1, [0, 1], 3)

In [59]: t[1][0] = 6

In [60]: t
Out[60]: (1, [6, 1], 3)

通过上面的例子我们可以发现,其实元组也是可以“改变”的,我们可以这样理解,上面这个例子中,t = (1, [0, 1], 3),元组 t 中的第二个元素其实是指向了列表[0, 1]的地址,列表[0, 1] 的地址没有改变,但是该地址的内存空间存储的值可以改变。

6. 集合

Python 中集合(set)是一个无序、可哈希的集合。集合具有确定性、互异性、无序性。创建集合的意义:

  1. 利用集合来去重。假如我们要对 list_a 进行去重,那么可以这么操作。list_a = list(set(list_a)).
  2. 判断一个数是否在集合。x in s 或者 x not in s。因为集合是可哈希的,所以它的查找时间复杂度为 O(1),效率远高于列表。
  3. 此外我们还可以用集合来求交集、并集、补集等运算。

我们可以通过 s = set() 创建一个空集合,注意:s = {}创建的是一个空字典。

更多 set 的用法可以用查看官方文档。

7. 字典

字典(dictionary)是 Python 中另一种极为重要的数据类型,是 Python 中唯一一个映射容器,用途非常广泛。字典也是可变的。字典是用{}包围的,专门存储 key: value,即键值对的容器,各键值对用逗号分隔。

它不同于列表和元组等序列通过下标来索引,字典是通过键(key)来索引的。

字典中键必须是不可变的:

  1. 通常我们用数字字符串 来作为字典的键。
  2. 元组也可以作为字典的键,前提是元组中不包含可变对象,比如 (1, 2, 3) 可以作为键,但是 ([1, 2], 2) 不可以作为字典的键。
  3. 列表不可以作为字典的键。因为列表是可变的。

7.1 创建字典

创建字典可以通过以下方式创建:

  1. d = {}:创建一个空字典。
  2. d = dict():利用 dict()函数创建一个空字典。

测试如下:

In [62]: a = dict(one=1, two=2, three=3)

In [63]: b = {'one': 1, 'two': 2, 'three': 3}

In [64]:  c = dict(zip(['one', 'two', 'three'], [1, 2, 3]))

In [65]: d = dict([('two', 2), ('one', 1), ('three', 3)])

In [66]: e = dict({'three': 3, 'one': 1, 'two': 2})

In [67]: a == b == c == d == e
Out[67]: True

7.2 增加字典元素

往字典中添加元素,我们有以下几种方法:

  1. d[key] = value :用这个操作来往字典中添加元素存在风险,可能会使得我有的 key-value 被更改,所以用这个方法前要用 key in d 进行键的判断。
  2. d.update([other]):other 可以是另一个字典,也可以是其他可迭代的键值对。也存在覆盖已有的 key-value 的风险。
  3. d.setdefault(key[, default]):当存在 key 时获取它对应的值,如果不存在这个 key,就加入这个 key,并把它对应的值设为 default ,并返回 default。default 默认是 None

7.3 删除字典元素

删除字典元素有以下几种方法:

  1. del d[key]:删除字典元素 d[key]。
  2. d.clear():清空字典,变为空字典。
  3. del d:删除字典。
  4. d.pop(key[, default]):如果 d 中存在 key 那么删除 key-value 键值对,并返回 key 对应的 value。如果 key 不存在,则返回 default,未指定 default 则报错 KeyError 。
  5. d.popitem():删除并返回一个键值对元组,即:(key, value)。注意:从 Python3.7 开始,删除的键值对遵循后进先出原则(LIFO)。在版本 Python 3.7 之前是任意删除返回一个键值对。

7.4 修改字典元素

我们可以通过 d[key] = value 来修改字典元素,如果 key 不存在,则会往字典中添加 key-value 键值对。

7.5 查询字典元素

查询字典中的元素我们有以下几种方法:

  1. key in d:查询 key 是否在 字典 d 中,如果在返回 True ,否则返回 False.
  2. key not in d:查询 key 是否在 字典 d 中,如果不在返回 True ,否则返回 False.
  3. d[key]:返回 key 对应的 value。如果 key 不存在,报错 KeyError
  4. d.get(key[, default]):这是 d[key] 的友好模式。如果 key 在字典中,返回其对应的 value,否则返回 default,default 默认为 None。

7.6 字典的遍历

  1. 遍历字典的 key:for key in d
  2. 遍历字典的 value: for value in d.values()
  3. 遍历字典的项:for item in d.items()
  4. 遍历字典的 key-value: for key, value in d.items()

示例如下:

d = {"one": 1, "two": 2, "three": 3, "four": 4}

print('遍历字典的key')
for key in d:
    print(key)

print('遍历字典的value')
for value in d.values():
    print(value)

print('遍历字典的item')
for item in d.items():
    print(item)

print('遍历字典的key-value')
for key, value in d.items():
    print(key, value)

结果输出:
遍历字典的key
one
two
three
four
遍历字典的value
1
2
3
4
遍历字典的item
('one', 1)
('two', 2)
('three', 3)
('four', 4)
遍历字典的key-value
one 1
two 2
three 3
four 4

for 循环的用法,我们下次学习会总结。这部分中字符串、列表、字典非常重要,它们的方法也很多,很难全部记住。如果忘记了我们及时查看官方文档的用法即可,不一定非要死记硬背。

8. 小结

学习这部分之后我们需要:

  1. 掌握可变对象和不可变对象区别。
  2. 掌握字符串、列表、元组、字典、集合的增删改查操作。
  3. 了解下标和切片。
  4. 了解每一种数据结构的应用场景。

9. 巨人的肩膀

  1. The Python 3.8 Tutorial

推荐阅读:

  1. 编程小白安装Python开发环境及PyCharm的基本用法
  2. 一文了解Python基础知识
  3. 一文了解Python数据结构
  4. 一文了解Python流程控制
  5. 一文了解Python函数
  6. 一文了解Python部分高级特性
  7. 一文了解Python的模块和包

你可能感兴趣的:(一文了解Python数据结构)