master
/ 5.1.1 索引.ipynb

5.1.1 索引.ipynb @masterview markup · raw · history · blame

Notebook

索引

对于__元组、列表____range__ 对象等基本__序列__类型,序列中每个元素拥有一个序号。
对于字符串,其中的每个字符拥有一个序号。
序列数据内部的字符或元素按照顺序__有序存储__,可以使用序号取得相应的数据项或字符。

Python维护了两套索引:
__正向索引__正向从 __0__ 开始,终止值为序列__长度减1__(元素个数减一,即 len(s)-1);
__逆向索引____-1__ 开始,逆向索引终止值为负的序列__长度__(即-len(s) )
(注意,计算机中 -0 和 0 一样,因此,负数索引从 -1 开始)

两种序号体系可以__混合使用__,并且结合两种表示方法可以方便的对序列进行索引和切片。
图 6.1 中以列表和字符串为例给出正向和逆向两种索引编号规则的示例。
对字符串而言,英文、中文、空格和各种符号都__各占一个字符__位。

对于序列结构数据来说,索引和步长都具有正负两个值,分别表示左、右两个方向取值。
索引的正方向从左往右取值,起始位置为0;
逆向从右往左取值,起始位置为 -1。
因此任意一个序列结构数据的索引范围为 -len(seq) 到 len(seq)-1 范围内的连续整数。

所谓的__索引__是指通过序列数据的__序号__返回其对应的__字符或元素__的操作。 可以按正向序号进行索引或按逆向序号进行索引,通过序号获取对应的元素。 索引的方法是:

序列名__[__序号__]__

In [1]:
# 字符串序列
s = 'Hello Python!'  # 字符串,13个字符,空格算字符
print(s[0])    # 按序号正向索引,返回序号为0的字符 'H'
print(s[-13])  # 按逆向序号索引,返回序号为-13的字符 'H'

print(s[4])    # 按序号正向索引,返回序号为4的字符 'o'
print(s[-9])   # 按逆向序号索引,返回序号为-9的字符 'o'

print(s[6])    # 按序号正向索引,返回序号为6的字符 'P'
print(s[-7])   # 按逆向序号索引,返回序号为-7的字符字符 'P'

print(s[12])   # 按序号正向索引,返回序号为12的字符 '!'
print(s[-1])   # 按逆向序号索引,返回最后一个字符 '!'
H
H
o
o
P
P
!
!

实例 6.1 从身份证获取性别

公民身份号码里面还隐含出生日期、出生地、性别等信息,身份证倒数第二位是性别信息,奇数表示“男”,偶数表示“女”。
输入一个合法的身份证号,输出其持有者的性别。

In [2]:
id_number = input()

if id_number[16] in '02468':       # 字符串更简洁,也避免转整数的运算
# if int(id_number[16]) % 2 == 0:  # 也可转整数再用取模运算判定奇偶
    gender = '女'
else:
    gender = '男'

print(f'性别为{gender}')
性别为男
In [ ]:
# 利用条件表达式实现更简洁

id_number = input()
gender = '女' if id_number[16] in '02468' else '男'
print(f'性别为{gender}')

# 可在一行内完成
print(f'性别为女') if input()[16] in '02468' else print(f'性别为男')

In [3]:
# 序列类型 — 列表
scores = ['李明', 84, 80, 95, 88, 76, 65, 85, 98, 55]
print(scores[0])   # 按序号正向索引,返回序号为0的元素'李明'
print(scores[4])   # 按序号正向索引,返回序号为3的元素88
print(scores[-1])  # 按逆向序号索引,返回倒数第1个元素55
print(scores[-3])  # 按逆向序号索引,返回倒数第3个元素85
李明
88
55
85
In [ ]:
# 序列类型 — range
r = range(10)  # 获得0,1,2,3,4,5,6,7,8,9的序列对象
print(r[3])    # 按序号正向索引,返回序号为3的元素  3
print(r[-3])   # 按逆向序号索引,返回倒数第3个元素  7

索引超出范围

要注意的是,当使用的__索引值超出__列表现有数据的索引时,Python将会产生“索引超出范围”的错误。
例如,用试图用scores[10] 或scores[-11]获取列表 scores中不存在的序号 10 的数据,会得到“__IndexError: list index out of range__”的出错提示。

In [5]:
# 序列类型 — 列表索引越界
scores = ['李明', 84, 80, 95, 88, 76, 65, 85, 98, 55]
print(scores[10])
# 输出:IndexError: list index out of range
---------------------------------------------------------------------------
IndexError                                Traceback (most recent call last)
/tmp/ipykernel_212/4158428604.py in <module>
      1 # 序列类型 — 列表索引越界
      2 scores = ['李明', 84, 80, 95, 88, 76, 65, 85, 98, 55]
----> 3 print(scores[10])
      4 # 输出:IndexError: list index out of range

IndexError: list index out of range
In [ ]:
# 序列类型 — 列表索引越界
scores = ['李明', 84, 80, 95, 88, 76, 65, 85, 98, 55]
print(scores[-11])
# 输出:IndexError: list index out of range

索引序号__必须为整数__,不可为浮点数。

当试图用浮点数做索引序号时,Python将会产生TypeError。
当索引值是通过计算得到时,务必使其值为整型才可用做索引序号,或先对其进行__取整操作__再用做索引序号。
例如,10 / 5和10.0 // 5的结果都为 2.0,这是一个浮点数,当尝试输出 scores 列表中索引号为 10 / 5的数据,因索引序号为浮点数,程序会返回“TypeError: list indices must be integers or slices, not float”的出错提示,提示用户“列表的索引必须是整数,不能是浮点数”。

In [7]:
scores = ['李明', 84, 80, 95, 88, 76, 65, 85, 98, 55]
print(scores[10 / 5])
# 输出:TypeError: list indices must be integers or slices, not float
80

浮点数的整除结果是一个值等于整数的浮点数,也不可以做索引序号。

In [9]:
scores = ['李明', 84, 80, 95, 88, 76, 65, 85, 98, 55]
print(10 // 5)  # 运算结果为2.0,浮点数
print(scores[10 // 5])
# 输出:TypeError: list indices must be integers or slices, not float
2
80
In [ ]:
# 以下用法可以得到整数,可用作序号
scores = ['李明', 84, 80, 95, 88, 76, 65, 85, 98, 55]

print(scores[int(10 / 5)])     # 除法结果取整
print(scores[10 // 5])         # 整数做整除
print(scores[int(10.0 // 5)])  # 浮点数整除后取整
# 输出:TypeError: list indices must be integers or slices, not float

索引得到的对象的数据类型取决于序列中对应序号的元素的数据类型。

如果索引结果仍是序列类型数据时,可以继续应用索引的方法获取元素中的数据。

In [ ]:
cv = ['李明', 35, ('博士','副教授'), [96, 92, 85]]  

列表 cv 中包含用3个逗号分隔开的4个元素,分别是:
字符串'李明'、整数 35, 元组('博士','副教授')和列表[96, 92, 85]。

In [14]:
cv = ['李明', 35, ('博士', '副教授'), [96, 92, 85]]

# 包含字符串、数字、元组和列表等多种类型数据的列表
print(cv[0])  # 序号为 0 的元素为字符串:'李明'
print(cv[1])  # 序号为 1 的元素整数:35
print(cv[2])  # 序号为 2 的元素为元组('博士', '副教授')
print(cv[3])  # 序号为 3 的元素为列表[96, 92, 85]
李明
35
('博士', '副教授')
[96, 92, 85]

其中包含的字符串'李明'、元组('博士','副教授')和列表[96, 92, 85]这三个元素都属于序列类型,可以继续应用索引的方法获取元素内部的数据。
如:'李明'[0]的值为'李'、('博士','副教授')[1] 的值为'副教授'。

In [15]:
cv = ['李明', 35, ('博士', '副教授'), [96, 92, 85]]
# 包含字符串、数字、元组和列表等多种类型数据的列表
print(cv[0])     # 序号为 0 的元素为字符串:'李明'
print(cv[0][0])  # 序号为 0 的元素中序号为0的元素为:'李'
print(cv[2])     # 序号为 2 的元素为元组('博士', '副教授')
print(cv[3])     # 序号为 3 的元素为列表[96, 92, 85]
print(cv[2][0])  # 序号为 2 的元素中序号为0的元素为:博士
print(cv[3][1])  # 序号为 3 的元素中序号为1的元素为:92
李明
李
('博士', '副教授')
[96, 92, 85]
博士
92

cv[2] 索引的结果是一个元组:('博士', '副教授'),元组仍是一个序列类型数据,仍可应用索引获取其中的元素。
cv[2][0]相当于:('博士', '副教授')[0]
元组对象('博士', '副教授')中序号为0的元素为:’博士’
所以cv[2][0]最终获取的数据为:'博士'。

实例 6.2 百分制转五分制

可以用字符串索引的方法实现百分制成绩转五分制成绩。

先将 区间0-100 划分为[0,10)、[10,20)、[20,30)、[30,40)、[40,50)、[50,60)、[60,70)、[70,80)、[80,90)、[90,100)、[100] 共 11 个区间。
各区间成绩分别对应五分制的“E、E、E、E、E、E、D、C、B、A、A”。
如果将落在各区间的数分别对 10 做整除,正好可以得到 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10共 10 个数字。
这些数字正好是字符串“EEEEEEDCBAA”中每个字符的序号。
根据用户输入的分数整除10,去掉其个位上的数字,得到其十位上的数字“i”,字符串中以“i”值作为索引的字符正好对应该分数段的五分制成绩。
例如,输入85时,85//10 的结果为 8,输出字符串中序号为 8 的字符“B”。

In [ ]:
score = int(input())            # 输入整数
degree = 'EEEEEEDCBAA'          # 序号0,1,2,3,4,5,6,7,8,9,10
if score > 100 or score < 0:    # 排除不合法数据
    print('Data error!')
else:
    i = score // 10
    print(degree[i])   # degree[i]索引返回其中序号为i的字符

以上程序也可以用条件表达式实现:

In [ ]:
score = int(input())
degree = 'EEEEEEDCBAA'
print('Data error!') if (score > 100 or score < 0) else print(degree[score // 10])

练一练1

实例6.2的程序要求输入必须为整数,否则会触发异常。修改这个程序,使之可以接收浮点数的输入并正确转为五分制输出。

In [ ]:
# 补充你的程序
测试用例: 输入: 99.9 输出: A

练一练2

继续修改这个程序,使之可以重复接收浮点数的输入并正确转为五分制输出,直到输入回车时结束程序。

In [ ]:
# 补充你的程序
测试用例: 101.5 Data error! 100 A 59.9 E 88 B