Python序列

0x00 前言

所谓序列,指的是一块可存放多个值的连续内存空间,这些值按一定顺序排列,可通过每个值所在位置的编号(称为索引)访问它们。

为了更形象的认识序列,可以将它看做是一家旅店,那么店中的每个房间就如同序列存储数据的一个个内存空间,每个房间所特有的房间号就相当于索引值。也就是说,通过房间号(索引)我们可以找到这家旅店(序列)中的每个房间(内存空间)。

Python 中,序列类型包括字符串、列表、元组、集合和字典,这些序列支持以下几种通用的操作,但比较特殊的是,集合和字典不支持索引、切片、相加和相乘操作。

字符串也是一种常见的序列,它也可以直接通过索引访问字符串内的字符。

本节介绍的序列主要是指列表和元组,这两种类型看起来非常相似,最主要的区别在于:元组是不可变的,元组一旦构建出来,程序就不能修改元组所包含的成员(就像字符串也是不可变的,程序无法修改字符串所包含的字符序列);但列表是可变的,程序可以修改列表所包含的元素。

在具体的编程过程中,如果只是固定地保存多个数据项,则不需要修改它们,此时就应该使用元组;反之,就应该使用列表。此外,在某些时候,程序需要使用不可变的对象,比如 Python 要求字典的 key 必须是不可变的,此时程序就只能使用元组。

简单讲,列表和元组的关系就是可变和不可变的关系。

0x01序列索引

序列中,每个元素都有属于自己的编号(索引)。从起始元素开始,索引值从 0 开始递增,如图 1 所示。

img
图 1 序列索引值示意图

除此之外,Python 还支持索引值是负数,此类索引是从右向左计数,换句话说,从最后一个元素开始计数,从索引值 -1 开始,如图 2 所示。

img
图 2 负值索引示意图

注意,在使用负值作为列序中各元素的索引值时,是从 -1 开始,而不是从 0 开始。

无论是采用正索引值,还是负索引值,都可以访问序列中的任何元素。以字符串为例,访问“C语言中文网”的首元素和尾元素,可以使用如下的代码:

str="C语言中文网"
print(str[0],"==",str[-6])
print(str[5],"==",str[-1])

输出结果为:

C == C
网 == 网

0x02 序列切片

注:切片遵循左闭右开原则

切片操作是访问序列中元素的另一种方法,它可以访问一定范围内的元素,通过切片操作,可以生成一个新的序列。

序列实现切片操作的语法格式如下:

sname[start : end : step]

其中,各个参数的含义分别是:

  • sname:表示序列的名称;
  • start:表示切片的开始索引位置(包括该位置),此参数也可以不指定,会默认为 0,也就是从序列的开头进行切片;
  • end:表示切片的结束索引位置(不包括该位置),如果不指定,则默认为序列的长度;
  • step:表示在切片过程中,隔几个存储位置(包含当前位置)取一次元素,也就是说,如果 step 的值大于 1,则在进行切片去序列元素时,会“跳跃式”的取元素。如果省略设置 step 的值,则最后一个冒号就可以省略。

例如,对字符串“C语言中文网”进行切片:

str="C语言中文网"

#取索引区间为[0,2]之间(不包括索引2处的字符)的字符串
print(str[:2])

#隔 1 个字符取一个字符,区间是整个字符串
print(str[::2])

#取整个字符串,此时 [] 中只需一个冒号即可
print(str[:])

运行结果为:

C语
C言文
C语言中文网

上面语法中 start、end 两个索引值都可使用正数或负数,其中负数表示从倒数开始。该语法表示从 start 索引的元素开始(包含),到 end 索引的元素结束(不包含)的所有元素,这和所有编程语言的约定类似。

step 表示步长,因此 step 使用负数没有意义。

下面代码示范了使用 start、end 获取元组中间一段的用法:

a_tuple = ('crazyit', 20, 5.6, 'fkit', -17)
# 访问从第2个到倒数第4个(不包含)所有元素
print(a_tuple[1: 3]) # (20, 5.6)
# 访问从倒数第3个到倒数第1个(不包含)所有元素
print(a_tuple[-3: -1]) # (5.6, 'fkit')
# 访问从第2个到倒数第2个(不包含)所有元素
print(a_tuple[1: -2]) # (20, 5.6)
# 访问从倒数第3个到第5个(不包含)所有元素
print(a_tuple[-3: 4]) # (5.6, 'fkit')

如果指定 step 参数,则可间隔 step 个元素再取元素。例如如下代码:

b_tuple = (1, 2, 3, 4, 5, 6, 7, 8, 9)
# 访问从第3个到第9个(不包含)、间隔为2的所有元素
print(b_tuple[2: 8: 2]) # (3, 5, 7)
# 访问从第3个到第9个(不包含)、间隔为3的所有元素
print(b_tuple[2: 8: 3]) # (3, 6)
# 访问从第3个到倒数第2个(不包含)、间隔为3的所有元素
print(b_tuple[2: -2: 2]) # (3, 5, 7)

0x03 序列相加

Python 中,支持两种类型相同的序列使用“+”运算符做相加操作,它会将两个序列进行连接,但不会去除重复的元素。

这里所说的“类型相同”,指的是“+”运算符的两侧序列要么都是序列类型,要么都是元组类型,要么都是字符串。

例如,前面章节中我们已经实现用“+”运算符连接 2 个(甚至多个)字符串,如下所示:

str="c.biancheng.net"
print("C语言"+"中文网:"+str)

输出结果为:

C语言中文网:c.biancheng.net

列表和元组支持加法运算,加法的和就是两个列表或元组所包含的元素的总和。

需要指出的是,列表只能和列表相加;元组只能和元组相加;元组不能直接和列表相加。

如下代码示范了元组和列表的加法运算:

a_tuple = ('crazyit' , 20, -1.2)
b_tuple = (127, 'crazyit', 'fkit', 3.33)
# 计算元组相加
sum_tuple = a_tuple + b_tuple
print(sum_tuple) # ('crazyit', 20, -1.2, 127, 'crazyit', 'fkit', 3.33)
print(a_tuple) # a_tuple并没有改变
print(b_tuple) # b_tuple并没有改变
# 两个元组相加
print(a_tuple + (-20 , -30)) # ('crazyit', 20, -1.2, -20, -30)
# 下面代码报错:元组和列表不能直接相加
#print(a_tuple + [-20 , -30])
a_list = [20, 30, 50, 100]
b_list = ['a', 'b', 'c']
# 计算列表相加
sum_list = a_list + b_list
print(sum_list) # [20, 30, 50, 100, 'a', 'b', 'c']
print(a_list + ['fkit']) # [20, 30, 50, 100, 'fkit']

0x04 序列相乘

列表和元组可以和整数执行乘法运算,列表和元组乘法的意义就是把它们包含的元素重复 N 次(N 就是被乘的倍数)。

如下代码示范了列表和元组的乘法:

a_tuple = ('crazyit' , 20)
# 执行乘法
mul_tuple = a_tuple * 3
print(mul_tuple) # ('crazyit', 20, 'crazyit', 20, 'crazyit', 20)
a_list = [30, 'Python', 2]
mul_list = a_list * 3
print(mul_list) # [30, 'Python', 2, 30, 'Python', 2, 30, 'Python', 2]

当然,也可以对列表、元组同时进行加法、乘法运算。例如,把用户输入的日期翻译成英文表示形式,即添加英文的“第”后缀。对于 1、2、3 来说,英文的“第”后缀分别用 st、nd、rd 代表,其他则使用 th 代表。

为此,可使用如下代码来完成该转换:

# 同时对元组使用加法、乘法
order_endings = ('st', 'nd', 'rd')\
    + ('th',) * 17 + ('st', 'nd', 'rd')\
    + ('th',) * 7 + ('st',)
# 将会看到st、nd、rd、17个th、st、nd、rd、7个th、st
print(order_endings)
day = input("输入日期(1-31):")
# 将字符串转成整数
day_int = int(day)
print(day + order_endings[day_int - 1])

该程序中,同时对 (‘th’,) 元组使用了乘法,再将乘法得到的结果使用加法连接起来,最终得到一个元组,该元组共有 31 个元素。

可能有读者对 (‘th’,) 这种写法感到好奇,此处明明只有一个元素,为何不省略逗号?这是因为 (‘th’) 只是字符串加上圆括号,并不是元组,也就是说,(‘th’) 和 ‘th’ 是相同的。为了表示只有一个元素的元组,必须在唯一的元组元素之后添加英文逗号。

运行上面程序,可以看到如下运行结果:

输入日期(1-31):27
27th

从上面的运行结果可以看出,用户输入 27,程序通过元组为 27 添加了“th”后缀。

检查元素是否包含在序列中

Python 中,可以使用 in 关键字检查某元素是否为序列的成员,其语法格式为:

value in sequence

其中,value 表示要检查的元素,sequence 表示指定的序列。

例如,检查字符‘c’是否包含在字符串“c.biancheng.net”中,可以执行如下代码:

str="c.biancheng.net"
print('c'in str)

运行结果为:

True

和 in 关键字用法相同,但功能恰好相反的,还有 not in 关键字,它用法检查某个元素是否不包含在指定的序列中,比如说:

str="c.biancheng.net"
print('c' not in str)

输出结果为:

False

长度、最大值和最小值

Python 提供了内置的 ten()、max()、min() 全局函数来获取元组或列表的长度、最大值和最小值。

由于 max()、min() 要对元组、列表中的元素比较大小,因此程序要求传给 max()、min() 函数的元组、列表的元素必须是相同类型且可以比较大小。例如如下代码:

# 元素都是数值的元组
a_tuple = (20, 10, -2, 15.2, 102, 50)
# 计算最大值
print(max(a_tuple)) # 102
# 计算最小值
print(min(a_tuple)) # -2
# 计算长度
print(len(a_tuple)) # 6
# 元素都是字符串的列表
b_list = ['crazyit', 'fkit', 'Python', 'Kotlin']
# 计算最大值(依次比较每个字符的ASCII码值,先比较第一个字符,若相同,继续比较第二个字符,以此类推)
print(max(b_list)) # fkit(26个小写字母的ASCII码为97~122)
# 计算最小值
print(min(b_list)) # Kotlin (26个大写字母的ASCII码为65~90)
# 计算长度
print(len(b_list)) # 4

在上面代码中,首先使用 3 个函数对元素都是数值的元组进行处理,可以看到程序获取元组的最大值、最小值等。程序后半部分使用 3 个函数对元素都是宇符串的列表进行处理,也可以看到程序获取列表的最大值、最小值等,这说明 Python 的字符串也是可比较大小的,即 Python 依次按字符串中每个字符对应的编码来比较字符串的大小。

序列封包和序列解包

Python 还提供了序列封包(Sequence Packing)和序列解包(Sequence Unpacking)的功能。简单来说,Python 允许支持以下两种赋值方式:

  • 程序把多个值赋给一个变量时,Python 会自动将多个值封装成元组。这种功能被称为序列封包。
  • 程序允许将序列(元组或列表等)直接赋值给多个变量,此时序列的各元素会被依次赋值给每个变量(要求序列的元素个数和变量个数相等)。这种功能被称为序列解包。

下面代码示范了序列封包和序列解包的功能:

# 序列封包:将10、20、30封装成元组后赋值给vals
vals = 10, 20, 30
print(vals) # (10, 20, 30)
print(type(vals)) # <class 'tuple'>
print(vals[1]) # 20
a_tuple = tuple(range(1, 10, 2))
# 序列解包: 将a_tuple元组的各元素依次赋值给a、b、c、d、e变量
a, b, c, d, e = a_tuple
print(a, b, c, d, e) # 1 3 5 7 9
a_list = ['fkit', 'crazyit']
# 序列解包: 将a_list序列的各元素依次赋值给a_str、b_str变量
a_str, b_str = a_list
print(a_str, b_str) # fkit crazyit

如果在赋值中同时运用了序列封包和序列解包机制,就可以让赋值运算符支持同时将多个值赋给多个变量。例如如下代码:

# 将10、20、30依次赋值给x、y、z
x, y, z = 10, 20, 30
print(x, y, z) # 10 20 30

上面代码实际上相当于如下执行过程:

#先执行序列封包
xyz = 10,20,30
#再执行序列解包
x,y,z = xyz

使用这种语法也可以实现交换变量的值,例如如下代码:

# 将y,z, x依次赋值给x、y、z
x, y, z = y, z, x
print(x, y, z) # 20 30 10

在序列解包时也可以只解出部分变量,剩下的依然使用列表变量保存。为了使用这种解包方式,Python 允许在左边被赋值的变量之前添加“*”,那么该变量就代表一个列表,可以保存多个集合元素。例如如下程序:

# first、second保存前2个元素,rest列表包含剩下的元素
first, second, *rest = range(10)
print(first) # 0
print(second) # 1
print(rest) # [2, 3, 4, 5, 6, 7, 8, 9]
# last保存最后一个元素,begin保存前面剩下的元素
*begin, last = range(10)
print(begin) # [0, 1, 2, 3, 4, 5, 6, 7, 8]
print(last) # 9
# first保存第一个元素,last保存最后一个元素,middle保存中间剩下的元素
first, *middle, last = range(10)
print(first) # 0
print(middle) # [0, 1, 2, 3, 4, 5, 6, 7, 8]
print(last) # 9

和序列相关的内置函数

Python提供了几个内置函数(表 3 所示),可用于实现与序列相关的一些常用操作。

函数功能
len()计算序列的长度,即返回序列中包含多少个元素。
max()找出序列中的最大元素。注意,对序列使用 sum() 函数时,做加和操作的必须都是数字,不能是字符或字符串,否则该函数将抛出异常,因为解释器无法判定是要做连接操作(+ 运算符可以连接两个序列),还是做加和操作。
min()找出序列中的最小元素。
list()将序列转换为列表。
str()将序列转换为字符串。
sum()计算元素和。
sorted()对元素进行排序。
reversed()反向序列中的元素。
enumerate()将序列组合为一个索引序列,多用在 for 循环中。

这里给大家给几个例子:

str="c.biancheng.net"

#找出最大的字符
print(max(str))

#找出最小的字符
print(min(str))

#对字符串中的元素进行排序
print(sorted(str))

输出结果为:

t
.
['.', '.', 'a', 'b', 'c', 'c', 'e', 'e', 'g', 'h', 'i', 'n', 'n', 'n', 't']
©️2020 CSDN 皮肤主题: 技术黑板 设计师:CSDN官方博客 返回首页