列表、字符串及字典——Python基本数据结构

Python 的强大之处就在于它有丰富的库以及各种方便的特性。这篇文章将会介绍 Python 中的几个内置类型——列表、元组、字符串及字典。

我是按照 Python3 来介绍,如果和 Python2 有不同的地方会特别指出的。并且,文中的代码如果有输出的部分,都是按照 Python3 语法进行。另外,如果代码中是以 >>> 开头的,就说明这段代码是我直接在交互模式下运行的,并且包含了输出。

List——列表类型

首先,我们来介绍列表类型。什么是列表呢?你可以认为列表是多个变量的集合,并且这个集合是有序的。你在以前遇到的类型可能都只是涉及单个元素,比如说 intfloat 之类的,这都只涉及了一个数字。但是列表不同,它可以用一个变量存储多个元素,如果你了解过其它语言,列表和数组是十分类似的。

一个列表是用一个方括号包围的,列表中的元素使用逗号分隔,例如下面这几个变量都是一个列表:

值得注意的是,一个列表可以不包含任何元素,就像变量 c 存储的列表一样,这样的列表叫做空列表

另外,一个列表内的元素不一定要有相同的类型,甚至列表本身也可以作为元素存储在一个列表中:

比如说上面第一个列表,它存储着三个不同类型的元素,一个是整数,一个是浮点数,另外一个是字符串。

至于第二个列表,它包含四个元素,第一个元素是一个整数 233 ,第二个元素是先前的列表 x ,第三个元素是一个空列表 [] ,最后一个元素是另外一个仅包含一个字符串作为元素的列表。

列表的索引

那么,有了一个列表之后如何访问列表中的元素呢?

和其它语言中的数组一样,Python 中的列表中元素的标号是从 0 开始的。也就是说,列表中的第一个元素实际上被标号为 0,第二个元素被标号为 1,……,第 n 个元素被标号为 n - 1,……

如果最初接触它你可能会觉得非常奇怪,但是不用纠结太多,你可以把它当成一个规定记下来。在使用过程中你会发现这个规定是多么合理和方便。

说了这么多,我们拿前面的那个列表 x 来举一个例子。

你可以用 x[0] 来访问它的第一个元素,用 x[2] 来访问它的第三个元素。也就是说,在列表名字后跟着一个方括号,括号内可以是一个表达式(通常被称为下标),它的值就是要访问的列表元素的标号。

现在来看最后一行,方括号内是一个负数!如果在列表名字后的方括号中给出的表达式的值是一个负数,那么就表示从列表尾部开始计数,-1 就表示最后一个元素,-2 就表示倒数第二个元素…… 这时候你就会发现第一个元素标号为 0 的合理性了。

那么,如果要访问的元素不存在怎么办?也就是如果列表只有三个元素,但是给出的下标却是 3 或者 -4,就像这样 x[3] 。事实上,这时候 Python 就会给出一个 IndexError,告诉你你给出的下标有问题,就像这样(注意。这是我直接在解释器里运行的)

另外,列表中的元素也可以通过索引来直接修改:

列表的基本操作

Python 提供了一个名为 len 的函数来帮助我们获得一个列表的长度。

列表有一个名为 append 的方法,它可以在列表末尾插入一个元素。

还有两个方便的操作符来帮助我们判断一个元素是否在列表中—— innot in

例如变量 x 存储着一个列表 [1, 'hello', [2]] 。那么要判断 1 是不是在 x 中就是用 1 in x ,这个表达式会返回 True ,类似的 3 not in x 会返回 False ,表示 3 不在 x 中。

我们可以在 if 语句中使用这个操作符:

列表的比较

两个列表实际上是可以比较大小的。比如说要确定一个列表是否小于另外一个列表,可以直接用小于号来比较。

比如说要确定 A < B 是否成立。比较的过程是首先比较列表的第一个元素,如果 A[0] < B[0] 那么这就是 True ,如果 A[0] > B[0] 那么这就是 False 。否则继续比较第二个元素,直到能确定为止。如果列表 A 的长度小于列表 B 的,并且在整个列表 A 的元素都和 B 的比较完了之后还不能确定,那么就认为是 True 。相反的情况也是类似。

列表的排序

列表有一个 sort 方法可以将列表本身排序,另外还有一个 sorted 函数,它接受一个列表为参数并且返回一个已经排好序的列。默认情况下,这两个函数都是按照升序排序的。

这两个函数的排序算法都是使用稳定排序的算法,可能有些人会对此有兴趣。

列表的迭代

你可以用 forin 方便地遍历整个列表:

这段代码相当于

在不需要知道当前是列表第几个元素只想要遍历整个列表的情况下,这种写法是非常方便的。

事实上,有一个叫做 enumerate 的函数,它返回一个元组的列表(事实上是一个可迭代对象),每个元组的第一个元素是原列表的下标,第二个元素是原列表对应下标的值,有了这个函数就可以这样遍历这个列表了

列表的分片

Python 的列表有另外一个十分强大并且优雅的操作——分片,这个操作可以让你访问一个列表的某一段元素。

对于一个列表 x ,最基本的分片运算是这样的 x[start : end] 。这个操作创建了一个新的列表,这个列表是 x 中从标号为 start 的元素开始到标号为 end前一个元素这段区间。注意 end 这个元素是不包含在分片里面的。

注意上面 end-1 的情况,先前我们说过,如果标号是负数,则表示从末尾开始计数, -1 表示最后一个元素。由于分片不包含 end ,因此在上面它是 -1 的时候你会发现最后一个元素没有办法进入分片中。事实上,如果要求切割到列表的末尾,我们可以省略 end 。也就是说, x[start:] 这个分片表示一个从标号为 start 的元素开始到整个序列末尾的列表。

另外,如果省略 start ,例如 x[:end] 则表示从第一个元素开始,到 end (但不包含)的列表。

如果你需要拷贝整个列表,可以直接省略 startend ,就像这样 x[:]

如果 startend 大或者和它相等,那么你会得到一个空的列表。如果 end 大于或者等于 len(x) ,那么这就相当于省略了 end

除了最基本的分片以外,我们还可以选择分片的步长: x[start : end : step] 。在默认情况下,步长是 1 。分片是按照 step 来遍历从  startend 之间的所有元素的。例如在步长是 2 的时候。

也就是,从 start 开始,到 end 为止,分片操作将每 step 个元素提取一个出来生成一个新的列表。

步长不能为零,但是它可以是负数。当步长是负数的时候就表示从右向左提取元素。

在步长是负数的时候, start 是要大于 end 的。这时候如果要得到正确的结果可能需要稍微动些脑筋。

列表的分片赋值

我们还可以利用分片操作对列表的某一段进行修改,例如有一个列表 x = [1, 2, 3] 。我们知道 x[1 : 2] 表示的是分片 [2] ,我们可以直接对它进行赋值,例如 x[1 : 2] = [5, 6] ,这会将这段替换为 [5, 6] ,最后得到的列表会是 [1, 5, 6, 3]

由于替换分片的内容没有必要和分片的长度相同,所以这样的操作可以改变列表的长度。

如果你将一个空的列表赋值给某个分片,就相当于在列表中删除这段分片。类似的,有一个 del 运算符,它可以用来删除列表的某个元素或者一段分片:

列表的加法和乘法

如果你想把两个列表相连接,你可以使用 + 操作符,假设有两个列表 xy ,那么 x + y 就相当于一个新的列表,这个新列表的前一部分是 x 的内容,后一部分是 y 的内容。

类似的,列表可以和一个整数 n 相乘,如果 n 是正数,那么相当于获得一个新的列表,这个列表相当于将原列表重复 n 次;如果 n 不是整数,那么相当于获得一个空的列表。

这个操作可以用来让你生成一个占用 n 个空间的列表,比如说, [2] * 10 可以生成一个占用 10 个空间,并且每个元素都是 2 的列表。另外,你也可以用 [None] * 10 来初始化一个占用 10 个空间的列表。

其它的列表运算

包括前面已经介绍的运算,以及一些将要介绍的运算,我们来归结一些不会改变列表元素的运算:

x in L 如果 xL 中,返回 True ,否则返回 False
x not in L 如果 x 不在 L 中,返回 True ,否则返回 False
L1 + L2 连接两个列表 L1L2
L * nn * L n 个列表  L 相连接
len(L) 获取列表 L 的长度
max(L) 返回列表 L 中最大的那个元素
min(L) 返回列表 L 中最小的那个元素
L[i] 返回列表 L 中标号为 i 的元素
L[i : j] 获得列表 Lij 的一个分片
L[i : j : s] 获得列表 Lij 步长为 s 的一个分片
L.index(x, i, j) 返回列表 L 中在 i 及之后到 j 之前(但不包括 j )这一段中值为 x 的元素的位置。如果不存在,则会产生 ValueError。这里 ij 用是可以省略的。
L.count(x) 返回列表 L 中值为 x 的元素出现的次数

另外,还有一些会改变列表内容的操作(分片赋值和 del 运算符我就不列出了):

L.append(x) 在列表 L 的末尾插入元素 x
L.extend(A) 在列表 L 的末尾插入列表 A 中的元素
L.insert(i, x) 在列表 L 标号为 i 的位置后插入元素 x ,这等同于 L[i : i] = x
L.pop(i) 返回并且删除列表 L 标号为 i 那个位置的元素,相当于 del L[i] 。如果 i 省略,那么就相当于返回并且删除列表最后一个元素
L.remove(x) 删除列表 L 中第一个值为 x 的元素
L.clear() 清除整个列表 L ,相当于 L = []
L.reverse() 将列表 L 翻转
L.copy() 创建一个列表 L 的浅复制,相当于 L[:]

Tuple——元组类型

在 Python 中有一个很类似列表的类型,叫做元组。元组的定义和列表类似,只不过它是用一对圆括号包围的。例如 (1, 23) 。你可以轻易将一个列表或者字符串转换为元组:

当然有时候元组也可以省略包围它的圆括号

不过元组没有什么太多的操作,只有一些分片和访问元组元素的。和列表不同,元组是不可变的,你不能改变元组的元素。

元组在字典中可以当作键使用,因为它是不可修改的。

不过元组有一个好处就是可以让你打包赋值变量:

Str——字符串类型

字符串常量

在 Python 中,字符串是一个很常用的类型,一个字符串可以用单引号或者双引号包围,这两者没有区别。例如 'AA'"AA" 是一样的。如果在一个用双引号包围的字符串中要出现双引号,那需要使用 \" 。就和换行是用 \n 表示一样, \ 表示转义符。如果要打出转义符本身,则需要用 \\

你可以直接将两个字符串常量连在一起写,达到拼接他们的目的

但是,不能将两个字符串变量像这样拼接在一起

长字符串

有时候,你会需要非常非常长的字符串,并且可能会跨越多行,这时候你可以用三个单引号来包裹它们

这将会输出

长字符串的好处就是你可以不需要用反斜杠来转义单引号和双引号。

原始字符串

还有一种叫做原始字符串,这种字符串表示方法在书写正则表达式或 Windows 的路径的时候是十分有用的。原始字符串以 r 开头,你可以在其中放入任何字符,它不需要使用转义符。但是,引号仍然需要转义,只不过在输出的时候转义符也会出现在字符串中

不过,你不能在原始字符串最后一个字符(也就是引号前面那个)写上反斜线。

字符串的基本操作

字符串和一个列表很像,前面那些不会改变列表的运算(比如求最大值、最小值、字符串长度等)都可以用在字符串上:

不过, innot in 运算稍稍有些不同,他们可以判断某个字符串是不是另一个字符串的字串:

常用的字符串操作

str 有一个和 index 很类似的查询字串位置的方法,叫做 find 。在调用 index 如果没有找到字串,那么会产生一个 ValueError。但是如果是 find 方法,没有找到字串就会返回 -1 ,这在某些时候或许会更加方便。

当然,如果你仅仅是需要判断一个字符串是否在另一个字符串中,建议使用 innot in

find 方法相似,有一个叫做 rfind 的方法,它查找的是字符串中某字串最后一次出现的位置。

还有几个很常用的方法,我就不一一介绍了,用表格的形式给出

s.startswith(prefix, start, end) 如果字符串 s 是以 prefix 开头,那么这个函数返回 True ,否则返回 Falsestartend 两个参数可以省略,如果没有省略则类似于 s[start:end].startswith(prefix)
s.endswith(suffix, start, end) 类似 startswith ,只不过它是判断字符串是否以 suffix 结尾
s.strip() 返回字符串 s 的拷贝,这个拷贝删除了 s 头尾的空白符
s.lower() 返回字符串 s 的拷贝,这个拷贝将 s 中的字符全部转为小写
s.upper() 类似 lower ,只不过拷贝将 s 中字符全部转为大写
s.replace(old, new) 返回字符串 s 的拷贝,这个拷贝将 s 中所有的字串 old 替换为 new

split 和 join

字符串还有两个很常用的方法 splitjoin

split 方法把字符串分割成多个部分,这些部分构成一个列表。比如说

默认情况下, split 是按照空白符来分割字符串的,并且,如果有多个相连的空白符也视为只有一个。但是 split 是可以指定分隔符的,比如说

在你指定了分隔符的情况下,多个相邻的分隔符就不会像空白符那样被视为一个了。

还有一个是 join 方法,它有点类似 split 的逆向操作。这个方法将一个列表(或者可迭代的对象)用某个字符串作为分隔符相连接。

不过,这个方法要求列表里的内容都是字符串。但是如果我们里面元素有一个不全是字符串,但是可以全部转换为字符串的列表怎么办呢?我们可以用一个叫做 map 的函数。

字符串的格式化

字符串有一个独有的运算符 %,它被用来格式化一个字符串。

考虑一下我们现在有一个表示年份的变量 year ,表示月份的变量 month 和表示日期的变量 day 。现在要打印时间,我们可以这样做

这里都是用 '-' 来连接的,你可能会这样写 print('-'.join(map(str, [year, month, day]))) 。但是,无论哪种写法都有不方便之处。

现在来看看使用字符串格式化的 % 运算符的写法把,这可以写成

左边的字符串表示格式串,它由一些普通的字符和一些特殊的占位符组成。占位符是以 % 开始的,例如 %d 就表示一个数字的占位符,表明这里应该是一个整数。 % 运算符的目的也就是用这个运算符右侧的一些变量来替换占位符以生成一个新的字符串。如果你要输出 % 本身,你需要用 %% 来输出。

% 右侧是一个元组,这个运算符会将左边格式串的第一个占位符替换为元组中的第一个元素,第二个占位符替换为元组中的第二个元素……

占位符除了单纯地指定类型以外,还可以指定一些额外的东西,比如 %04d 则表示这应该是一个数字,如果这个数字长度至少是 4 ,如果不足就补零。

另外还有几个控制格式的占位符, %+d 会强行在数字前加上它的符号, % 5d 表示这个数长度至少为 5 ,如果不足则在左侧补上空格,类似的, %-5d 表示这个数长度至少为 5 ,如果不足就在右侧补上空格。当然, %+ 5d 就是前面两种格式化方法的混合

%d 用来标识整数,对于浮点数,我们有三种方法来表示它,其中 %f%e ,分别表示输出普通的浮点数、科学计数法表示的浮点数。还有一种比较复杂是 %g ,如果指数部分小于 -4 或者大于或者等于指定精度,那么就使用科学计数法表示,否则就用普通方法表示。

浮点数实际上可以指定输出精度的,例如保留到小数点后 5 位就是 %.5f ,当然也可以混合前面整数的格式化方法, %07.3f 表示长度至少为 7 ,如果不足前面补零,并且输出的数保留到小数点后 3 位。

另外,如果是普通的字符串,就用 %s 作为占位符。当然,前面整数的格式化方法也可以用到字符串上,例如 %05s 。注意如果元组里对应位置的元素只要是可以使用 str 函数转换为字符串就可以使用 %s 作为占位符。另外, %r 只要是能够使用 repr 转换的就可以作为占位符。 %x 以小写输出十六进制数字, %X 以大写输出十六进制数字。

格式化的时候还可以给占位符指定一个名称,之后操作符号后给出的变量将元组改为字典(字典类型之后会介绍)。

名称用圆括号包裹,直接跟在 % 后。指定名称的好处就是一个变量可以被使用多次。而且在传入的时候不需要按照顺序。

Dict——字典类型

回想一下刚刚的列表类型,你可以将一些值映射到一些索引中。但是,如果你的索引是诸如字符串之类的类型列表就无能为力了。

字典是 Python 中一种可以通过名字来索引值的数据结构。这种结构通常被成为映射(mapping)。在 Python 中,唯一内建的类型就是字典。

你可以把字典当作一种数据库来使用,我们先来看看字典的功能吧!比如说我们有一个记录了学生成绩的字典,现在要实现一个查询成绩的功能。

'DKF''AKF' 这样的索引叫做字典的(key),通过键索引到的叫做字典的(value)。字典的键可以是一个数,一个字符串,甚至可以是一个元组!

如何创建字典

如你所见,字典是使用花括号包围的,和空列表类似, {} 就表示一个空的字典。

字典里的元素是一个键/值对,在创建字典的时候可以直接指定一些字典里的元素,每个元素是像 key : value 一样的形式,元素与元素之间用逗号 , 分隔。

你还可以通过一个列表来创建一个字典,这个列表的元素应该是一个二元组 (key, value) 表示相应的键和值,然后通过 dict 函数来创建,例如

dict 函数还有一个很方便的创建字典的功能,那就是直接通过参数创建

或者,如果你有一个 key 的键列表,一个 value 的值列表,也可以通过 zip 函数将它们组合起来,再传给 dict 函数创建字典

字典的基本操作

和前面列表类似,也可以通过 len 函数来查询字典有多少个元素,要访问字典的元素也十分简单,例如有一个字典 d ,要查询键为 key 的元素的值直接输入 d[key] 即可。但是,如果字典中原本不存在键为 key 的元素,那么这个操作会产生一个 KeyError

不过,即使不存在键为 key 的元素,如果你直接用 d[key] = value 对字典操作,是不会产生错误的。这个操作如果存在键为 key 的元素就将它的值改为 value ,否则就创建一个新的元素。

如果你要判断字典里是否存在键为 key 的元素,可以使用 in 运算符,例如 key in d 就是判断字典 d 里有没有键为 key 的元素,像上面那个成绩查询的小程序一样。千万记住, in 判断的是键,而不是值!

字典还有一个很方便的方法叫做 get ,它可以查询字典里对应键的元素的值,如果不存在这个元素会返回 None ,但是,你可以指定一个缺省的值,如果不存在,就返回你指定的值。

还有一个类似的方法,叫做 setdefault ,如果指定的键存在,那么它会返回这个键对应的值,否则就创建一个新的元素,将这个元素的值设置成给定的缺省值并返回。默认值可以省略,如果省略就表示 None

字典的迭代

和列表类似,你可以直接通过 forin 语句来对字典的键进行遍历

这将会输出

如果你想要类似使用 enumerate 遍历列表时一样,对键和值一起进行遍历,可以使用一个叫做 items 的函数,在 Python3 中它返回的是一个叫做 view 的东西,类似与前面创建字典使用的键值对的列表,具体可以参看官方文档:https://docs.python.org/3/library/stdtypes.html#dict-views

使用这个函数可以将上边的代码改写为

还有两个类似的函数叫做 keysvalues ,它们分别返回字典中键的 view 和值的 view

Python2 中的不同:在 Python2 中, keys 返回的是键的列表, iterkeys 会返回针对键的迭代器。如果仅仅需要迭代的话, iterkeys 会更快一些。另外两个函数 itemsvalues 也类似。

其它字典方法

字典还有一个 popitem 方法,和列表的 pop 方法类似,但是由于字典中的元素没有顺序这种概念,它会删除并返回字典中的任意一项。

还有一个叫做 pop 的方法,它用来获取给定键的值,并且将这个元素删除。

Miskcoo's Space,版权所有丨如未注明,均为原创
转载请注明转自:http://blog.miskcoo.com/2016/07/python-fundamental-data-structures

miskcoo

顺利从福州一中毕业!感觉大学周围都是聚聚十分可怕QAQ 想要联系的话欢迎发邮件:miskcoo [at] gmail [dot] com

Leave a Reply

Your email address will not be published. Required fields are marked *

NOTE: If you want to add mathematical formulas, use $$ to wrap them. For example, use $$x_0$$ to get $$x_0$$.

If you want to get a newline, hit Enter twice, that is, use double newlines to get a newline.