为什么需要列表?
变量可以存储一个元素,而列表是一个 大容器
可以存储N多个元素(不同数据类型),程序可以方便地对这些数据进行整体操作
列表有两种索引方式:如下图
代码示例:
a = 10 # 变量存储的是一个对象的引用(标识)
lst = ['hello', 'world', 98] # 存储多个对象的引用(标识)
print(lst)
图示:(以下id标识自编的不真实)
一个list列表中,存储的是多个对象的引用标识(id)
,
分隔# 第一种方式,使用[]
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]
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']
索引不存在时,不会报错
索引值不存在时,索引值为负数,则将元素添加至第一位,索引是整数,则添加至最后一位
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
两种方式:
reverse=True
降序排序,不会产生新对象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
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
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)) #
# 要加一个 逗号
ts = ('hello',)
print(type(ts)) #
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()
s = {10, 20, 30, 40}
print(10 in s) # True
print(100 not in s) # True
# 单值添加
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))
discard()
一次删除一个指定元素,如果不存在,不抛出异常# 两集合是否相等,元素相同就相等
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对字符串进行了优化处理
join()
方法,而非 +
,因为 join()方法是先计算出所有字符串中的长度,然后进行拷贝,只new一次对象,效率要比+
高建议使用 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
转换后,会生成新的字符串对象
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
如果不声明以什么字符分割,则会默认 空格
字符串,返回一个列表
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']
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 有个'!'
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
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位小数
为什么要进行字符串的编码转换
注意,编码和解码时,使用相同的编码格式。。
s = '天涯共此时'
# 编码
print(s.encode('GBK')) # GBK这种编码格式,一个中文占用 两个字节
print(s.encode('UTF-8')) # UTF-8 一个中文占用 三个字节
# 解码
byte1 = s.encode('GBK')
print(byte1.decode('GBK'))