第一章 变量、常用循环体、代码结构、代码练习
第二章 列表、元组等数据结构、字符串驻留机制及字符串格式化操作
第三章 函数、面向对象、文件操作、深浅拷贝、模块、异常及捕获
第四章 项目打包、类和对象高级、序列、迭代器、生成器、装饰器
第五章 正则表达式、json、logging日志配置、数据库操作、枚举、闭包、匿名函数和高阶函数、time、datetime
第六章 Socket编程、多线程(创建方式、线程通信、线程锁、线程池)
列表、元组、字典、集合
列表
为什么需要列表?
变量可以存储一个元素,而列表是一个 大容器
可以存储N多个元素(不同数据类型),程序可以方便地对这些数据进行整体操作
列表有两种索引方式:如下图
内存示例
代码示例:
a = 10 # 变量存储的是一个对象的引用(标识)
lst = ['hello', 'world', 98] # 存储多个对象的引用(标识)
print(lst)
图示:(以下id标识自编的不真实)
一个list列表中,存储的是多个对象的引用标识(id)
列表的创建
- 使用中括号,[],括号中的元素使用
,
分隔 - 使用内置函数list()
# 第一种方式,使用[]
lst = [1, 2, 3]
# 第二种方式,使用内置函数 list()
lst2 = list([1, 2, 3])
# 空列表
a = []
a = list()
列表的特点
- 列表元素按顺序有序排列
- 索引映射唯一一个数据
- 列表可存储重复数据
- 任意类型数据
- 根据需要动态分配和回收内存
lst = ['hello', 'hello', 1 , 2]
print(lst)
print(lst[0])
# 输出结果
['hello', 'hello', 1, 2]
hello
列表的操作
获取列表中元素的索引值
元素不存在时,报错
lst = ['hello', 'hello', 1 , 2]
print(lst.index('hello')) # 找第一个'hello'元素的索引值
print(lst.index('hello', 1, 3)) # 在列表lst的索引1-3范围内,找第一个'hello'的索引值
获取列表中单个元素
索引不存在,报错
lst = ['hello', 'world', 'hello', 200, 300]
# 获取 'world' 元素
print(lst[1])
print(lst[-4])
获取列表多个元素(切片操作)
返回值为一个新的列表,就是原列表片段的拷贝(在内存开空间存储新的元素)。
lst = [10, 20, 30, 40, 50, 60, 70, 80]
# 截取 30-60 要注意切片的范围是 [start,stop) 半闭半开区间
print(lst[2:6:1])
lst[:6:] # 这种写法== [0:6:1],都取的默认值
判断元素是否存在
元素 in 列表
元素 not in 列表
lst = [10, 20, 30, 40, 50, 60, 70, 80]
print(100 in lst) # False
print(10 in lst) # True
列表元素遍历
for 迭代变量 in 列表:
循环体
lst = [10, 20, 30, 40, 50, 60, 70, 80]
for item in lst:
print(item)
列表元素的增加操作
-
append()
使用append()后,向列表末尾添加一个元素,并且不会改变列表对象(标识),没有产生新的列表对象
lst =[10, 20, 30]
# 向列表末尾添加一个元素
lst.append(40)
print(lst) # [10, 20, 30, 40]
- extend() 批量添加
lst =[10, 20, 30]
# 向列表末尾添加一个列表元素
lst2 = ['hello', 'world']
lst.append(lst2)
print(lst) # [10, 20, 30, ['hello', 'world']]
# 向列表末尾添加至少一个元素(扩展添加)
lst =[10, 20, 30]
lst.extend(lst2)
print(lst) # [10, 20, 30, 'hello', 'world']
- insert
索引不存在时,不会报错
索引值不存在时,索引值为负数,则将元素添加至第一位,索引是整数,则添加至最后一位
lst =[10, 20, 30]
lst.insert(1,100) # 在索引为 1 的位置上添加 元素 100
print(lst)
- 切片操作
注意,以下并没有创建新的列表对象,lst的id标识没有改变
lst = [10, 20, 30, 40, 50, 60, 70, 80]
lst2 = ['hello','world', 'json']
lst[1:] = lst2 # 把索引从1开始的lst中所有元素替换成 lst2中的所有元素
print(lst) # [10, 'hello', 'world', 'json']
列表删除元素操作
-
remove() 刪除一个元素
从列表中删除第一个匹配的元素
如果元素不存在,则会报错
lst = [10, 20, 30, 40, 50, 60, 70, 80] lst.remove(30) print(lst)
-
pop() 根据索引移除元素
索引不存在,报错
不指定索引,删除列表最后一个元素
lst = [10, 20, 30, 40, 50, 60, 70, 80] lst.pop() print(lst) # [10, 20, 30, 40, 50, 60, 70] lst.pop(0) print(lst) # [20, 30, 40, 50, 60, 70]
-
切片
一次至少删除一个元素,但是会产生新的列表对象
就是列表元素的截取,然后生成新的列表对象
lst = [10, 20, 30, 40] lst2 = lst[1:3] print(lst) #[10, 20, 30, 40] print(lst2) #[20, 30]
-
不产生新对象,删除元素
lst = [10, 20, 30, 40] lst[1:3] = [] # 只把lst中的 第二个元素和第三个元素 换成空列表 print(lst) # [10, 40]
-
clear()
清空列表
lst = [10, 20, 30, 40] lst.clear() print(lst) # [] 空列表
-
del 删除列表对象
del lst print(lst) # NameError: 'lst' is not defined 报错
列表修改元素
- 一次修改一个值
lst = [10, 20, 30, 40]
# 根据索引修改
lst[1] = 100
print(lst)
图示内存模型
-
一次修改多个值
lst = [10, 20, 30, 40] print(lst) # [10, 20, 30, 40] lst[1:3] = [100, 200, 300, 400] print(lst) # [10, 100, 200, 300, 400, 40]
把第二个元素和第三个元素去掉,替换成了 100,200,300,400
列表元素的排序
两种方式:
- sort()方法,所有元素默认升序排序,可指定
reverse=True
降序排序,不会产生新对象 - sorted()内置函数,可指定
reverse=True
降序排序,将会产生新的列表对象
# 使用sort()方法 不产生新对象
lst = [40, 20, 20, 10, 30]
lst.sort()
print(lst) # [10, 20, 20, 30, 40]
lst.sort(reverse=True)
print(lst) # [40, 30, 20, 20, 10]
# 使用sorted()内置函数 产生新对象
lst = [40, 20, 20, 10, 30]
sorted(lst)
print(lst) # [10, 20, 20, 30, 40]
sorted(lst, reverse=True)
print(lst) # [40, 30, 20, 20, 10]
列表生成式
# 产生1-9的列表对象
lst = [i for i in range(1, 10)]
print(lst)
# 值为乘本身
lst = [i*i for i in range(1, 10)]
print(lst)
字典
内存示例
字典:是Python内置的数据结构之一,与列表一样是一个可变序列
以键值对的方式存储数据,字典是一个无序的序列
语法示例:
特点
字典是无序的
字典中的所有元素都是k-v对,key不许重复(key重复,则会值覆盖,但是key还是只有一个),value可以重复
字典的key值是不可变序列,同字符串和整数类型一样都是不可变序列
字典可以根据需要动态的伸缩
字典会比较浪费较大的内存,是一种使用空间换时间的数据结构
实现原理
字典的实现原理和查字典类似,Python中字典是根据key
查找value
所在的位置。而这个key需要经过hash()函数计算得来,找到key后,直接获取到value
字典的创建
{}
创建dict()
内置函数创建
scores = {'age': 23, 'name': 'zs', 'gender': '男'}
print(scores)
scores = dict(name='jack', age=20)
print(scores)
sc = {} # 空字典
sc = dict()
字典的操作
获取字典中的元素
两种方式
scores = {'age': 23, 'name': 'zs', 'gender': '男'}
age = scores.get('age')
gender = scores['gender']
print(age) # 23
print(gender) # 男
两种的区别:
scores['gender']
这种方式不存在时会报错
scores.get('age')
这种方式会返回 None
get(),读取不到时,可设置默认值
scores = {'age': 23, 'name': 'zs', 'gender': '男'}
aaa = scores.get('aaa', 'wlh')
print(aaa) # wlh
判断key是否存在
in
not in
scores = {'age': 23, 'name': 'zs', 'gender': '男'}
print('aaa' in scores) # False
字典元素的删除
del,删除指定的 k-v对
scores = {'age': 23, 'name': 'zs', 'gender': '男'}
del scores['age']
print('age' in scores) # False,因为已经删除了 'age'的key
scores.clear() # 清空
print(scores) # {}
字典元素新增
scores = {'age': 23, 'name': 'zs', 'gender': '男'}
scores['wlh'] = 888
print(scores) # {'age': 23, 'name': 'zs', 'gender': '男', 'wlh': 888}
字典元素修改
scores = {'age': 23, 'name': 'zs', 'gender': '男'}
scores['wlh'] = 888
scores['wlh'] = 999
print(scores['wlh']) # 999
获取字典视图
- keys() 获取所有key
- values() 获取所有value
- items() 获取所有键值对k-v对
scores = {'age': 23, 'name': 'zs', 'gender': '男'}
print(scores.keys()) # dict_keys(['age', 'name', 'gender'])
print(scores.values()) # dict_values([23, 'zs', '男'])
print(scores.items()) # dict_items([('age', 23), ('name', 'zs'), ('gender', '男')])
注意:这里获取到的并不是list类型的,我们可以把这些类型转换成list列表
scores = {'age': 23, 'name': 'zs', 'gender': '男'}
print(list(scores.keys())) # ['age', 'name', 'gender']
print(list(scores.values())) # [23, 'zs', '男']
print(list(scores.items())) # [('age', 23), ('name', 'zs'), ('gender', '男')]
字典元素的遍历
scores = {'age': 23, 'name': 'zs', 'gender': '男'}
for i in scores: # 这里注意 i 是key 的值
print(i, scores[i], scores.get(i))
字典生成式
使用内置函数zip()
用于将可迭代的对象最为参数,将对象中对应的元素打包成一个元组
,然后返回由这些元组组成的列表
items = ['Fruits', 'Books', 'Others']
prices = [96, 78, 85]
d = { item:price for item,price in zip(items, prices)} # 以item为key,price为value生成字典
e = { item.upper():price for item,price in zip(items, prices)}
print(d) # {'Fruits': 96, 'Books': 78, 'Others': 85}
print(e) # {'FRUITS': 96, 'BOOKS': 78, 'OTHERS': 85}
元组
元组是Python内置的数据结构之一,是一个不可变序列
元组的创建
-
()
创建如果元组中只有一个元素,一定要加上逗号
,
-
使用内置函数
tuple()
# ()创建
t = ('hello', 'world', 90)
print(t) # ('hello', 'world', 90)
# ()可省略
t1 = 'Hello', 'world', 98
print(t1) # ('Hello', 'world', 98)
# 注意,这样子写,会认为是str类型
ts = ('Hello')
print(type(ts)) # <class 'str'>
# 要加一个 逗号
ts = ('hello',)
print(type(ts)) # <class 'tuple'>
t2 = tuple(('hello', 'world', 98))
print(t2) # ('hello', 'world', 98)
# 空元组创建
t = ()
t = tuple()
元组中获取数据
和列表一致,都是通过下标获取即可
t = (10, [23,30], 9)
print(t[1]) # [23, 30] 是一个list列表
为什么将元组设计为不可变序列
- 多任务环境下,同时操作对象不需要加锁
- 在程序中尽量使用不可变序列
注意:
元组中存储的是对象的引用
- 如果元组中对象本身是不可变对象,则不能再引用其他对象
- 如果元组中对象是可变对象,则可变对象的引用不允许改变,但是数据可以改变
注意:元组中引用的对象不能发生改变,不管引用对象是可变
还是不可变
可变对象的引用中,值可以改变,但是引用不能
t = (10, [23,30], 9)
# 错误写法
t[1] = 100
print(t)
# 正确写法 列表中添加元素
t[1].append(100)
print(t)
元组的遍历
for-in 遍历
t = (10, [23,30], 9)
for i in t:
print(i, end='\t') # 10 [23, 30] 9
集合
集合中的元素不能重复,会自动去重
-
可变类型的序列
-
集合是没有
value的字典
-
key的位置,同样是使用hash函数计算得出
集合的创建
-
直接使用
{}
-
使用内置函数
set()
# 通过{}
s = {2, 3, 4, 5, 5}
print(s)
# 通过set()内置函数
s1 = set(range(6))
print(s1)
# 通过set() 将list转为set集合
s2 = set([1,2,3,4])
print(s2)
# 将元组 转为set集合
s3 = set(('Python', 2, 5))
print(s3)
# 将字符串转为set集合(字符分割)
s4 = set('Python')
print(s4)
s5 = set({1,2,3,4,5})
print(s5)
# 定义空集合 只能使用 set()的方式
s6 = set()
集合的操作
集合元素判断操作
- in 和 not in
s = {10, 20, 30, 40}
print(10 in s) # True
print(100 not in s) # True
集合元素新增操作
- add() 方法,一次添加一个元素
- update() 方法,至少添加一个元素(批量添加)
# 单值添加
s = {10, 20, 30, 40}
s.add(50)
print(s) # {40, 10, 50, 20, 30}
# 批量添加
s.update({60, 70, 80})
print(s) # {70, 40, 10, 80, 50, 20, 60, 30}
# 也可以添加元组、列表等可迭代对象的元素
s.update([1,3,5])
s.update(('Python','hello',98))
集合的删除操作
- remove()方法,一次删除一个指定元素,如果元素不存在,则抛出异常
discard()
一次删除一个指定元素,如果不存在,不抛出异常- pop() 一次只删除一个任意元素
- clear() 清空集合
集合之间的关系
# 两集合是否相等,元素相同就相等
s1 = {10, 20, 30, 40}
s2 = {40, 30, 20, 10}
print(s1 == s2) # True
s1 = {10, 20, 30, 40}
s2 = {40, 30, 20}
# s2是s1的子集
print(s2.issubset(s1)) # True
# s1是s2的超集(父集)
print(s1.issuperset(s2)) # True
# 两个集合是否 没有交集
print(s1.isdisjoint(s2)) # False
集合数学操作
交集
intersection()
方法&
操作符
s1 = {10, 20, 30}
s2 = {20, 30, 40, 50}
print(s1.intersection(s2))
print(s1 & s2)
并集
union()
方法|
操作符
# 并集
print(s1.union(s2))
print(s1 | s2)
差集
比如 A比较B的差集,就是只取在 A中
B没有的元素
difference()
方法-
操作符
# 差集
print(s1.difference(s2))
print(s1 - s2)
对称差集
取两集合,互相没有的元素
symmetric_difference()
方法^
操作符
# 对称差集
print(s1.symmetric_difference(s2))
print(s1 ^ s2)
集合生成式
s = {i for i in range(6)}
print(s) # {0, 1, 2, 3, 4, 5}
列表、元组、字典、集合总结
可变序列和不可变序列
-
不可变序列:没有
增删改
的操作字符串、元组、整数
-
可变序列:可以对序列执行
增删改
操作,且对象的地址不会发生改变列表、字典、集合
字符串
字符串的驻留机制
字符串是一个基本数据类型,是一个不可变的字符序列
驻留机制:
仅保存一份相同且不可变字符串的方法,不同的值被存放在字符串的驻留池
中,Python的驻留机制对相同的字符串只保留一份拷贝,后续创建的相同字符串时,不会开辟新的空间,而是把已存在的字符串地址赋给新创建的变量。
-
驻留机制的几种情况,以下情况会让多变量指向同一个内存空间 (交互模式下看,PyCharm对这些做了优化)
-
字符串长度为0或1
-
符合标识符的字符串
-
字符串只在编译时进行驻留,而非运行时
-
a = 'abc' b = 'ab' + 'c' c = ''.join(['ab', 'c']) print(a is b) # True b的创建,在编译时就已经连接好了 print(a is c) # False c的创建,是程序运行时才连接好'abc'值
-
-
[-5,256]之间的整数数字
-
a = -5 b = -5 print(a is b) # True a = -6 b = -6 print(a is b) # False
-
-
-
sys中intern()方法,强制2个字符串指向同一个对象
-
import sys s1 = 'abc%' # 不符合标识符的字符串(正常情况下不被驻留) s2 = s1.intern(s1) print(s1 is s2) # True 强制驻留
-
-
PyCharm对字符串进行了优化处理
驻留机制的优缺点
- 当需要值相同的字符串时,可以直接从字符串池里拿出来用,避免频繁的创建和销毁,提升效率节约内存,因此拼接字符串和修改字符串时会比较影响性能的
- 在需要进行字符串的拼接时,建议使用str类型的
join()
方法,而非+
,因为 join()方法是先计算出所有字符串中的长度,然后进行拷贝,只new一次对象,效率要比+
高
字符串的常用操作
字符串的查找
- index() 查元素第一次出现的索引位置,不存在报错
- rindex() 查元素最后一次出现的索引位置,不存在报错
- find() 查元素第一次出现的索引位置,不存在返回 -1
- rfind() 查元素最后一次出现的索引位置,不存在返回 -1
建议使用 find()
和rfind()方法
s = 'hello,hello'
print(s.index('lo')) # 3
print(s.find('lo')) # 3
print(s.rindex('lo')) # 9
print(s.rfind('lo')) # 9
print(s.index('k')) # 报错
print(s.find('k')) # 返回 -1
字符串的大小写转换
转换后,会生成新的字符串对象
- upper()
- lower()
- swapcase()
- title()
s = 'hello,python'
a = s.upper() # 转大写,会生成新对象
print(a) # HELLO,PYTHON
b = a.lower() # 转小写,会生成新对象
print(b) # hello,python
s2 = 'hello,Python'
print(s2.swapcase()) # 大写转小写,小写转大写 HELLO,pYTHON
print(s2.title()) # 每个单词的首字母大写,其他小写 Hello,Python
字符串对齐操作
注意:如果指定的字符串长度,小于原字符串长度,那么将直接返回原字符串,不会进行填充操作。
s = 'hello,Python'
print(s.center(20, '*')) # 字符串居中,总长度20,剩余 '*' 补齐 ****hello,Python****
print(s.ljust(20, '*')) # 字符串左对齐,总长度20,剩余 '*' 补齐 hello,Python********
print(s.rjust(20, '*')) # 字符串右对齐,总长度20,剩余 '*' 补齐 ********hello,Python
print(s.zfill(20)) # 字符串右对齐,总长度20,剩余 '0' 补齐 00000000hello,Python
字符串分割
如果不声明以什么字符分割,则会默认 空格
字符串,返回一个列表
- split()方法
s = 'hello world Python'
a = s.split() # 从左边分割,默认空格分割
print(a) # ['hello', 'world', 'Python']
s1 = 'hello|world|Python'
a2 = s1.split('|') # 以 '|' 分割
print(a2) #
a3 = s1.split('|', 1) # 以 '|' 分割,且只分割一次
print(a3) # ['hello', 'world|Python']
- rsplit() 方法
s1 = 'hello|world|Python'
a2 = s1.rsplit('|') # 从右边以'|'分割
print(a2) # ['hello', 'world', 'Python']
a3 = s1.rsplit('|', 1) # 从右边以'|'分割,只分割一次
print(a3) # ['hello|world', 'Python']
注意点: 如果未指定最大分割次数,那么split()和rsplit()效果一样,如果指定最大分割次数,那么要注意两个方法的执行效果可能不一致。
字符串判断
s = 'hello,world'
# 是否是合法标识字符串
print(s.isidentifier()) # False
print('hello'.isidentifier()) # True
# 是否全部为空白字符
print(' '.isspace()) # True
# 全部是字母?
print('abc'.isalpha()) # True
print('张三'.isalpha()) # True
# 全部是 十进制 数字?
print('1234'.isdecimal()) # True
print('12三'.isdecimal()) # False
# 全部是数字?
print('122'.isnumeric()) # True
print('12三'.isnumeric()) # True 不只中文数字,罗马数字同样可以
# 由 数字和字母组成?
print('safds112张三'.isalnum()) # True
print('safds112张三!'.isalnum()) # False 有个'!'
字符串的替换与合并
- replace()
s = 'Hello,Python'
print(s.replace('Python', 'Java')) # 将'python'替换为'Java' Hello,Java
s = 'Hello,Python,Python,Python'
print(s.replace('Python', 'Java', 2)) # 将'python'替换为'Java',只替换2个 Hello,Java,Java,Python
- join()
lst = ['Java', 'Python', 'Go']
print('|'.join(lst)) # Java|Python|Go
print(''.join(lst)) # JavaPythonGo
print("|".join('Python')) # P|y|t|h|o|n
字符串的比较
print('apple' > 'app') # True
print('apple' > 'banana') # False
# 原理就是 挨个比较 单个字符的 原始值(可用ord()内置函数获取)
print(ord('a'), ord('b')) # a = 97 b = 98 所以
# chr() 可以获取原始值对应的 字符
print(chr(97), chr(98)) # a b
== 和 is 的区别
==:比较的是内容是否相等
is : 比较的是内存地址(id标识)是否相等
字符串的切片操作
字符串是不可变类型
- 不具备 增删改操作
- 切片后将产生新的对象
# 每切一次都会产生新的对象
s = 'hello,Python'
print(s[:5:1]) # 默认从0开始切,步长默认1(可省略) # hello
print(s[6::1]) # 默认切到最后一个最值,步长默认1(可省略) # Python
# 步长为负数
print(s[::-1]) # 默认从字符串最后一位置开始,因为步长是负数 # nohtyP,olleh
# 索引为负数
print(s[-6::1]) # 从-6位置开始,到字符串最后结束,步长为1 # Python
格式化字符串
%
占位符{}
占位符f-string
name = '张三'
age = 21
# % 占位符
print('我的名字是%s,今年%d岁了' % (name, age))
print('%.3f' % 3.1415826) # 保留3位小数
print('%10.3f' % 3.1415926) # 总长度10,并且保留3位小数
# {} 占位符
print('我的名字是{0},今年{1}岁了'.format(name, age))
# f-String python3新增
print(f'我的名字是{name},今年{age}岁了')
print('{0:.3}'.format(3.1415926)) # 3.14 保留3位有效数字
print('{:.3f}'.format(3.1415926)) # 3.142 保留3为小数
print('{:10.3f}'.format(3.1415926)) # 总长度10,并且保留3位小数
字符串编码转换
为什么要进行字符串的编码转换
编码和解码
- 编码:将字符串转换为二进制数据(bytes)
- 解码:将bytes类型的数据转换成字符串类型
注意,编码和解码时,使用相同的编码格式。。
s = '天涯共此时'
# 编码
print(s.encode('GBK')) # GBK这种编码格式,一个中文占用 两个字节
print(s.encode('UTF-8')) # UTF-8 一个中文占用 三个字节
# 解码
byte1 = s.encode('GBK')
print(byte1.decode('GBK'))