跳转至

第3章 常用数据结构

3.1.1 序列的种类

容器序列与扁平序列

  • 容器序列(Container Sequences)
  • 容器序列中存储的并不是对象本身,而是对象的引用。因此,容器序列中可以存放不同类型的数据。
  • listtuplecollections.deque
  • 扁平序列(Flat Sequences)
  • 扁平序列中存储的是对象的取值(即对象自身),要求一段连续的内存空间。因此,扁平序列中的对象必须具有相同的类型。
  • strbytesbytearrayarray.array

可变序列与不可变序列

  • 可变序列
  • 序列中存储的元素可以被添加、删除、修改
  • 常见的可变序列有listbytearrayarray.arraycollections.deque
  • 不可变序列
  • 序列创建之后,其中的元素不能再添加、删除修改
  • 常见的不可变序列有tuplestrbytes

  • 不可变的含义

    • “不可变”是指其中存储的引用是不能改变的,并不意味着引用指向的对象是不可改变的
1
2
t = (1, 'w', [4, 2])
t[2] = 1
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
---------------------------------------------------------------------------

TypeError                                 Traceback (most recent call last)

<ipython-input-3-4623fcbd5836> in <module>
      1 t = (1, 'w', [4, 2])
----> 2 t[2] = 1


TypeError: 'tuple' object does not support item assignment
1
2
t[2][0]=0
t
1
(1, 'w', [0, 2])

3.1.2 序列的基本操作

  • 索引
  • 索引取值可以为负

1
2
3
4
lst = [1, 4, 2, 8, 5, 7]
print(lst[0])
print(lst[-1])
print(lst[3], lst[-3])
1
2
3
1
7
8 8
  • 切片
  • 形如i:j:k
1
2
3
4
5
6
lst = [1, 4, 2, 8, 5, 7]
print('lst[1:5] = ', lst[1:5])
print('lst[:-1] = ', lst[:-1])
print('lst[-5:-2] = ', lst[-5:-2])
print('lst[:] = ', lst[:])
print('lst[::2] =', lst[::2])
1
2
3
4
5
lst[1:5] =  [4, 2, 8, 5]
lst[:-1] =  [1, 4, 2, 8, 5]
lst[-5:-2] =  [4, 2, 8]
lst[:] =  [1, 4, 2, 8, 5, 7]
lst[::2] = [1, 2, 5]
  • 连接与重复
1
(1, 4, 2) + (8, 5, 7)
1
(1, 4, 2, 8, 5, 7)
1
[1, 4, 2] + (8, 5, 7)
1
2
3
4
5
6
7
8
9
---------------------------------------------------------------------------

TypeError                                 Traceback (most recent call last)

<ipython-input-17-fce18c4ee146> in <module>
----> 1 [1, 4, 2] + (8, 5, 7)


TypeError: can only concatenate list (not "tuple") to list
1
[1, 4, 2] * 3
1
[1, 4, 2, 1, 4, 2, 1, 4, 2]
1
3 * 'Python '
1
'Python Python Python '
  • 元素检查
  • 判断一个数值或对象是否包含在序列中
  • 可使用innot in运算符
  • index方法
  • count方法
1
2
3
lst = ['Python', 'Java', 'C++']
print('Python' in lst)
print('C#' not in lst)
1
2
True
True
1
lst.index('Python')
1
0
1
2
s = 'Hello Python'
s.count('o')
1
2
  • 其他序列操作
  • len:返回序列的长度
  • max:返回序列中的最大值
  • min:返回序列中的最小值
  • sum:序列元素值求和
  • sorted:对序列元素进行排序,不可变序列排序返回一个排序后的列表
1
2
3
4
5
lst = [1, 4, 2, 8, 5, 7]
print(len(lst))
print(max(lst))
print(min(lst))
print(sum(lst))
1
2
3
4
6
8
1
27
1
2
print(sorted(lst))
print(sorted(lst, reverse=True))  # 逆序排序
1
2
[1, 2, 4, 5, 7, 8]
[8, 7, 5, 4, 2, 1]

3.2 列表

3.2.1 列表的定义

1
2
3
4
lst = [1, 4, 2, 8, 5, 7]
lst = list() # lst = []
s = 'Python'
list(s)  # list将字符串转为列表
1
['P', 'y', 't', 'h', 'o', 'n']

3.2.2 列表元素操作

  • 添加元素:list.append(obj)
1
2
3
lst = [1, 4, 2, 8]
lst.append(5)
lst
1
[1, 4, 2, 8, 5]
  • 插入元素: list.insert(index, obj)
1
2
3
4
5
lst = [1, 4, 2, 5]
lst.insert(4, 8)
lst
lst.insert(2, [0, 0])
lst
1
[1, 4, [0, 0], 2, 5, 8]
  • 扩充元素:list.extend(seq_obj)
1
2
3
lst = [1, 4, 2, 8]
lst.extend([5, 7])
lst
1
[1, 4, 2, 8, 5, 7]
  • 删除元素
  • del函数
  • list.remove(obj)方法
  • list.clear方法
1
2
3
4
5
6
7
lst = [1, 4, 2, 8, 5, 7]
del lst[0]
print(lst)
lst.remove(7)
print(lst)
lst.clear()
print(lst)
1
2
3
[4, 2, 8, 5, 7]
[4, 2, 8, 5]
[]
  • 修改元素值
1
2
3
4
5
6
7
8
9
lst = [1, 4, 2, 8]
lst[-1] = 7              # 索引修改
print(lst)
lst[0:2] = [5, 7]        # 修改前两个元素
print(lst)
lst[::2] = [0, 0]        # 修改索引为偶数的元素
print(lst)
lst[2:2] = [4, 2, 4, 2]  # 插入多个元素
print(lst)
1
2
3
4
[1, 4, 2, 7]
[5, 7, 2, 7]
[0, 7, 0, 7]
[0, 7, 4, 2, 4, 2, 0, 7]
  • 排序
  • sorted
  • list.sort
  • 区别
    • sorted函数会复制出一份新的列表,在新的列表上排序,原来的列表不会被改变,而list.sort方法则在原列表上排序
1
2
lst = [1, 4, 2, 8, 5, 7]
lst_id = id(lst)
1
2
lst_sorted = sorted(lst)
lst_id == id(lst_sorted)
1
False
1
2
lst.sort()
lst_id == id(lst)
1
True
  • 列表的reverse方法用于反转列表元素的顺序。该方法也是在序列上操作,不会返回新的序列
1
2
3
4
5
lst = [1, 4, 2, 8, 5, 7]
lst_id = id(lst)
lst.reverse()
print(lst_id == id(lst))
print(lst)
1
2
True
[7, 5, 8, 2, 4, 1]
  • 复制
  • list.copy
1
2
3
4
5
lst = [1, 4, 2, 8, 5, 7]
lst_new = lst.copy()
lst_new.sort()
print(lst_new)
print(lst)
1
2
[1, 2, 4, 5, 7, 8]
[1, 4, 2, 8, 5, 7]
  • 浅复制,list.copycopy.copy
1
2
3
4
5
lst = [1, 4, 2, 8, [5, 7]]
lst_new = lst.copy()
lst_new[-1][0] = 0
print(lst_new)
print(lst)
1
2
[1, 4, 2, 8, [0, 7]]
[1, 4, 2, 8, [0, 7]]
  • 深复制,copy.deepcopy
1
2
3
4
5
6
from copy import deepcopy
lst = [1, 4, 2, 8, [5, 7]]
lst_new = deepcopy(lst)
lst_new[-1][0] = 0
print(lst_new)
print(lst) 
1
2
[1, 4, 2, 8, [0, 7]]
[1, 4, 2, 8, [5, 7]]

3.2.3 列表推导式

  • 在Python中应当尽可能减少循环语句的使用
  • 能使用列表推导式实现时,就不要使用循环
  • 列表推导式的语法形式为:
1
[表达式 for...in iter_obj]
1
2
3
lst_str = ['1', '4', '2', '8', '5', '7']
lst = [int(s) for s in lst_str]
lst
1
[1, 4, 2, 8, 5, 7]
  • 同时遍历多个序列
    1
    [表达式 for...in iter_obj1 for... in iter_obj2 ...]
    
1
2
3
4
lst1 = [11, 12, 13]
lst2 = [21, 22, 23]
lst_new = [(e1, e2, e1 + e2) for e1 in lst1 for e2 in lst2]
lst_new
1
2
3
4
5
6
7
8
9
[(11, 21, 32),
 (11, 22, 33),
 (11, 23, 34),
 (12, 21, 33),
 (12, 22, 34),
 (12, 23, 35),
 (13, 21, 34),
 (13, 22, 35),
 (13, 23, 36)]
  • if子句
1
[表达式 for...in list_obj1 for... in list_obj2 ... if 逻辑表达式]
1
2
3
4
5
6
7
lst1 = [11, 12, 13]
lst2 = [21, 22, 23]
lst_add = [e1 + e2 for i, e1 in enumerate(lst1) for j, e2 in enumerate(lst2) if i == j]
lst_add

nes_lst = [e1 + e2 for e1, e2 in zip(lst1, lst2)]
nes_lst
1
[32, 34, 36]
  • 列表推导式与循环的性能比较
1
from time import time
1
2
3
4
5
start_time = time()
l = []
for i in range(10000000):
    l.append(i)
print(time()-start_time)
1
1.1165800094604492
1
2
3
start_time = time()
l = [i for i in range(10000000)]
print(time()-start_time)
1
0.7393479347229004
  • 更快速的方式
1
2
3
start_time = time()
l = list(range(10000000))
print(time()-start_time)
1
0.5307950973510742

3.2.4 栈

  • 栈的特征
  • 后进先出(Last In First Out)
  • list.pop方法的作用是弹出列表的最后一个元素
  • Python列表没有push方法,但可使用append方法替代
1
2
3
4
stack = [1, 4, 2, 8, 5, 7]
print(stack.pop())
stack.append(7)
print(stack)
  • 也可以利用Python动态语言的特性,使得栈的操作更加附合使用习惯
1
2
3
4
5
6
7
stack = [1, 4, 2, 8, 5, 7]
pop = stack.pop
push = stack.append
print(pop())
print(stack)
push(7)
print(stack)
1
2
3
7
[1, 4, 2, 8, 5]
[1, 4, 2, 8, 5, 7]

3.3 元组

3.3.1 元组的定义与使用

  • 定义
  • ()
  • tuple
1
2
3
4
5
6
7
8
t = tuple()
print(t)
lst = [1, 4, 2, 8, 5, 7]
t = tuple(lst)
print(t)
s = 'Python'
t = tuple(s)
print(t)
1
2
3
()
(1, 4, 2, 8, 5, 7)
('P', 'y', 't', 'h', 'o', 'n')
  • 当元组中只有一个元素时,使用()定义必须要在后边加一个逗号
1
2
3
t = (1)
print(t)
print(type(t))
1
2
1
<class 'int'>
1
2
t = (1,)
print(type(t))
1
<class 'tuple'>
  • 元组的操作
  • 索引、切片、连接、重复、成员资格检查,以及长度、最大值、最小值等
  • 元素不能改变

3.3.2 元组的不可变陷阱

  • 如果将一个可变序列作为元组的元素,那么这个可变序列的元素是否能够被改变呢?
  • 元组同时还是一个容器类型,其中存储的并不是元素自身而是 元素的引用
  • “不可变”是指其中存储的引用是不能改变的,并不意味着引用指向的对 象是不可改变的
1
2
t = (1, 'w', [4, 2])
t[2] = 1
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
---------------------------------------------------------------------------

TypeError                                 Traceback (most recent call last)

<ipython-input-2-018f412d059b> in <module>
      1 t = (1, 'w', [4, 2])
----> 2 t[2] = 1
      3 t


TypeError: 'tuple' object does not support item assignment
1
2
t[2][0]=0 
t
1
(1, 'w', [0, 2])

3.3.3 生成器推导式*

  • 在定义时,生成器推导式与元组的关系,也列表生成器与列表的关系相似

1
(表达式 for...in iter_obj)
- 生成器推导式中也可以包含多个for子句以及if子句 - 本质上,生成器推导式与元组其实关系不大 - 它返回的是一个生成器 - 生成器是一种特殊的可迭代对象,它并没有保存所有的无素,而是仅仅定义了获取下一个元素的方法

  • 性能比较
1
2
from time import time
from sys import getsizeof
1
2
3
4
start_time = time()
lst = [i for i in range(10000000)]
print(time()-start_time)
print(getsizeof(lst))
1
2
0.670072078704834
81528048
1
2
3
4
start_time = time()
g = (i for i in range(10000000))
print(time()-start_time)
print(getsizeof(g))
1
2
9.274482727050781e-05
112

3.4 集合

3.4.1 集合的定义

  • 可变集合set
  • 可以使用set()定义一个空集合或将其他可迭代对象转为一个可变集合类型
  • 不可变集合frozenset
  • 可以定义一个空的不可变集合,或者将其他可迭代对象转为一个不可变集合
1
2
s = {1, 4, 2, 1, 2, 7}
s
1
{1, 2, 4, 7}
  • 利用集合不允许重复元素的特点,可以非常高效地去除序列中的重复元素
1
2
3
4
l = list(range(10000)) * 2
print(len(l))
s = set(l)
print(len(s))
1
2
20000
10000

3.4.2 常用集合操作方法

  • 集合元素操作
  • 集合是一种可迭代对象,可以利用循环语句来遍历每个元素。但由于元素是无序的,因此无法像列表那样利用索引或切片访问单个或部分元素。不过,可变集合可以添加或去除其中的元素。常见的集合元素操作见下表
方法 功能 示例 结果
set.add 添加一个元素 {1,4,2}.add(8) {8,1,2,4}
set.update 添加多个元素 {1,4}.update({2,8}) {8,1,2,4}
set.remove 去除元素 {1,4,2,8}.remove(0) KeyError
set.discard 去除元素 {1,4,2,8}.discard(0) {8,1,2,4}
set.pop 随机弹出元素 {1,4,2,8}.pop()
set.clear 清空集合 {1,4,2,8}.clear() {}

此外,用于求元素数量(len)、最大值(max)、最小值(min)、求和(sum)等函数对集合对象也有效

  • 集合运算
  • Python中的集合类型具有和数学中集合相同的交、并、差,以及对称差等集合运算。集合运算可以使用运算符,也可以使用集合对象的方法实现,见下表 Python中的集合类型具有和数学中集合相同的交、并、差,以及对称差等集合运算。集合运算可以使用运算符,也可以使用集合对象的方法实现,见下表
运算符 方法 运算 示例 结果
& set.intersection {1,2,3} & {4,2} {2}
\| set.union {1,2,3} \| {4,2} {1,2,3,4}
- set.difference {1,2,3} - {4,2} {1,3}
^ set.symmetric_difference 对称差 {1,2,3} ^ {4,2} {1,3,4}
  • 关系检查
  • 判断集合中是否包含一个元素的方法与序列相同,使用innot in运算符。集合类型提供了相应的方法来判断两个集合之间的关系。见下表
方法/运算符 功能 示例 结果
set.isdisjoint 交集是否为空 {1,2,3}.isdisjoint({1,2}) False
set.issubset 是否为子集 {1,2}.issubset({1,2,3}) True
set.issuperset 是否为超集 {1,2,3}.issuperset({1,2}) True

此外,两个集合对象关系判断更方便的方法是使用比较运算符:集合相等(==)、集合不全相等(!=)、子集(<=)、真子集(<)、超集(>=)、真超集(>

3.4.3 集合推导式

  • 集合推导式与列表推导式和生成器推导式的概念相似,可以不使用循环基于一个可迭代对象创建一个集合。集合推导式的语法形式与列表推导式的唯一区别就是将[]换为{}
1
{表达式 for...in iter_obj}
  • 当然,集合推导式中也可以包含多个for子句以及if子句
1
2
3
4
5
6
7
8
scores = [
    ('张三', '数学', 90),
    ('李四', '数学', 80),
    ('王五', '英语', 85),
    ('张三', '英语', 95)
]
name_set = {e[0] for e in scores}  # 集合推导式
print(f'有 {len(name_set)} 人参加了考试')
1
有 3 人参加了考试

3.4.4 排列组合

  • 排列,是从\(n\)个元素中不重复地取出\(m\)个并按一定的顺序排列,称为一个排列。所有可能的排列的个数,称为排列数,记为: $$ A_n^m = n(n-1)(n-2)\cdots(n-m+1) = \frac{n!}{(n-m)!} $$
  • 组合,是从\(n\)个元素中不重复地最出\(m\)个,称为一个组合。所有可能的组合的总数称为组合数,记为: $$ C_n^m= \frac{P_n^m}{P_m} = \frac{n!}{m!(n-m)!} $$

  • Python的itertools包提供了两个函数permutationscombinations分别用于排列和组合的运算

1
2
3
4
5
6
from itertools import permutations, combinations
s = {1, 4, 2, 8}
p = permutations(s, 2)     # 排列
print(list(p))
c = combinations(s, 2)     # 组合
list(c)
1
2
3
[(8, 1), (8, 2), (8, 4), (1, 8), (1, 2), (1, 4), (2, 8), (2, 1), (2, 4), (4, 8), (4, 1), (4, 2)]

[(8, 1), (8, 2), (8, 4), (1, 2), (1, 4), (2, 4)]

3.5 字典

3.5.1 字典的定义

  • 以键-值对(key-value)的形式存储元素,可以使用{key:value, ...}的形式定义
  • {}可以用于定义一个空词典。字典的类型为dict,空字典也可以使用dict()定义。也可以使用dict()定义字典,或者将一个由元组组成的列表转换为字典
1
2
d = dict(a=1, b=4, c=2, d=8)
d
1
{'a': 1, 'b': 4, 'c': 2, 'd': 8}
1
2
lst = [('a', 1), ('b', 4), ('c', 2), ('d', 8)]
dict(lst)
1
{'a': 1, 'b': 4, 'c': 2, 'd': 8}

3.5.2 字典常用操作方法

  • 字典元素的操作
  • 字典元素的访问、修改、删除使用key实现
1
2
d = {'a':1, 'b':4, 'c':2, 'd':8}
d['a']
1
1
1
2
d['e'] = 5
d
1
{'a': 1, 'b': 4, 'c': 2, 'd': 8, 'e': 5}
1
2
del d['e']
d
1
{'a': 1, 'b': 4, 'c': 2, 'd': 8}
  • 在使用key访问元素时,若key不存在会抛出KeyError。更安全的方法是使用字典对象的get方法,key不存在的时返回None或者指定的默认值
1
2
d = {'a':1, 'b':4, 'c':2, 'd':8}
d['e']
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
---------------------------------------------------------------------------

KeyError                                  Traceback (most recent call last)

<ipython-input-15-756b0088b919> in <module>
      1 d = {'a':1, 'b':4, 'c':2, 'd':8}
----> 2 d['e']


KeyError: 'e'
1
d.get('e') is None
1
True
1
d.get('e', 'no exist')
1
'no exist'
  • 字典的遍历
  • 字典的遍历有多种方法,可以只遍历所有的key或value,也可以遍历所有的key-value对。分别通过如下三个方法实现:

    • dict.keys:返回字典对象中所有key组成的可迭代对象
    • dict.values:返回由所有value组成的可迭代对象
    • dict.items:返回由元组(key, value)组成的可迭代对象
1
2
3
4
d = {'a':1, 'b':4, 'c':2, 'd':8}
print(d.keys())
print(d.values())
print(d.items())
1
2
3
dict_keys(['a', 'b', 'c', 'd'])
dict_values([1, 4, 2, 8])
dict_items([('a', 1), ('b', 4), ('c', 2), ('d', 8)])
1
2
3
4
5
6
d = {'a': 1, 'b': 4, 'c':2, 'd': 8}
for key in d.keys():          # 方法1
    print(key, d[key])

for key, value in d.items():  # 方法2
    print(key, value)
1
2
3
4
5
6
7
8
a 1
b 4
c 2
d 8
a 1
b 4
c 2
d 8
  • 字典的其他操作
方法 功能 示例 结果
dict.clear 清空字典 {'a':1, 'b':4, 'c':2, 'd':8}.clear() {}
dict.popitem 随机弹出一个key-value对 {'a':1, 'b':4, 'c':2, 'd':8}.popitem() ('d', 7)
dict.pop 弹出指定的值 {'a':1, 'b':4, 'c':2, 'd':8}.pop('a') 1
dict.update 利用另一个字典更新当前字典 {'a':4, 'b':2}.update({'a':1, 'b':3}) {'a': 1, 'b': 3}

3.5.3 字典推导式

  • 字典推导式与列表推导式和集合推导式类似,能够不使用循环的情况下创建一个字典。其语法形式为:

1
{key:value for key, value in iter_obj}
- 当然,字典推导式中也可以包含多个for子句以及if子句,用于在更复杂的情况下构造字典

1
2
3
languages = ['Python', 'Java', 'C', 'C++', 'C#']
language_len = {e: len(e) for e in languages}
language_len
1
{'Python': 6, 'Java': 4, 'C': 1, 'C++': 3, 'C#': 2}

3.6 字符串

3.6.1 字符串的定义

  • 'Python'
  • "Python"
  • '''Python'''
  • """Python"""
  • u'Python'
  • r'Python'
  • b'Python'

3.6.2 常用字符串处理方法

  • 字符串大小写转换
操作 功能 示例输入 示例操作 示例输出
str.lower 所有字母转为小写 s='Python' s.lower() 'python'
str.upper 所有字母转为大写 s='Python' s.upper() PYTHON
str.swapcase 大小写互换 s='Python' s.swapcase() 'pYTHON'
str.title 单词首字母大写 s='python is easy' s.title() 'Python Is Easy'
  • 空白去除与填充
操作 功能 示例输入 示例操作 示例输出
str.strip 去除两侧空白 s=' Python ' s.strip() 'Python'
str.lstrip 去除左侧空白 s=' Python ' s.lstrip() 'Python '
str.rstrip 去除右侧空白 s=' Python ' s.rstrip() ' Python'
str.center 两侧填充符号 s='Python' s.center(10, '*') '**Python**'
str.ljust 左侧填充符号 s='Python' s.ljust(10, '*') 'Python****'
str.rjust 右侧填充符号 s='Python' s.rjust(10, '*') '****Python'
str.zfill 左侧补0 s='Python' s.zfill(10) '0000Python'
  • 字符串查找与替换
操作 功能 示例输入 示例操作 示例输出
str.find 从左查找子串,
返回索引或-1
s='Python is easy' s.find('y') 1
str.rfind 从右查找子串,
返回索引或-1
s='Python is easy' s.rfind('y') 13
str.index 从左查找子串,
返回索引或错误
s='Python is easy' s.index('y') 1
str.rindex 从右查找子串,
返回索引或错误
s='Python is easy' s.rindex('y') 13
str.replace 替换子串 s='Python is easy' s.replace('is', '') 'Python easy'
str.expandtabs Tab符替换为空格 s='Python\teasy' s.expandtabs(2) 'Python easy'
  • 字符串翻译
  • 字符互换
1
2
3
trans=str.maketrans('abc', '123')
s = 'abccba'
s.translate(trans)
1
'123321'
  • 字符串分割
操作 功能 示例输入 示例操作 示例输出
str.split 从左侧分割 s='a,b,c' s.split(',') ['a', 'b', 'c']
str.rsplit 从右侧分割 s='a,b,c' s.rsplit(',', 1) ['a,b', 'c']
str.splitlines 按行分割 s='a\nb\nc' s.splitlines() ['a', 'b', 'c']
str.partition 从左侧切断 s='a,b,c' s.partition(',') ('a', ',', 'b,c')
str.rpartition 从右侧切断 s='a,b,c' s.rpartition(',') ('a,b', ',', 'c')
  • 连接
1
2
3
lst = ['1', '4', '2', '8']
'-'.join(lst)
'1-4-2-8'
1
'1-4-2-8'
  • 字符串的特征测试
方法 功能 示例 方法调用 输出
isalnum 是否仅含字母或数字 s='Abc123' s.isalnum() True
isdecimal 是否仅含十进制数字 s='123' s.isdecimal() True
isalpha 是否仅含字母 s='Abc123' s.isalpha() Fasle
isdigit 是否仅含整数数字 s='123.0' s.isdigit() False
isnumeric 是否仅含数字 s='123' s.isnumeric() True
isupper 是否不含小写符号 s='ABC123' s.isupper() True
islower 是否不含大写 s='abc123' s.islower() True
isspace 是否仅含空白符号 s=' \t' s.isspace() True
istitle 是否首字母大写 s='Python is easy' s.istitle() False
isascii 是否仅含 ASCII 符号 s='Python 编程' s.isascii() False
isprintable 是否为可打印符号 s='\t' s.isprintable() False
startswith 是否以给定子串开头 s='Abc123' s.startswith('Abc') True
endswith 是否以给定子串结尾 s='Abc123' s.endswith('123') True
  • 字符串的动态执行*
  • 函数
    • exec:作为脚本代码执行
    • eval:作为表达式执行
  • 存在安全隐患
1
exec('print("Hello Python!")')
1
Hello Python!
1
eval('5 > 3')
1
True
1
2
lst = eval('[1, 4, 8, 2, 7]')
lst
1
[1, 4, 8, 2, 7]

3.6.3 字符串格式化

  • 将一个字符串模板中的占位符替换为所需输出的具体值,从而实现复杂的字符串输出
  • 三种格式化方法
  • %符号
  • format方法
  • f-string

使用 %符号格式化字符串

与C语言中printf函数的使用方法相似。

利用%符号格式化字符串时,占位符以%开头,其语法形式为%[(name)][flags][width].[precision]type

  • (name):可选,用于以字典的形式传递占位数值
  • flags:可选,可能的取值有
  • + 右对齐;正数前加正好,负数前加负号
  • - 左对齐;正数前无符号,负数前加负号
  • 空格 右对齐;正数前加空格,负数前加负号
  • 0 右对齐;正数前无符号,负数前加负号;用0填充空白处

  • width: 取值为整数,表示占位宽度

  • precision: 取值为整数,表示保留小数位数
  • type: 表示占位数据类型

  • 常用类型符号

符号 类型 示例 输出
c 字符或ASCII码值 '%c%c' % (80, 121) 'Py'
s 字符串 'Hello %s' % 'Python' 'Hello Python'
d 整数 '%d-%d' % (4, 2) '4-2'
f 浮点数 'pi is %f' % 3.14 'pi is 3.140000'
e 科学计数法 'pi is %e' % 3.14 'pi is 3.140000e+00'
g 相当于fe 'pi is %g' % 3.14 'pi is 3.14'
  • 示例
功能 示例 输出
以字典的方式传递占位值 '%(n1)d + %(n2)d = %(rst)d' % {'n1':3, 'n2': 2, 'rst': 5} '3 + 2 = 5'
指定小数位数和总宽度 'pi is %10.3f' % 3.1415926 'pi is 3.143'
指定宽度,不足左侧补0 'pi is %010.3f' % 3.1415926 'pi is 000003.143'
左对齐 'pi is %-10.3f' % 3.1415926 'pi is 3.143 '

利用str.format方法格式化字符串

  • 利用str.format方法格式化字符串要更加灵活
  • 占位符为{},形式为 {:format_spec}

format_spec常用的语法形式为[[fill]align][sign][0][width][,][.precision][type]

  • fill:填充符号
  • align:对齐方式,取值可以是
  • <:左对齐
  • >:右对齐
  • =:仅对数字有效,在sign和数字之间填充符号
  • ^:居中对齐

  • sign:符号,取值可以是

  • +:正数前有+,负数前有-
  • -:负数前有-,该取值为默认值
  • 空格:正数前有空格,负数前有-
  • 0:在数值前补0,仅对数值有效
  • width:占位宽度
  • ,:仅对数字有效,表示千位分隔符
  • precision:表示有效数字位数(注意与%符号格式化有区别)
  • type:占位数据类型,常用类型与%符号格式化字符串一致,默认取值为d

  • 示例

功能 示例 输出
指定小数位数和总宽度 'pi is {:10.3}'.format(3.1415926) 'pi is 3.14'
指定宽度,不足左侧补0 'pi is {:010.3}'.format(3.1415926) 'pi is 0000003.14'
左对齐 'pi is {:<10.3}'.format(3.1415926) 'pi is 3.14 '
左对齐,填充* 'pi is {:*<10.3}'.format(3.1415926) 'pi is 3.14******'
  • 按顺序替换
1
 'The sercret numbers are {}, {}, {}, and {}'.format(9, 5, 2, 7)
1
'The sercret numbers are 9, 5, 2, and 7'
  • 按索引替换
1
'The sercret numbers are {3}, {2}, {1}, and {0}'.format(9, 5, 2, 7)
1
'The sercret numbers are 7, 2, 5, and 9'
  • 按标识符替换
1
'The sercret numbers are {one}, {two}, {three}, and {four}'.format(one=9, two=5, three=2, four=7)
1
'The sercret numbers are 9, 5, 2, and 7'

利用f-Strings格式化字符串

  • f-Strings格式化字符串与str.format方法非常相似。f-String的语法形式为{表达式}或者{表达式:format_spec}
  • format_spec的语法形式与str.format完全一样
  • f-Strings与str.format方法的区别
  • 字符串前加f以标识出其为一个格式字符串
  • 不使用format函数,而直接将表达式放置在:
功能 示例 输出
指定小数位数和总宽度 f'pi is {3.1415926:10.3}' 'pi is 3.14'
指定宽度,不足左侧补0 f'pi is {3.1415926:010.3}' 'pi is 0000003.14'
左对齐 f'pi is {3.1415926:<10.3}' 'pi is 3.14 '
左对齐,填充* f'pi is {3.1415926:*<10.3}' 'pi is 3.14******'
  • Python 3.8中的新功能
1
2
 x, y = 3, 2
f'变量的取值为 x={x}, y={y}'
1
'变量的取值为 x=3, y=2'
1
 f"变量的取值为 {x=}, {y=}"
1
'变量的取值为 x=3, y=2'

3.7 二进制序列

3.7.1 字节串的原理

  • 二进制序列的类型
  • 字节串bytes(或称为字节码)
    • 不可变类型
  • 字节数组bytearray

    • 可变类型
  • 二进制序列 VS 字符串

  • 二进制序列不是字符串
  • 字符串的最终存储形式为二进制序列,其他数据如图像、音频、视频等也以二进制序列的形式存储
  • 一个字节包含8位,ASCII码中的一个符号也占用一个字节

    • 如果字节串的内容都是ASCII符号,则可以直接在字符串前加前缀b来构建字节串
  • 字节串在显示的时候将一个字节的8位分为两部分,每部分4位,用一个十六进制数字表示

  • 字节串的中每个元素本来面目是两个十六进制数字

3.7.2 字节串的应用

  • Python 3.x默认编码方式为utf-8,每个中文字符用3个字节存储
  • 下例中,“很”字的二进制序列表示为 \xe5\xbe\x88
  • \x 表示十六进制数,第一个字节的十六进制数值为 e5
  • 每个 ASCII 字符占 1 个字节,每个中文占 3 个字节,因此字节串的长度为 18
1
'Python很有意思'.encode('utf-8')
1
b'Python\xe5\xbe\x88\xe6\x9c\x89\xe6\x84\x8f\xe6\x80\x9d'
1
2
3
bs =[i for i in bs]
print(len(bs))
print(bs)
1
2
18
[80, 121, 116, 104, 111, 110, 229, 190, 136, 230, 156, 137, 230, 132, 143, 230, 128, 157]
  • 字节串(bytes)只负责以字节序列的形式来记录数据,至于这些数据到底表示什么 内容,完全由数据自身的编码格式决定
  • 采用正确的字符集,字符串可以转换成字 节串;反过来,字节串也可以恢复成对应的字符串
  • 图像可以用适当的编码方式 保存为字节串,而字节串也可以用同样的编码方式恢复成图像

3.8 高级数据结构

3.8.1 collection模块

  • ChainMap
  • ChainMap对多个字典对象进行整合,并且实现了大部分字典的方法,能够将多个字典当一个字典来使用
  • 字典中的第一个称为子map,其他的称为父map。ChainMap中除了大部分字典操作字典的方法,还有如下几个常用的属性或方法:
    • maps:返回所有字典构成的列表
    • parents:返回所有的父map构成的列表
    • new_child方法:返回一个新的ChainMap对象,其中包含一个空的子map,原对象中的字典都被作为父map
1
2
3
4
5
from collections import ChainMap
m1 = {'a': 1, 'b':4}
m2 = {'c': 2, 'd':8}
cm = ChainMap(m1, m2)
cm.maps
1
[{'a': 1, 'b': 4}, {'c': 2, 'd': 8}]
1
2
3
cm['c'] = 0              # 只能有子map能修改
print(cm)
cm.new_child()           # 得到一个新的ChainMap对象
1
2
3
4
5
6
7
ChainMap({'a': 1, 'b': 4, 'c': 0}, {'c': 2, 'd': 8})





ChainMap({}, {'a': 1, 'b': 4, 'c': 0}, {'c': 2, 'd': 8})
  • Counter
  • Counter用于统计一个可迭代对象中不同元素出现的频次。Counter的操作方法类似于字典,还包含了一些有用的属性或方法。常用的有:

    • elements方法:返回所有元素组成的列表,重复元素会出现多次
    • most_common方法:返回出现频最多的元素及频次的元组组成的列表
1
2
3
4
5
from collections import Counter
s = 'The quick brown fox jumps over a lazy dog.'
c = Counter(s)
print(c)
c.most_common()
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
Counter({' ': 8, 'o': 4, 'e': 2, 'u': 2, 'r': 2, 'a': 2, 'T': 1, 'h': 1, 'q': 1, 'i': 1, 'c': 1, 'k': 1, 'b': 1, 'w': 1, 'n': 1, 'f': 1, 'x': 1, 'j': 1, 'm': 1, 'p': 1, 's': 1, 'v': 1, 'l': 1, 'z': 1, 'y': 1, 'd': 1, 'g': 1, '.': 1})





[(' ', 8),
 ('o', 4),
 ('e', 2),
 ('u', 2),
 ('r', 2),
 ('a', 2),
 ('T', 1),
 ('h', 1),
 ('q', 1),
 ('i', 1),
 ('c', 1),
 ('k', 1),
 ('b', 1),
 ('w', 1),
 ('n', 1),
 ('f', 1),
 ('x', 1),
 ('j', 1),
 ('m', 1),
 ('p', 1),
 ('s', 1),
 ('v', 1),
 ('l', 1),
 ('z', 1),
 ('y', 1),
 ('d', 1),
 ('g', 1),
 ('.', 1)]
  • deque
  • deque是一种双向队列,即可以从任意一端添加或删除元素。deque可以基于一个可迭代对象创建,并指定队列的长度。常用的方法有:
    • append:在右端添加元素
    • appendleft:在左端添加元素
    • pop:弹出右端元素
    • popleft:弹出左端元素
    • extend:从右端扩展元素
    • extendleft:从左端扩展元素
    • clear:清空队列
    • reverse:返转队列元素顺序
    • rotate:移动全部队列元素,参数大于0向右移动,参数小于0向左移动
    • count:统计队列中元素的频次

3.8.1 array.array

  • array模型中的array类型的使用方法与list非常相似,区别在于其中的元素必须具有相同的数据类型。这与静态语言中的数组类似。在创建array.array对象时,必须利用typecode参数指明要存储的元素的类型。array.array支持的数据类型见下表
1
2
3
4
import array
int_numbs = array.array('i',[1, 4, 2, 8, 5, 7])
print(int_numbs)
print(int_numbs.typecode)
1
2
array('i', [1, 4, 2, 8, 5, 7])
i
类型码 C类型 Python类型 最小字节数
'b' signed char int 1
'B' unsigned char int 1
'u' Py_UNICODE Unicode character 2
'h' signed short int 2
'H' unsigned short int 2
'i' signed int int 2
'I' unsigned int int 2
'l' signed long int 4
'L' unsigned long int 4
'q' signed long long int 8
'Q' unsigned long long int 8
'f' float float 4
'd' double float 8

3.8.3 其他有用的数据结构

  • 除了前文介绍的数据结构之外,Python还内置了很多更加复杂的数据结构或更多的实现类型,参见下表
模块 特征描述
heapq 堆(heap)或树形数据结构
bisect 有序列表的高效操作(插入、删除)
weakref 帮助创建Python引用,但不会阻止对象的销毁操作
queue 提供了多种队列的实现类型