Python学习笔记
文章目录
课程笔记参考B站视频: Python全栈开发教程。
第八站 是排还是散
本章介绍元组和集合。
1. 什么是元组
元组是Python内置的数据结构之一,是一个不可变序列。下面是不可变序列与可变序列的区别:
分类 | 数据结构 | 特点 |
---|---|---|
不可变序列 | 字符串、元组 | 没有增、删,改的操作 |
可变序列 | 列表、字典、集合 | 可以对序列执行增、删、改操作,对象地址不发生更改 |
下面是代码示例:
'''
演示不可变序列与可变序列的区别
'''
print('-------展示不可变序列------')
str = 'hello'
print(id(str), str)
str += 'world'
print(id(str), str)
print('-------展示可变序列-------')
lst = [10,20,45]
print(id(lst), lst)
lst.append(50)
print(id(lst), lst)
- 运行结果
-------展示不可变序列------
2313388674032 hello
2313388674480 helloworld
-------展示可变序列-------
2313388293120 [10, 20, 45]
2313388293120 [10, 20, 45, 50]
根据内存地址的变化可以看出,一旦修改了不可变序列的元素,其内存地址就会发生变化。
2. 元组的创建方式
元组使用小括号定义(列表使用方括号定义),元组内的元素对象也是使用逗号分隔开。元组的创建方式主要有两种:
- 直接使用小括号。
- 使用内置函数
tuple()
。
下面是代码示例:
'''
元组的创建方式
'''
print('---------使用小括号--------')
tup1 = ('Python', 'hello', 100)
print(type(tup1), id(tup1), tup1)
tup2 = 'Python', 'hello', 100 # 可以省略小括号
print(type(tup2), id(tup2), tup2)
tup2 = ('python',) # 若只有一个元素,要加上小括号和逗号
print(type(tup2), id(tup2), tup2)
print('---------使用tuple()------')
tup3 = tuple(('为了荣耀!', 'AI', 999.99))
print(type(tup3), id(tup3), tup3)
print('---------创建空元组--------')
tup4 = ()
print(type(tup4), id(tup4), tup4)
tup5 = tuple()
print(type(tup5), id(tup5), tup5)
- 运行结果
---------使用小括号--------
<class 'tuple'> 2337434224704 ('Python', 'hello', 100)
<class 'tuple'> 2337434224704 ('Python', 'hello', 100)
<class 'tuple'> 2337433810016 ('python',)
---------使用tuple()------
<class 'tuple'> 2337433843904 ('为了荣耀!', 'AI', 999.99)
---------创建空元组--------
<class 'tuple'> 2337428537408 ()
<class 'tuple'> 2337428537408 ()
一定要注意,若元组只有一个元素,不管采用什么方式,都要加上小括号和逗号。
有意思的是,由于元组是不可变序列,所以只要是元组的对象元素完全相同,那么就会指向相同的内存地址。
问题探讨:为什么要将元组设计成不可变序列?
因为不可变序列一旦创建成功,就不允许再进行增删改等操作。这样在多任务环境下,同时操作对象时不需要加锁。因此,在程序中尽量使用不可变序列。
注意事项:元组中存储的是对象的引用,需要注意:
a) 如果元组中对象本身不可变对象,则不能再引用其它对象。
b) 如果元组中的对象是可变对象,则可变对象的引用不允许改变,但数据可以改变。
即:顶层的引用逻辑禁止发生变化,下面的其他层可以变。
下面是代码示例:
tup1 = ('hello', [10,20], 963)
print(id(tup1), type(tup1),tup1)
print(id(tup1[0]), type(tup1[0]),tup1[0])
print(id(tup1[1]), type(tup1[1]),tup1[1])
print(id(tup1[2]), type(tup1[2]),tup1[2])
print('----------更改不可变对象-----------')
# tup1[0] = 20 # 报错:TypeError: 'tuple' object does not support item assignment
print(id(tup1[0]), type(tup1[0]),tup1[0])
print('----------更改可变对象-------------')
tup1[1][1] = 30 # 修改
print(id(tup1[1]), type(tup1[1]),tup1[1])
tup1[1].append('world') # 增加
print(id(tup1[1]), type(tup1[1]),tup1[1])
- 运行结果
2302301489024 <class 'tuple'> ('hello', [10, 20], 963)
2302301489968 <class 'str'> hello
2302301109248 <class 'list'> [10, 20]
2302301206512 <class 'int'> 963
----------更改不可变对象-----------
2302301489968 <class 'str'> hello
----------更改可变对象-------------
2302301109248 <class 'list'> [10, 30]
2302301109248 <class 'list'> [10, 30, 'world']
3. 元组的遍历
元组是可迭代对象,所以元组的遍历有两种方法:
- 直接使用for…in进行遍历。
- 在保证不越界的情况下,使用索引遍历。
下面是代码示例:
tup1 = ('hello', [10, 20], 963)
print('---------直接使用for循环遍历---------')
for i in tup1:
print(type(i), i)
print('---------使用for循环遍历索引---------')
for i in range(0,len(tup1),1):
print(type(tup1[i]), tup1[i])
- 运行结果
---------直接使用for循环遍历---------
<class 'str'> hello
<class 'list'> [10, 20]
<class 'int'> 963
---------使用for循环遍历索引---------
<class 'str'> hello
<class 'list'> [10, 20]
<class 'int'> 963
4. 什么是集合
集合也是Python语言提供的内置数据结构,与列表、字典一样都属于可变类型的序列。集合就是没有value的字典。 下面是集合的内存示意图,可以看出集合也是无序的。
![](https://i-blog.csdnimg.cn/blog_migrate/e641c6557e3d088f3830cf5a7fc2c0f6.png)
5. 集合的创建
集合的创建同样也有两种方式:
- 直接使用 { } 。
- 使用内置函数
set()
。
下面是代码示例:
print('----------使用花括号创建集合--------')
se1 = {'hello', 20.3, 56, 56} # 集合元素不允许重复
print(id(se1), type(se1), se1)
print('----------使用set()创建集合--------')
se2 = set(range(6)) # 可迭代对象
print(id(se2), type(se2), se2)
se3 = set(range(6))
print(id(se3), type(se3), se3)
# 可以将迭代器对象、字符串、列表、元组、字典、集合都转化成集合的形式
se3 = set('hello') # 字符串
print('字符串:', id(se3), type(se3), se3)
se3 = set(['hello', 20.3, 56]) # 列表
print('列表:', id(se3), type(se3), se3)
se3 = set(('hello', 20.3, 56)) # 元组
print('元组:', id(se3), type(se3), se3)
se3 = set({'hello':'world', 20.3:20, 56:56.9}) # 字典
print('字典:', id(se3), type(se3), se3)
se3 = set({'hello', 20.3, 56}) # 集合
print('集合:', id(se3), type(se3), se3)
print('----------元素不能为可变对象--------')
# se3 = set(['hello', 20.3, 56, [10,20]]) # TypeError: unhashable type: 'list'
# print('列表:', id(se3), type(se3), se3)
print('----------定义空集合---------------')
se4 = {}
print(id(se4), type(se4), se4)
se4 = set() # 只能用set定义空集合
print(id(se4), type(se4), se4)
- 运行结果
----------使用花括号创建集合--------
2497148610144 <class 'set'> {56, 20.3, 'hello'}
----------使用set()创建集合--------
2497148715744 <class 'set'> {0, 1, 2, 3, 4, 5}
2497148716640 <class 'set'> {0, 1, 2, 3, 4, 5}
字符串: 2497148716416 <class 'set'> {'h', 'o', 'e', 'l'}
列表: 2497148716640 <class 'set'> {56, 20.3, 'hello'}
元组: 2497148716416 <class 'set'> {56, 20.3, 'hello'}
字典: 2497148716640 <class 'set'> {56, 20.3, 'hello'}
集合: 2497148716864 <class 'set'> {56, 20.3, 'hello'}
----------元素不能为可变对象--------
----------定义空集合---------------
2497148298624 <class 'dict'> {}
2497148716640 <class 'set'> set()
通过上述代码也可以总结一下集合的特点(参考字典的特点):
- 集合中的所有元素不允许重复,否则会自动删除。
- 集合中的元素是无序的。
- 集合中的元素必须是不可变对象(比如列表就不可以)。
- 集合也可以根据需要动态地伸缩。
- 集合会浪费较大的内存,也是一种使用空间换时间的数据结构。
- 空集合的定义只能使用内置函数
set()
。
6. 集合的增、删、查操作
分类 | 集合方法 | 操作描述 |
---|---|---|
判断 | in 或 not in | 判断集合中是否包含某一元素 |
增加 | .add() | 一次添中一个元素 |
.update() | 一次至少添加一个元素 | |
删除 | .remove() | 一次删除一个指定元素,如果指定的元素不存在抛出KeyError |
.discard() | 一次删除一个指定元素,如果指定的元素不存在不抛异常 | |
.pop() | 删除集合的第一个(地址最小的)元素 | |
.clear() | 清空集合所有元素 | |
del(内置函数) | 删除字典中一个键值对,必须添加索引 |
注意集合的判断操作就相当于对集合的元素进行了访问,而增加和删除操作就相当于对于集合的元素做出了修改。
下面是代码示例:
'''
展示集合的相关操作
'''
print('-----------集合的判断操作----------')
se1 = set(range(9))
print(id(se1), type(se1), se1)
print('9在集合中:', 9 in se1)
print('10不在集合中:', 10 not in se1)
print('-----------集合的增加操作----------')
se1.add(56.3) # 若添加已有元素,什么也不会发生
print(id(se1), type(se1), se1)
se1.update(('hello', 100.3)) # 创建集合的所有类型都支持
print(id(se1), type(se1), se1)
print('-----------集合的删除操作----------')
se1.remove(56.3)
print(id(se1), type(se1), se1)
# se1.remove('world') # 删除不存在的元素:KeyError: 'world'
se1.discard(4)
print(id(se1), type(se1), se1)
se1.discard('world') # 删除不存在的元素,不会报错
print(id(se1), type(se1), se1)
se1.pop()
print(id(se1), type(se1), se1)
se1.clear()
print(id(se1), type(se1), se1)
del se1 # 删除这个集合对象
- 运行结果
-----------集合的判断操作----------
2092296431200 <class 'set'> {0, 1, 2, 3, 4, 5, 6, 7, 8}
9在集合中: False
10不在集合中: True
-----------集合的增加操作----------
2092296431200 <class 'set'> {0, 1, 2, 3, 4, 5, 6, 7, 8, 56.3}
2092296431200 <class 'set'> {0, 1, 2, 3, 4, 5, 6, 7, 8, 100.3, 'hello', 56.3}
-----------集合的删除操作----------
2092296431200 <class 'set'> {0, 1, 2, 3, 4, 5, 6, 7, 8, 100.3, 'hello'}
2092296431200 <class 'set'> {0, 1, 2, 3, 5, 6, 7, 8, 100.3, 'hello'}
2092296431200 <class 'set'> {0, 1, 2, 3, 5, 6, 7, 8, 100.3, 'hello'}
2092296431200 <class 'set'> {1, 2, 3, 5, 6, 7, 8, 100.3, 'hello'}
2092296431200 <class 'set'> set()
7. 集合的关系与数学操作
关系 | 集合方法 | 操作描述 |
---|---|---|
相等 | == 或 != | 判断两个集合是否相等 |
子集 | .issubset() | 判断一个集合是否是另一个集合的子集 |
超集 | .issuperset() | 判断一个集合是否是另一个集合的超集 |
交集 | .isdisjoint() | 判断两个集合是否没有交集 |
下面是代码示例:
print('------------判断是否相等----------')
se1 = set(range(6))
se2 = set(range(5,-1,-1))
print('se1:', se1)
print('se2:', se2)
print('是否相等?', se1 == se2) # 判断相等与定义是的顺序无关
print('---------判断是否子集、超集--------')
se1.pop()
print('se1:', se1)
print('se2:', se2)
print('se1是se2的子集?', se1.issubset(se2))
print('se1是se2的超集?', se1.issuperset(se2))
print('------------判断是否有交集---------')
print('se1:', se1)
print('se2:', se2)
print('两者没有交集吗?', se1.isdisjoint(se2))
- 运行结果
------------判断是否相等----------
se1: {0, 1, 2, 3, 4, 5}
se2: {0, 1, 2, 3, 4, 5}
是否相等? True
---------判断是否子集、超集--------
se1: {1, 2, 3, 4, 5}
se2: {0, 1, 2, 3, 4, 5}
se1是se2的子集? True
se1是se2的超集? False
------------判断是否有交集---------
se1: {1, 2, 3, 4, 5}
se2: {0, 1, 2, 3, 4, 5}
两者没有交集吗? False
基于上述集合关系的基础上,便有如下集合的数学操作:
描述 | 集合方法&数学运算符 | 操作描述 |
---|---|---|
求交集 | .intersection() 或 & | 求两个集合的交集 |
求并集 | .union() 或 | | 合并两个集合的所有元素 |
求差集 | A.difference(B) 或 A-B | 集合A减去集合B |
求对称差集 | .symmetric_difference() 或 ^ | 两个集合的并集 减去 他们的交集 |
下面是代码示例:
'''
演示集合的数学操作
'''
se1 = set(range(6))
se2 = set(range(3,-3,-1))
print('-----------求交集-----------')
print('se1: ', id(se1), se1)
print('se2: ', id(se2), se2)
se3 = se1.intersection(se2)
print('交集:', id(se3), se3)
se3 = se1 & se2
print('交集:', id(se3), se3)
print('-----------求并集-----------')
print('se1: ', id(se1), se1)
print('se2: ', id(se2), se2)
se4 = se1.union(se2)
print('并集:', id(se4), se4)
se4 = se1 | se2
print('并集:', id(se4), se4)
print('-----------求差集-----------')
print('se1: ', id(se1), se1)
print('se2: ', id(se2), se2)
se5 = se1.difference(se2)
print('差集se1-se2:', id(se5), se5)
se5 = se1 - se2
print('差集se1-se2:', id(se5), se5)
se5 = se2.difference(se1)
print('差集se2-se1:', id(se5), se5)
se5 = se2 - se1
print('差集se2-se1:', id(se5), se5)
print('---------求对称差集---------')
print('se1: ', id(se1), se1)
print('se2: ', id(se2), se2)
se6 = se1.symmetric_difference(se2)
print('对称差集se1-se2:', id(se6), se6)
se6 = se1 ^ se2
print('对称差集se1-se2:', id(se6), se6)
se6 = se2.symmetric_difference(se1)
print('对称差集se2-se1:', id(se6), se6)
se6 = se2 ^ se1
print('对称差集se2-se1:', id(se6), se6)
print('---同一个方法产生的新集合地址相同---')
se2.add(100)
se7 = se1.symmetric_difference(se2)
print('对称差集se1-se2:', id(se7), se7)
se7 = se1 ^ se2
print('对称差集se1-se2:', id(se7), se7)
se7 = se2.symmetric_difference(se1)
print('对称差集se2-se1:', id(se7), se7)
se7 = se2 ^ se1
print('对称差集se2-se1:', id(se7), se7)
- 运行结果
-----------求交集-----------
se1: 1738975405664 {0, 1, 2, 3, 4, 5}
se2: 1738975511264 {0, 1, 2, 3, -2, -1}
交集: 1738975512160 {0, 1, 2, 3}
交集: 1738975511936 {0, 1, 2, 3}
-----------求并集-----------
se1: 1738975405664 {0, 1, 2, 3, 4, 5}
se2: 1738975511264 {0, 1, 2, 3, -2, -1}
并集: 1738975512160 {0, 1, 2, 3, 4, 5, -1, -2}
并集: 1738975512384 {0, 1, 2, 3, 4, 5, -1, -2}
-----------求差集-----------
se1: 1738975405664 {0, 1, 2, 3, 4, 5}
se2: 1738975511264 {0, 1, 2, 3, -2, -1}
差集se1-se2: 1738975512160 {4, 5}
差集se1-se2: 1738975511712 {4, 5}
差集se2-se1: 1738975512160 {-2, -1}
差集se2-se1: 1738975511712 {-2, -1}
---------求对称差集---------
se1: 1738975405664 {0, 1, 2, 3, 4, 5}
se2: 1738975511264 {0, 1, 2, 3, -2, -1}
对称差集se1-se2: 1738975512160 {4, 5, -1, -2}
对称差集se1-se2: 1738975510816 {4, 5, -1, -2}
对称差集se2-se1: 1738975512160 {4, 5, -1, -2}
对称差集se2-se1: 1738975510816 {4, 5, -1, -2}
---同一个方法产生的新集合地址相同---
对称差集se1-se2: 1738975512160 {100, 4, 5, -2, -1}
对称差集se1-se2: 1738975511488 {100, 4, 5, -2, -1}
对称差集se2-se1: 1738975512160 {4, 5, 100, -2, -1}
对称差集se2-se1: 1738975511488 {4, 5, 100, -2, -1}
注意到:
- 上述所有的数学操作都是产生了一个新的集合,并不会对原来的集合产生影响。
- 若集合元素没变,由同一个操作(包括方法和数学运算符)生成的新集合地址相同(哪怕颠倒集合的操作顺序)。
- 若集合元素改变,那么由方法生成的新集合地址不变,但由数学运算符生成的新地址集合会改变。
8. 集合生成式
集合生成式的定义介于列表生成式与字典生成式之间(三者之间的关系如下图),它的标准格式如下:
{ 集合元素表达式 for 自定义变量 in 可迭代对象}
下面是代码示例:
se1 = {i**3 for i in range(-3,4)}
print(type(se1), se1)
- 运行结果
<class 'set'> {0, 1, -27, 8, -8, 27, -1}
9. 元组、列表、字典、集合的总结
注:Python中没有元组生成式,因为元组是不可变序列。
性质 | 元组(tuple) | 列表(list) | 字典(dict) | 集合(set) |
---|---|---|---|---|
可变序列? | 不可变 | 可变 | 可变 | 可变 |
可重复? | 可重复 | 可重复 | 键不可重复 值可重复 | 不可重复 |
有序? | 有序 | 有序 | 无序 | 无序 |
定义符号 | 小括号 ( ) | 方括号 [ ] | 花括号 { key:value } | 花括号 { } |
对于 可变序列&不可变序列 来说,它们有一些通用的性质:
- 元素相同时,不可变序列会指向同一个内存,可变序列会开辟出不同的内存。
- 不可变序列的元素一旦修改,其指向的内存地址就会更改。但如果是对不可变序列中的可变对象进行增删改操作,其指向的内存地址不变,
- 不可变序列一旦创建成功,就不允许再进行增删改等操作。
- 有序序列都可以通过索引进行访问(正向索引和逆向索引),无序序列只能通过查找的方式访问。
10. 本章作业
1. 我的咖啡馆你做主
说明:
![](https://i-blog.csdnimg.cn/blog_migrate/98e84b269c3c645e0149eeddf61fa773.png)
下面是代码示例:
'''演示元组的使用'''
coffee_name = ('蓝山', '卡布奇诺', '拿铁', '皇家咖啡', '女五咖啡', '美丽与哀愁')
print('您好!欢迎光临小喵咖啡屋')
print('本店经营的咖啡有:')
for index,item in enumerate(coffee_name):
print(str(index+1) + '.' + item)
index= int(input('请输入您喜欢的咖啡编号:'))
if 0<=index<=len(coffee_name):
print(f'您的咖啡[{coffee_name[index-1]}]好了,请您慢用')
else:
print('您点的咖啡不存在,请下次光临')
- 运行结果
您好!欢迎光临小喵咖啡屋
本店经营的咖啡有:
1.蓝山
2.卡布奇诺
3.拿铁
4.皇家咖啡
5.女五咖啡
6.美丽与哀愁
请输入您喜欢的咖啡编号:1
您的咖啡[蓝山]好了,请您慢用
2. 显示2019中超联赛前5名排行
说明:
![](https://i-blog.csdnimg.cn/blog_migrate/5ebbd55183ee6f57067e33e0370a1da3.png)
下面是代码示例:
'''演示元组之中又包含一个元组'''
scores=(('广州恒大',72),('北京国安',70), ('上海上港',66), ('江苏苏宁',53), ('山东鲁能',51))
for index,item in enumerate (scores):
print(str(index+1) + '.', end=' ')
for i in item:
print(i, end=' ')
print()
- 运行结果
1. 广州恒大 72
2. 北京国安 70
3. 上海上港 66
4. 江苏苏宁 53
5. 山东鲁能 51
3. 模拟手机通信录
说明:
![](https://i-blog.csdnimg.cn/blog_migrate/7b605fdd5edce8a37fbaa61f6cb483f6.png)
下面是代码示例:
'''演示集合的无序'''
phones = set()
for i in range(5): # 必须输入5个人
info= input(f'请输入第{i+1}个朋友的姓名和手机号码:')
phones.add(info)
for item in phones:
print(item)
- 运行结果
请输入第1个朋友的姓名和手机号码:张三1111111
请输入第2个朋友的姓名和手机号码:李四2222222
请输入第3个朋友的姓名和手机号码:王五3333333
请输入第4个朋友的姓名和手机号码:陈六4444444
请输入第5个朋友的姓名和手机号码:麻七5555555
麻七5555555
张三1111111
李四2222222
王五3333333
陈六4444444