likes
comments
collection
share

Python中的序列

作者站长头像
站长
· 阅读数 25

序列是指一块可以存放多个值的连续的内存空间,这些值按一定的顺序进行排列,可以通过每个值的编号也称索引访问到它们。

文章介绍

  1. 第一节主要描述了对序列根据不同的情况作出的不同分类
  2. 第二节主要描述了列表推导式的使用(作用域问题)和生成器表达式的使用
  3. 第三节主要描述了元组发生改变时的一些情况
  4. 第四节通过表格的方式划分了列表和元组的一些常见方法
  5. 第五节描述了拆包操作(*号的作用和*号在函数传参时的作用)
  6. 第六节描述了切片的作用(切片对象和通过切片改变序列)

一、序列的分类

1. 根据存储对象的类型进行划分

可以分为容器序列和扁平序列

容器序列

容器序列可以存放不同类型的项,存放的是所包含对象的引用,对象可以是任何类型 常见的容器序列有: list和tuple。

# 容器序列存放的是对象的引用,也可以理解为存放的是对象在内存中的地址
li = [7, 8, 9]
lis = [li]
print("当前li对象:", id(li))
print(lis)
li = [9, 10, 11]  # 当我们给变量重新赋值时,实际上,变量只是改变了对某个对象的绑定
print("重新绑定后的li对象:", id(li))
print(lis)  # 此时,由于原始对象没有发生改变,所以列表中的数据并不会发生改变
print("lis中存储的li对象:", id(lis[0]))
li = lis[0]
print("li对象:", id(li))
print(li)
li[0] = 100
print(lis)

扁平序列

扁平序列在自己的内存空间中存储所包含内容的值。结构更加紧凑,也更加节省内存,但是只能存储同种类型的数据。 常见的扁平序列如: str、array.array

2. 根据序列是否可变(序列内的某个元素发生改变是否会产生新的对象)

不可变序列

序列内的某个元素发生改变时,会产生一个新的对象。 常见的如: tuple、str、 bytes

可变序列

序列内的某个元素发生改变时,操作的还是原有的对象。 常见的如: list、array.array

二、列表推导式和生成器表达式

列表推导式

作用就是可以以更简洁的方式生成一个列表。同时,在作用域方面也有一定的优势

# 使用列表推导式生成列表
L1 = []
for i in range(10, 20):
    L1.append(i)
print("L1:", L1)
L2 = [x for x in range(10, 20)]
print("L2:",L2)

可以看到,使用for循环需要三行的代码,使用列表推导式一行就可以解决,代码更加简洁。列表推导式还可以作用于多个可迭代序列的生成。

# 多个可迭代序列生成
L = [5, 6]
L1 = [7, 8]
L2 = [9, 10]
L3 = []
for i in L:
    for j in L1:
        for k in L2:
            L3.append([i, j, k])
print("L3:", L3)
L4 = [[i, j, k] for i in L for j in L1 for k in L2]
print("L4:", L4)

在遍历多个可迭代序列时,列表推导式显得更加简洁。

# 列表推导式在作用域方面的好处
x = 10
L1 = []
for x in range(10, 20):
    L1.append(x)
print("L1:", L1)
print("x:", x)
y = 10
L2 = [y for y in range(10, 20)]
print("L2:", L2)
print("y:", y)

可以看到,当我们在使用for循环生成列表时,如果变量使用不规范,很容易导致某些全局变量被改变,但是使用列表推导式则完全不需要考虑这个问题

生成器表达式

与列表推导式的使用方式一致,只是将[]换成(),但是在功能上而言,生成器表达式更加节省内存空间,因为它可以通过迭代器协议逐个产出元素,而不是像列表推导式那样,直接在内存空间中生成所有的元素

三、当元组发生改变时

元组是不可变对象,且相较于列表更加节省内存空间

T1 = (5, 6, 7, 8, 9)
print(id(T1))
T1 = (9, 10)
print(id(T1))

这种方式虽然让T1对应的元组对象发生了改变,但是实际上是改变了T1所绑定的对象,原始的元组对象并没有发生改变。但是有一种特殊情况,确实会出现元组内的元素发生了改变,但是元组对象仍然正常的。

当元组中包含可变对象时

lis = [9, 10, 11]
T1 = (5, 6, 7, 8, lis)
print("T1:", T1)
print("T1对象:", id(T1))
lis.append(12)
print("T1:", T1)
print("T1对象:", id(T1))

正如在前边所说的那样,容器序列保存的是对对象的引用,所以元组本身是不可变的,但是当元组内存存在可变对象的引用时,如果可变对象发生了改变,元组内部对应的元素也会发生改变。

四、列表和元组的一些常用方法

方法列表中是否存在元组中是否存在描述
s.__add__(s1)s+s1:拼接
s.__iadd__(s1)s+=s1:就地拼接
s.append(e)在最后一个元素后面追加一个元素
s.clear()删除所有项
s.__contains__(e)e in s: 判断某个元素是否在序列中
s.copy()浅拷贝序列
s.count(e)计算元素出现的次数
s.__delitem__删除位置p上的项
s.extend(it)追加可迭代对象it中的项
s.__getitem__(p)s[p]:获取指定位置上的项
s.__getnewargs__支持使用pickle优化序列项
s.index(e)找出e首次出现的位置
s.insert(p, e)在位置p上的项之前插入元素e
s.__iter__()获取迭代器
s.__len__()len(s):获取序列中的项数
s.__mul__(n)s*n:重复拼接
s.__imul__(n)s*=n: 就地重复拼接
s.__rmul__n*s: 反向重复拼接
s.pop([p])移除并返回最后一项或可选位置p上的项
s.remove(e)把e的值从首次出现的位置上移除
s.reverse()就地翻转项的顺序
s.__reversed__()获取从后往前遍历项的迭代器
s.__setitem__(p, e)s[p]=e:把e放在位置p上,覆盖现有的项
s.sort([key], [reverse])就地对项排序,key和reverse是可选的关键字参数

五、序列和可迭代对象拆包

并行赋值

一个常见的拆包方式,就是将可迭代序列中的元素赋值到对应的变量上

# 并行赋值的要求就是变量的数量要跟序列中的元素数量一致
lis = [5, 6, 7]
a, b, c = lis
print("a:", a)
print("b:", b)
print("c:", c)

使用*获取余下的项

前边的并行赋值,要求变量数量跟序列中的元素数量保持一致,但是有的时候,我们并不能准确的知道序列的具体数量,我们知道要去序列中的第几个元素,这个时候就要用到*来进行捕获多余的变量

lis = [x for x in range(1, 100)]
a, *_, c = lis  # 去开头和结尾的两项
print("a:", a)
print("c:", c)
print("_:", _)  # 一般情况下,_代表的是临时变量,也就是有些内容后面并不会用到,但是想不出好的承载这个数据的变量名,就用_

*可以获取在拆包过程中多余的内容,拆包的具体分配过程是:

先看*前面有几个变量,则将开头的几个元素给到这几个变量上。 在看后面有几个变量,则将结尾的几个元素一一给到这几个变量上。 然后再将剩下的所有元素,给到*对应的变量上

函数调用中的*

def fun(a, b, c, d, *rest):
    print("a:", a)
    print("b:", b)
    print("c:", c)
    print("d:", d)
    print("rest:",rest)
fun(*[1, 2], 3, *range(4,7))

在这段代码中,多次使用了*进行了拆包处理,具体的执行流程如下:

  1. 先将列表[1,2]进行拆包为1和2
  2. 在将*rage(4, 7)拆包为4, 5, 6
  3. 找个时候传入的参数元组为(1, 2, 3, 4, 5, 6)
  4. 然后1, 2, 3, 4分别对应到a, b, c, d这四个参数上,剩余的内容对应到rest参数上
# output
a: 1
b: 2
c: 3
d: 4
rest: (5, 6)

嵌套拆包

和前面的拆包方式一样,元素一一对应,或者用*匹配多余的元素

# 一个简单的嵌套拆包
lis = [5, 6, (7, 8), 9, [10, 11]]
a, b ,*_, (c, d) = lis
print("a:", a)
print("b:", b)
print("c:", c)
print("d:", d)
print("_:", _)

六、切片

切片对象

r = slice(0, -1, 2)  # 从可迭代序列的低一个元素开始,到最后一个元素终止,步长为2
L1 = [x for x in range(101)]
print(L1[r])
T1 = [x * 100 for x in range(10)]
print(T1[r])

这里的slice代表的就是创建一个切片对象,实际上,在执行切片操作时,Python也是会产生一个相应的切片对象,在根据这个切片对象去操作相关的序列。

为切片赋值

l = [x for x in range(10)]
print(l)
del l[0:2]  # 通过切片删除序列中的某些元素
print(l)
l[0:2] = [5]  # 通过切片修改序列中的某些元素
print(l)
# l[0:2] = 3  # 这种方式是错误的,通过切片赋值的内容必须是可迭代对象
print(l)
转载自:https://juejin.cn/post/7393533304504664115
评论
请登录