数据容器(数据结构)
前面我们介绍了 Python 最底层的基本数据类型:布尔型、整型、浮点型以及字符串型。
本章将要提到的 数据结构(容器) 。在这一章中,我们会把之前所学的基本 Python 类型以更为复杂的方式组织起来。这些数据结构以后会经常用到。在编程中,最常见的工作就是将数据进行拆分或合并,将其加工为特定的形式
大多数编程语言都有特定的数据结构来存储由一系列元素组成的序列,这些元素以它们所处的位置为索引:从第一个到最后一个依次编号。前一章已经见过 Python 字符串了,它本质上是字符组成的序列。
本文内容
- 列表
- 元组
- 字典
- 集合
知识点回顾
- Python 中数据类型可以分为 数字型 和 非数字型
- 数字型
- 整型 (
int
) - 浮点型(
float
) - 布尔型(
bool
)- 真
True
非 0 数
—— 非零即真 - 假
False
0
- 真
- 整型 (
- 非数字型
- 字符串
- 在
Python
中,所有 非数字型变量 都支持以下特点:- 都是一个 序列
sequence
,也可以理解为 容器 - 取值
[]
- 遍历
for in
- 链接
+
和 重复*
- 切片
- 都是一个 序列
列表(list)
列表是最常见的一种数据形式,是一种 有序 的 序列 ,可以随时添加和删除其中的元素。
列表非常适合利用顺序和位置定位某一元素,尤其是当元素的顺序或内容经常发生改变时。与字符串不同,列表是可变的。你可以直接对原始列表进行修改:添加新元素、删除或覆盖已有元素。
列表创建
List
(列表) 是Python
中使用 最频繁 的数据类型,在其他语言中通常叫做 数组- 专门用于存储 一串 信息
- 列表用
[]
定义,数据 之间使用,
分隔 - 列表的 索引 从
0
开始- 索引 就是数据在 列表 中的位置编号,索引 又可以被称为 下标
注意:从列表中取值时,如果 超出索引范围,程序会报错
# 用 list 创建空列表
array = list()
array
# 用 [] 创建空列表
array2 = []
array2
创建一个列表
array3 = [1, 2, 3, 4, 5, 6, 7]
array3
# 列表中可以存放多种数据
array4 = [1, 2, 3, True, False, int, "str", array]
array
类型转化
# 使用list()将其他数据类型转换成列表
s = 'hello world !'
list(s)
['h', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd', ' ', '!']
列表取值
使用[offset]获取与修改元素
# 直接获取
s[0]
s[-1]
根据索引位置修改内容
array3[0] = 5
列表切片
切片是返回一个新的内容
Python中符合序列的有序序列都支持切片(slice),例如列表,字符串,元组。
格式:[start:stop:step]
[起始值:结束值:步长]
- start: 起始索引,从0开始,-1表示结束
- stop:结束索引
- step:步长,end-start,步长为正时,从左向右取值。步长为负时,反向取值
arr = list(range(10))
# 指定区间切片
arr[0:5]
# 从头开始切片
arr[0:5]
# 切片到末尾
arr[0:]
# 省略参数切全部内容
arr[:]
# 逆序切片
arr[-12:-7]
# 指定步长切片
arr[0:5:1]
arr[0:5:2]
列表常用操作
- 在
ipython
中定义一个 列表,例如:l= list()
- 输入
l.
按下TAB
键,ipython
会提示 列表 能够使用的函数如下:
append() count() insert() reverse()
clear() extend() pop() sort()
copy() index() remove()
在变量后面输入
.
,然后选择针对这个变量要执行的操作,记忆起来比函数要简单很多
分类 | 关键字 / 函数 / 方法 | 说明 |
---|---|---|
增加 | append() | 添加元素至尾部 |
insert() | 在指定位置插入数据 | |
删除 | clear() | 清空列表 |
pop() | 默认弹出末尾数据 | |
pop(index) | 弹出指定索引数据 | |
remove(data) | 移除指定数据 | |
修改 | extend(列表2) | 将列表2 的数据追加到列表 |
查询 | count(数据) | 统计数据出现的次数 |
index(内容) | 查询内容所在位置 | |
其他 | copy() | 将列表复制一份 |
sort() | 排序 | |
reverse() | 逆序列表 |
案例:
In [7]: arr = list(range(1, 5))
# 添加元素到末尾
In [8]: arr.append(5)
In [9]: arr
Out[9]: [1, 2, 3, 4, 5]
# 插入元素到第一个
In [10]: arr.insert(0, 0)
In [11]: arr
Out[11]: [0, 1, 2, 3, 4, 5]
# 默认弹出最后一个元素
In [12]: arr.pop()
Out[12]: 5
# 指定弹出第一个元素
In [13]: arr.pop(0)
Out[13]: 0
In [14]: arr
Out[14]: [1, 2, 3, 4]
# 指定删除内容为 4 的元素
In [15]: arr.remove(4)
In [16]: arr
Out[16]: [1, 2, 3]
# 合并[4, 5, 6]列表
In [17]: arr.extend([4,5,6])
In [18]: arr
Out[18]: [1, 2, 3, 4, 5, 6]
# 查询内容为 4 的元素在第几个位置
In [19]: arr.index(4)
Out[19]: 3
# 排序后将内容输出(默认为升序)
In [20]: arr.sort(reverse=True)
In [21]: arr
Out[21]: [6, 5, 4, 3, 2, 1]
# 排序后将内容输出
In [22]: arr.sort()
In [23]: arr
Out[23]: [1, 2, 3, 4, 5, 6]
其他用方法
- 使用in判断值是否存在
- 使用 += 合并列表
- 使用
len()
获取长度 - 使用join()转换为字符串
In [24]: 2 in arr
Out[24]: True
In [25]: arr + [7,8,9]
Out[25]: [1, 2, 3, 4, 5, 6, 7, 8, 9]
In [26]: arr
Out[26]: [1, 2, 3, 4, 5, 6]
In [27]: len(arr)
Out[27]: 6
案例:
l = ['a', 'b', 1, 2, 3, 'c', 'd']
"""
打印将列表前两位与后两位删除的列表
打印将列表第三到第六位之间删除内容
"""
列表推导式(简单介绍)
推导式comprehensions(又称解析式),是Python的一种独有特性。推导式是可以从一个数据序列构建另一个新的数据序列的结构体。
x = []
for i in range(1,11):
x.append(i)
[x for x in range(1,11)]
对象引用、浅拷贝、深拷贝(拓展、难点、重点)
使用=赋值(对象引用)
>>> a = [1, 2, 3]
>>> a
[1, 2, 3]
>>> b = a
>>> b
[1, 2, 3]
>>> a[0] = 'surprise'
>>> a
['surprise', 2, 3]
>>> b
['surprise', 2, 3]
>>> b[0] = 'I hate surprises'
>>> b
['I hate surprises', 2, 3]
>>> a
['I hate surprises', 2, 3]
浅拷贝(copy)
>>> a = [1, 2, 3]
>>> b = a.copy()
>>> c = list(a)
>>> d = a[:]
>>> a[0] = 'integer lists are boring'
>>> a
['integer lists are boring', 2, 3]
>>> b
[1, 2, 3]
>>> c
[1, 2, 3]
>>> d
[1, 2, 3]
深拷贝(deepcopy)
>>> import copy
>>> a = [1, 2, 3, [1, 2, 3]]
>>> b = copy.copy(a)
>>> a[3][0] = "surprises"
>>> b
[1, 2, 3, ['surprises', 2, 3]]
>>> c = copy.deepcopy(b)
>>> b[3][0] = "i hate surprises"
>>> c
[1, 2, 3, ['surprises', 2, 3]]
>>> b
[1, 2, 3, ['i hate surprises', 2, 3]]
总结:
copy.copy 浅拷贝 只拷贝父对象,不会拷贝对象的内部的子对象。
copy.deepcopy 深拷贝 拷贝对象及其子对象
元组
创建元组
- 元组和列表类似,但属于不可变序列,元组一旦创建,用任何方法都不可以修改其元素。
- 元组的定义方式和列表相同,但定义时所有元素是放在一对圆括号“()”中,而不是方括号中。
- 元组没有列表中那么多方法可以使用,因为不可变,所以安全,速度比列表快。
# 使用 tuple() 创建元组
>>>tuple()
()
# 使用 () 创建元组
>>> ()
()
>>> type(())
tuple
>>> type(tuple())
tuple
元组中只包含一个元素时,需要在元素后面添加逗号
info_tuple = (50, )
元组取值与切片
- 元组的取值、切片与列表时一样使用
- 不能对元组的元素进行删除,但是可以删除整个元组:
In [1]: t = tuple("01234")
In [2]: t
Out[2]: ('0', '1', '2', '3', '4')
# 直接获取
In [3]: t[0]
Out[3]: '0'
In [4]: t[-1]
Out[4]: '4'
# 不能修改
In [5]: t[0] = 1
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-5-c8aeb8cd20ae> in <module>
----> 1 t[0] = 1
TypeError: 'tuple' object does not support item assignment
In [6]: t[0:5]
Out[6]: ('0', '1', '2', '3', '4')
元组常用操作
- 在
ipython
中定义一个 元组,例如:info = ()
- 输入
info.
按下TAB
键,ipython
会提示 元组 能够使用的函数如下:
info.count info.index
循环遍历
- 取值 就是从 元组 中获取存储在指定位置的数据
- 遍历 就是 从头到尾 依次 从 元组 中获取数据
# for 循环内部使用的变量 in 元组
for item in info:
# 循环内部针对元组元素进行操作
print(item)
- 在
Python
中,可以使用for
循环遍历所有非数字型类型的变量:列表、元组、字典 以及 字符串
元组和列表之间的转换
- 使用
list
函数可以把元组转换成列表
list(元组)
- 使用
tuple
函数可以把列表转换成元组
tuple(列表)
元组解包
序列类型
In [7]: a, b, c = tuple('abc')
In [8]: a
Out[8]: 'a'
In [9]: b
Out[9]: 'b'
In [10]: c
Out[10]: 'c'
# 用 _ 收集不用的元组
In [15]: _, _, c = tuple('abc')
In [16]: c
Out[16]: 'c'
_
是被舍弃的变量
元组与列表的区别
- 元组一旦定义就不允许更改。
- 元组没有
append()
、extend()
和insert()
等方法,无法向元组中添加元素。 - 元组没有
remove()
或pop()
方法,也无法对元组元素进行del
操作,不能从元组中删除元素。 - 从效果上看,
tuple( )
冻结列表,而list( )
融化元组。
元组的优点
- 元组的速度比列表更快。如果定义了一系列常量值,而所需做的仅是对它进行遍历,那么一般使用元组而不用列表。
- 元组对不需要改变的数据进行 “写保护” 将使得代码更加安全。
- 元组可用作字典的“键”,也可以作为集合的元素。列表永远不能当做字典键使用,也不能作为集合的元素,因为列表不是不可变的。
元组的缺点
不可修改
集合(set)
集合是无序、可变序列,使用一对大括号界定,元素不可重复,同一个集合中每个元素都是唯一的。
集合中只能包含数字、字符串、元组等不可变类型(或者说可哈希)的数据,而不能包含列表、字典、集合等可变类型的数据。
注意:创建一个空集合必须用 set() 而不是 { },因为 { } 是用来创建一个空字典。
集合的创建与删除
直接将集合赋值给变量
>>> a = {3, 5}
>>> a.add(7) #向集合中添加元素
>>> a
{3, 5, 7}
使用set将其他类型数据转换为集合
>>> a_set = set(range(8,14))
>>> a_set
{8, 9, 10, 11, 12, 13}
>>> b_set = set([0, 1, 2, 3, 0, 1, 2, 3, 7, 8]) #自动去除重复
>>> b_set
{0, 1, 2, 3, 7, 8}
>>> c_set = set() #空集合
>>> c_set
set()
集合运算
In [4]: a = {1, 2}
In [5]: b = {2, 3}
In [6]: a & b
Out[6]: {2}
In [7]: a.intersection(b)
Out[7]: {2}
In [8]: a | b
Out[8]: {1, 2, 3}
In [9]: a.union(b)
Out[9]: {1, 2, 3}
In [10]: a - b
Out[10]: {1}
In [11]: a.difference(b)
Out[11]: {1}
字典(dict)(无序)
字典(dictionary)是一种 key-value(键值对)
数据类型,且可存储任意类型对象。
字典的每个键值(key=>value)对用冒号(:)分割,每个对之间用逗号(,)分割,整个字典包括在花括号({})中
- 字典是无序、可变。
- 定义字典时,每个元素的键和值用冒号分隔,元素之间用逗号分隔,所有的元素放在一对大括号
“{}”
中。 - 字典中的 键可以为任意不可变数据 ,比如整数、实数、复数、字符串、元组等等。
字典的定义
- 字典用
{}
定义 - 字典使用 键值对 存储数据,键值对之间使用
,
分隔- 键
key
是索引 - 值
value
是数据 - 键 和 值 之间使用
:
分隔 - 键必须是唯一的
- 值 可以取任何数据类型,但 键 只能使用 字符串、数字 或 元组
- 键
字典的创建与修改
使用 {}
或者 dict()
创建空字典
In [1]: {}
Out[1]: {}
In [2]: dict()
Out[2]: {}
In [3]: type({})
Out[3]: dict
In [4]: type(dict())
Out[4]: dict
创建简单的字典
In [24]: d = {"name":"张三"}
In [25]: d
Out[25]: {'name': '张三'}
# 根据键名取值
In [26]: d['name']
Out[26]: '张三'
指定键名修改字典内容
In [34]: d['name'] = '李四'
In [35]: d
Out[35]: {'name': '李四'}
# 以键作为下标可以读取字典元素,若键不存在则抛出异常
In [14]: d['name1'] #键不存在,抛出异常
------------------------------------------
KeyError Traceback (most recent call last)
<ipython-input-17-688f57ecfd16> in <module>()
----> 1 d['name1']
KeyError: 'name1'
# 给不存在的键赋值添加内容
In [36]: d['name1'] = '张三'
In [37]: d
Out[37]: {'name': '李四', 'name1': '张三'}
创建复杂字典
字典与列表类似,可以存放任意类型的值。字典中每个元素拥有与之对应的互不相同的键(key),需要通过键来访问元素。
键通常是字符串,但它还可以是 Python 中其他任意的不可变类型:布尔型、整型、浮点型、元组、字符串,以及其他一些在后面的内容中会见到的类型。字典是可变的,因此你可以增加、删除或修改其中的键值对。
键不可以重复。
In [7]: d = {'str':'马克',
...: 'int':18,
...: 'bool':True,
...: 'list':[1, 2, 3],
...: 'tuple':(1, 2, 3),
...: 'set':{1, 2, 3},
...: 'dict':{
...: 'str':'马克',
...: 'int':18
...: }
...: }
In [8]: d
Out[8]:
{'str': '马克',
'int': 18,
'bool': True,
'list': [1, 2, 3],
'tuple': (1, 2, 3),
'set': {1, 2, 3},
'dict': {'str': '马克', 'int': 18}}
课后可以做一个练习:自己定义一个字典数据格式
字典元素添加与修改
- 当以指定键为下标为字典赋值时:
- 若键存在,则可以修改该键的值;
- 若不存在,则表示添加一个键、值对。
In [15]: d["new"] = 'new' # 添加内容
In [16]: d
Out[16]:
{'str': '马克',
'int': 18,
'bool': True,
'list': [1, 2, 3],
'tuple': (1, 2, 3),
'set': {1, 2, 3},
'dict': {'str': '马克', 'int': 18},
'new': 'new'}
In [18]: d["new"] = '新元素'
In [19]: d
Out[19]:
{'str': '马克',
'int': 18,
'bool': True,
'list': [1, 2, 3],
'tuple': (1, 2, 3),
'set': {1, 2, 3},
'dict': {'str': '马克', 'int': 18},
'new': '新元素'}
字典常用方法
- 在
ipython
中定义一个 字典,例如:d= {}
- 输入
d.
按下TAB
键,ipython
会提示 字典 能够使用的函数如下:
In [1]: d.
clear() get() pop() update()
copy() items() popitem() values()
fromkeys() keys() setdefault()
方法名 | 作用 |
---|---|
get() | 根据 key 获取 value , 如果不存在,默认为空 |
keys() | 获取所有 key 的列表 |
values() | 获取所有 value 的列表 |
items() | 获取所有 (key, value) 的元组列表 |
update() | dict1.update(dict2) 将字典2合并到字典1 |
copy() | 复制一个字典 |
pop() | dict1.pop(key) 指定 key 弹出 value |
popitem() | 随机弹出一对键值对 |
setdefault() | dict1.setdefault(key, value) 。key 存在不修改数据。不存在新建键值对。 |
clear() | 清空字典 |
del | 使用del 删除字典中指定键的元素 |
使用 get()
根据键名获取值
使用字典对象的get
方法获取指定键对应的值,并且可以在键不存在的时候返回指定值。
In [39]: d
Out[39]:
{'str': '马克',
'int': 18,
'bool': True,
'list': [1, 2, 3],
'tuple': (1, 2, 3),
'set': {1, 2, 3}}
In [40]: d.get('str1', "没有str1这个内容")
Out[40]: '没有str1这个内容'
使用 items()
方法获取字典的键、值对
In [41]: d.items()
Out[41]: dict_items([('str', '马克'), ('int', 18), ('bool', True), ('list', [1, 2, 3]), ('tuple', (1, 2, 3)), ('set', {1, 2, 3})])
使用 keys()
方法获取字典的键
In [42]: d.keys()
Out[42]: dict_keys(['str', 'int', 'bool', 'list', 'tuple', 'set'])
使用 values()
方法获取字典的值
In [43]: d.values()
Out[43]: dict_values(['马克', 18, True, [1, 2, 3], (1, 2, 3), {1, 2, 3}])
字典可以用来 存储多个数据
- 通常用于存储 描述一个
物体
的相关信息
和列表的区别
- 列表 是 有序 的对象集合
- 字典 是 无序 的对象集合
循环遍历
- 遍历 就是 依次 从 字典 中获取所有键值对
# for 循环内部使用的 `key 的变量` in 字典
for k in d:
print("%s: %s" % (k, xiaoming[k]))
提示:在实际开发中,由于字典中每一个键值对保存数据的类型是不同的,所以针对字典的循环遍历需求并不是很多
应用场景
- 尽管可以使用
for in
遍历 字典 - 但是在开发中,更多的应用场景是:
- 使用 多个键值对,存储 描述一个
物体
的相关信息 —— 描述更复杂的数据信息 - 将 多个字典 放在 一个列表 中,再进行遍历,在循环体内部针对每一个字典进行 相同的处理
- 使用 多个键值对,存储 描述一个
card_list = [{"name": "张三",
"qq": "12345",
"phone": "110"},
{"name": "李四",
"qq": "54321",
"phone": "10086"}
]
案例:统计字符串中每个字母出现的次数
d["name"] = 1
worlds = "this is a python and Python"
# 先创建一个空字典用于收集数据
worlds_dict = {}
for world in worlds:
# 如果单词已经出现在字典中就 +1
if world in worlds_dict:
worlds_dict[world] = worlds_dict[world] + 1
else:
# 如果不存在字典中 就设置为1
worlds_dict[world] = 1
print(worlds_dict.items())
for k,v in worlds_dict.items():
print('单词:{} 出现了 {} 次'.format(k, v))
升级:
- 不区分大小写统计
- 统计之后排序输出
案例升级:
# -*- coding: utf-8 -*-
# https://docs.python.org/3/library/stdtypes.html#iterator-types
with open('iterator.txt', encoding='utf-8') as f:
w = f.read()
worlds = w.split()
# 先创建一个空字典用于收集数据
worlds_dict = {}
for world in worlds:
# 如果单词已经出现在字典中就 +1
if world in worlds_dict:
worlds_dict[world] = worlds_dict[world] + 1
else:
# 如果不存在字典中 就设置为1
worlds_dict[world] = 1
worlds_dict = worlds_dict.items()
worlds_dict = sorted(worlds_dict, key=lambda x:x[1],reverse=True)
print(worlds_dict)
数据类型转化
int、float、str可以相互转化
In [1]: str(1)
Out[1]: '1'
In [2]: int('1')
Out[2]: 1
In [3]: float('1')
Out[3]: 1.0
# int关键字不能转为数字的字符串
In [4]: int("s")
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
<ipython-input-4-61f7ab14298a> in <module>()
----> 1 int("s")
ValueError: invalid literal for int() with base 10: 's'
str、list、tuple、set可以相互转化
# 字符串与列相互转化需要使用高级方法(显示转化)
In [6]: '1,2,3,4,5'.split(',')
Out[6]: ['1', '2', '3', '4', '5']
In [7]: ','.join(['1', '2', '3', '4', '5'])
Out[7]: '1,2,3,4,5'
# 数字类型不能直接转化为字符串
In [8]: ','.join([1, 2, 3, 4, 5])
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-8-16dd7015be3c> in <module>()
----> 1 ','.join([1, 2, 3, 4, 5])
TypeError: sequence item 0: expected str instance, int found
# list、tuple、set之间可以相互进行转化
In [9]: l = [1, 2, 3, 4, 5]
In [10]: tuple(l)
Out[10]: (1, 2, 3, 4, 5)
In [11]: set(l)
Out[11]: {1, 2, 3, 4, 5}
以下几个内置的函数可以执行数据类型之间的转换。这些函数返回一个新的对象,表示转换的值。Python Number 类型转换
关键字 | 说明 |
---|---|
int(x [,base ]) | 将 x 转换为一个整数 |
float(x ) | 将 x 转换到一个浮点数 |
str(x ) | 将对象 x 转换为字符串 |
tuple(s ) | 将序列 s 转换为一个元组 |
list(s ) | 将序列 s 转换为一个列表 |
set(s ) | 将序列 s 转换为一个集合 |
序列解包
可以使用序列解包功能对多个变量同时赋值
>>> x, y, z = 1, 2, 3 #多个变量同时赋值
>>> t = (False, 3.5, 'exp')
>>> (x, y, z) = t
>>> x, y, z = t
>>> x, y, z = range(3) #可以对range对象进行序列解包
>>> a, b = b, a #交换两个变量的值
>>> a, b, c = 'ABC' #字符串也支持序列解包
>>> x = [1, 2, 3, 4, 5, 6]
>>> x[:3] = map(str, range(5)) #切片也支持序列解包
>>> x
['0', '1', '2', '3', '4', 4, 5, 6]
- 序列解包对于列表和字典同样有效
>>> s = {'a':1, 'b':2, 'c':3}
>>> b, c, d = s.items()
>>> b
('c', 3)
>>> b, c, d = s #使用字典时不用太多考虑元素的顺序
>>> b
'c'
>>> b, c, d = s.values()
>>> print(b, c, d)
1 3 2
- 序列解包遍历多个序列
>>> keys = ['a', 'b', 'c', 'd']
>>> values = [1, 2, 3, 4]
>>> for k, v in zip(keys, values):
print((k, v), end=' ')
('a', 1) ('b', 2) ('c', 3) ('d', 4)
- Python 3.5还支持下面用法的序列解包
>>> print(*[1, 2, 3], 4, *(5, 6))
1 2 3 4 5 6
>>> *range(4),4
(0, 1, 2, 3, 4)
>>> [*range(10)]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> {'x': 1, **{'y': 2}}
{'y': 2, 'x': 1}