Python中的序列化数据是一种重要的数据类型,主要包括前面章节中见到的字符串string数据, 以及列表list数据、元组turple数据、字典dict数据、集合set数据等。本章将引导同学们了解上述几 种常见的序列化数据类型的特点、常见的针对序列化数据的处理方法等,并简介推导式及其用法。 学习本章内容时,要求: . 理解有序序列和无序序列;对于有序序列,掌握对其进行排序的相关方法; . 理解可变序列(不可哈希)和不可变序列(可哈希)的概念;对于可变序列,掌握对其元素进行赋 值等的相关方法; . 掌握字符串类型数据的概念、特点及基本操作方法; . 掌握列表类型数据的概念、特点及基本操作方法;掌握元组类型数据的概念、特点及基本操作方 法;掌握列表和元组的异同点; . 掌握字典类型数据的概念、特点及基本操作方法;掌握集合类型数据的概念、特点及基本操作方 法;掌握字典和集合的异同点; . 掌握列表推导式、元组推导式、字典推导式、集合推导式的用法。 3.1 概 述 3.1.1 序列化数据 在程序设计中,除了常见的基本数值类型(如整型int、浮点型float、布尔型bool等)外,有时还要处 理序列化数据。顾名思义,“序列”指按特定顺序依次排列的一组数据,它们可以占用一块连续的内存 空间,也可分散到多块空间中。本章将讨论序列化数据中的字符串、列表、元组、字典、集合。 有的同学可能会有疑问:基本数据类型就能解决很多问题了,为什么还要学习使用序列化数据呢? 这是因为在很多场合中,仅使用基本数据类型并不方便。很多时候,当需要保存一批数据时,使用序列 70 化数据类型是十分方便的。例如,在如下几种情况中,单纯使用数值型数据是不合适的。 . 全班同学的姓名(如晓明、小红、小兵……)适合用序列化数据中的字符串类型数据表示,适合用序 列化数据中的列表、元组等存储。例如定义一个列表a[“晓明”,“小红”,“小兵”],可以通过类似 于学号的索引号找到某个同学的姓名,如a[0]中存储“晓明”,a[1]代表“小红”,a[-1]代表“小兵” 等;如果没有重名的同学且存储是无序的,则使用序列化数据中的集合类型也可以表示。 . 全班同学期末考试的平均分数,如“晓明”考了89分,“小红”考了92分,“小兵”考了90分,适合 用键-值对(key-value)数据类型表示,其中姓名作为键(key),分数作为值(value),适合用序列 化数据中的字典存储,例如:{“晓明”:89,“小红”:92,“小兵”:90}。 . 期末考试的科目(如数学、物理、化学、语文)适合用序列化数据中的元组表示,因为科目名是不能被修 改的;若要求有序,可以用元组表示;若要求无序、无重复科目,也可用集合存储这些科目。 序列化数据在日常生活中其实是很常见的,高中数学课上学到的各种数列(如等差数列、等比数列 等)就是一种序列化数据。可见,对于上面的需求,无法用基本数据类型中的int、float、bool等表示。 此时,就需要使用序列化数据中的字符串、列表、元组、字典、集合等序列化的数据类型了。 3.1.2 推导式 推导式(或称为生成式,本书后续不再区分这两种称呼)是指可以从一个数据序列构建另一个新的 数据序列的高效的结构体,推导式的使用也是Python这门语言的一大特色,Python能用简洁的推导 式完成其他语言需要很多行代码才能实现的功能。 Python有几种常用的推导式,如生成器推导式(生成结果是生成器对象)、列表推导式(生成结果是列 表对象)、元组推导式(生成结果是元组对象)、字典推导式(生成结果是字典对象)、集合推导式(生成结果 是集合对象)。例如,列表推导式是形如“[exprforvalincollectionifcondition]”的形式简洁、内涵复杂的 具有Python特色的表达式。由于其定界符是方括号,因此这是一个列表推导式,它表达的含义是可构造 这样一个列表:用变量val依次遍历可迭代对象collection,找出满足条件condition的元素后,经expr表达 式或相关函数的变化后,输出以列表表示的结果。例如,从键盘输入一句英文句子,将单词长度大于3的 所有单词转换为大写字符输出,可以非常简洁地用图3.1所示的一条语句(第2行代码)完成任务。这里 的expr是upper()方法,collection是输入的英文句子中的各个单词,condition是len()>3。 图3.1 列表推导式示例 3.2 序列化数据的主要特点和常用内置函数 3.2.1 主要特点 除字符串外,序列化数据中的列表、元组、字典、集合的相同之处是其中的各个元素之间都是用逗号隔 开的。不同之处有不少,最直观的是它们的定界符是不一样的:列表用方括号[]作为定界符,元组用圆括 号()作 为定界符,字典和集合均用大括号{}作 为定界符;字典中的元素是用冒号:隔开的键-值对,其中键 (key)可以理解为类别(如一个人的姓名或一个科目名称), 而值(value)可以理解为这个类别的一个取值 (如某人的身高为180cm)或一组取值(如某科目的全班同学的考试成绩[90,80,88,92]) 等。 请你分别定义一个列表x和一个元组y(注意列表元素是用中括号括起来的,而元组元素是用圆 括号括起来的)。用type(x)和type(y)方法分别显示它们的类型。 序列化数据分为有序(如字符串“你吃”和“吃你”是不一样的)和无序(如集合中的诸多元素之间就 是无序的)数据。对于诸如字符串、列表、元组等有序数据来说,其数据存储是一个接一个地有序排列, 每个元素都拥有一个对应的值,代表它存储在序列中的某个位置,可以用索引定位其中的某个元素,而 对无序数据(如集合中的数据)是不能用索引号访问的。打个比方来说,钢琴一共有88 个有序排列的 琴键(琴键相当于数据), 这88 个有序的琴键能在五线谱上找到它们一一对应的确切位置,五线谱中的 高低位置就相当于索引号。因此,当看到五线谱某个位置上(索引)的符号后,就能够准确找到对应的 琴键(数据)。对于有序数据来说,它们大多支持双向索引(如字符串、列表、元组等都支持双向索引), 即它的第一个元素的索引(下标)为0,第二个元素的索引(下标)为1,最后一个元素的索引(下标)为-1, 倒数第二个元素的索引(下标)为-2,以此类推。 图3.其中le可获取字符串的长度,其索引从0开始;也可 2所示的例子显示了字符串的有序性, n() 使用enumerate() 方法,它用于将一个可遍历的数据对象(如列表、元组或字符串)组合为一个索引序 列,同时列出数据下标和数据本身,2中的第4行代码的迭代变量是ix和u两个变量。 因此图3.d 图3.字符串的索引与有序性示例 2 可以用方括号、圆括号、花括号分别定义列表、元组、字典、集合;使用lt()、pe()、t()、t() 也 istuldicse-is 可将数据转换为列表、元组、字典(需要键值对数据)、集合,但此时使用圆括号。请注意:①没有lt[]、 dict{}、set{} 的用法;②在进行索引(切片)时,列表、元组、集合元素都使用方括号,不使用圆括号和花括号。 按序列中的元素是否可变,又分为可变序列和不可变序列两类。如字符串就是一个不可变序列, 一旦定义后,其内容是不可改变的。请注意,字符串也是有替换方法replace() 可以使用的,只不过完成 替换操作后,要记得将其赋予另外一个新的字符串变量,原始字符串即使使用了replace(), 它也是不变 的,如图3.所以它是可变序列。 表3.3所示。列表中的元素是可以修改的, 可哈希” thon内置函数hash() 1给出了对常见序列化数据的说明。其中的“ 是指可以使用Py 得出其哈希值,即对于一个对象a,如果hash() 返回一个整型值(哈希值), 则a就是可哈希的。限于本 书的科普性质,这里先不介绍哈希函数,只需要知道列表、字典、集合这些可以增加元素、删除元素、修 改元素的可变对象属于不可哈希对象;元组、字符串这些不可变对象属于可哈希的对象。可以使用内 置函数hash() 如图3. 计算一个对象的哈希值,4所示。 71 图3.字符串的不可替换性 3 图3.使用hh() 判断是否可哈希 4 as 表3.字符串、列表、元组、字典、集合数据的主要特点 1 字符串str 列表list 元组tuple 字典dict 集合set 定界与分 隔符 单引号如a=‘ xyz’ 双引号如a= “ xyz ” 三引号如a=‘‘‘ xy’’’ 元素间无分隔符 中括号如[x,y,z] 元素间用逗号隔开 小括号如(x,y,z) 元素间用逗号隔开 大括号,如{x:v1, y:v2,z:v3} 元素间用逗号隔开 大括号,如 {x,y,z} 元素间用逗号 隔开 有序? 有序 a[0]a[-1] 有序 l[0]l[-1] 有序 t[0]t[-1] 无序无序 可变? 不可变 (可哈希) 可变 (不可哈希) 不可变 (可哈希) 不可变 (可哈希) 可变 (不可哈希) 可重复? 是是是 键不可重复 值可以重复 否 根据表3.序列化数据的特点分类如图3.所有元素占用 1, 5所示。列表和元组都按顺序保存元素, 一块连续的内存空间,因此每个元素都有自己的索引,可以通过索引访问;列表和元组的区别在于列表 元素是可以修改的,而元组是不可修改的。字典和集合存储的数据都是无序的,其中字典元素以键-值 对的形式保存。 1 yp 例3.分别定义列表、元组、字典、集合类型数据。使用te() 方法显示序列化数据的类型;使用 索引访问有序序列的某些元素;对于可变对象,修改或增加其元素值;对于不可变象,通过hash() 显示 其哈希值。 【提示与说明】6所示为代码实现。请注意以下几点:①定义列表时可以直接用方括号定 图3. 义,列表中的元素类型可以混杂存在,可以在列表中再嵌套列表、元组等,如第1行代码是在列表中又 嵌套的列表、元组;第2行代码定义元组时也是在内部嵌套了列表、元组、字符串,这四种序列化数据的 类型可通过type() 方法得到;②有序序列(如列表、元组等)可通过索引访问其中的有序元素,如第8、9 行代码所示;③对于可变序列,可以增加新的元素、修改已有的元素,请注意在列表、集合、字典中增加 72 图3.序列化数据的特点分类 5 新元素的方法———列表用append() 方法、集合用add() 方法,如第12 、13 行代码所示;④对字典中的元 素可以直接增加新值(如第14 行代码所示), 也可以修改键-值对结果;⑤列表、集合这些可以增、删、改 元素的对象,属于不可哈希对象;元组、字符串这些不可变对象属于可哈希对象,可以使用内置函数 hash() 计算一个对象的哈希值,但如果试图计算不可哈希对象的哈希值,则会抛出异常。 图3. 64 种序列化数据的比较 另外,序列化数据之间是可以相互转换的。例如一个字符串可以方便地转换为列表、元组、字典、 集合———可以用list()、tuple()、dict()、set() 实现相应数据类型的转换。需要注意的是,字典元素是 键-值对,单纯的“键”是不能成为字典元素的,因此需要“值”和它匹配,此时可以使用zip() 函数,相关示 例详见例3. 2 2。 ang例3.对于给定的字符串以及由re() 产生的一系列数字,分别将其分别转换为列表、元组、字 典、集合数据类型。 【提示与说明】本题是使用list、tuple、dict、set等方法将相应数据转换为列表、元组、字典、集合等 73 的示例,代码实现如图3.7所示。其中,第2、3行代码分别演示list() 、tuple() 转换为列表、元组;第4、5 行代码是使用dict() 转换为字典的情况,注意此例中zip() 的第一个变量是键,第二个变量是值,由于第 4、5行代码的zip() 的两个变量是不一样的,因此最后结果也是不同的。第6行代码是使用set() 转换 为集合,注意转换为集合后去掉了重复元素“l”且结果已变得无序,这也印证了表3.1中集合的不可重 复且无序的特性。另外,e() 对象也可以转换为特定类型的列表、元组、字典、集合等,进行类型转 换时可以使用map() 方法,详见第11~14 行代码。 rang 图3.字符串向其他序列化数据类型的转换 7 请仿照上面的例子,使用tuple()、str() 将一个列表中的内容转换为元组、字符串。 例3.列表中的元素可以是各种不同类型的数据。请利用lt[] 方法定义一个含有多个不同类 3 is 型数据的列表;利用for循环分别显示这个列表中的各项内容。 【提示与说明】此题是为下面将要介绍的列表进行预习,目的是了解列表中的元素可以是异构型 的。for循环的可迭代变量序列可以由list承担,8所示。从图中可看到, 代码实现如图3.列表中可以 嵌套由方括号表示的列表、由圆括号表示的元组、由花括号表示的集合等各种类型的数据。通过for循 环遍历时,会将其中的各种数据元素分别列出,请注意最后一行输出的集合元素与原始定义时的不同, 说明集合中的元素是无序的。 图3.使用fr循环遍历列表中的各个元素 8o 74 75 例3.4 定义一个内容为字符串的列表并修改其中的某个字符内容。类似地,试着定义一项内容 为字符串的元组,看看能修改它的值吗? 再定义一个字符串,看看能修改它的某个值吗? 【提示与说明】 分别定义一个列表和一个元组。由于它们都是有序的,因此按照其中某个元素的 索引,可以对列表中某个元素的值进行修改操作,实际运行效果如图3.9所示。可见,我们可以随意更 改列表中的元素,但无法对元组中的内容进行修改,这也印证了表3.1中元组不可变的特性。 图3.9 可变序列(列表)和不可变序列(元组) 3.2.2 常用内置函数 综上所述,序列化数据是一组存于连续内存或不连续内存区域的一组序列化的数据,一些内置函 数可应用于这些不同的序列化数据。表3.2给出了一些常用的内置函数。 表3.2 序列化数据的部分常用内置函数 函 数说 明 range(start,end,step) 返回从start(含)开始到end(不含)为止的一组以step为步长的序列化数 str(seq) 将数据seq转换为字符串 list(seq) 将数据seq转换为列表 tuple(seq) 将数据seq转换为元组 dict(seq) 将以键-值对表示的数据seq转换为字典 set(seq) 将数据seq转换为集合 sorted(seq) 对数据seq进行顺序排序 reversed(seq) 对数据seq进行逆序排序 len(seq) 求数据seq的长度 enumerate(seq) 迭代显示数据seq的索引号及其内容 例如,表3.2中的list()是将其他类型的数据转换为列表数据的方法,使用示例如图3.10所示。请 注意由于字典是键-值对数据,因此在转换为列表时,直接将字典变量作为list()的参数是将键-值对中 的键转换为列表元素;字典变量的values() 方法是将键-值对中的值转换为列表元素,items() 方法是完 整的键-值对数据。关于字典序列化数据的特点,本章后续会介绍。 图3.将其他序列化数据转换为列表数据 10 5 2中函数的使用方法, tr()、listule()、dicset() 转化的方法;使用sorted()、reversed() 排序并迭代输出排序后的效果;针对列表和元组,完成切片 例3.针对表3.验证使用st()、pt()、进行数据类型 操作。 代码实现如图3.t() 【提示与说明】11 所示。序列化数据及其可迭代对象的数据对象均可使用lis 函数转换为列表。请注意:①len() 方法不仅可用于字符串,其他序列化数据均可使用;②在使用dict() 函 数将序列化数据转换为字典类型数据时,数据要以键-值对的形式出现,此时使用zip() 函数是常用方 法,但要注意“键”不能重复,且无法被匹配上的数据将会舍弃,注意字符串a的长度是11,去重后的键 只有8个(空格也算一个), 因此mydict的长度是8;若调用zip() 时将两个变量的顺序调换(参见第5 图3.针对序列化数据的部分常用内置函数使用示例 11 76 77 行代码),则键-值对就完成了互换,mydict2的长度是11,此时键已经变成0、1、2等数字了,也就不再有 重复的键了;③集合元素是不能重复且无序的,参见集合处理后的输出结果;④第9行代码的reversed() 可完成对原始字符串的逆序排序,但这并不影响原始字符串本身,注意第15行代码的输出结果是排序 后的结果,而第18行代码则是原始结果;⑤enumerate()函数返回两个结果,分别是索引号及其对应的 内容,因此在第16行代码的for循环中需要两个循环变量;⑥使用max()、min()函数计算最大值和最 小值时,若自变量是字符串等非数值型数据,则按字符本身的编码值进行计算,不同编码方式,结果可 能不同。 例3.6 分别将给定的range对象,字符串,集合,字典中的键、值、键-值对转换为对应的列表。定 义一个列表,使用del删除它。 【提示与说明】 可以使用内置方法list()完成其他数据向列表的转换;可以使用del()删除列表。 注意:list()没有返回值,可用print()语句将其显示出来。实际运行效果如图3.12所示。列表被删除 后不能再继续使用了,参见图3.12中最后的出错信息。 图3.12 使用list()完成对其他序列化数据的列表转换示例 3.3 字 符 串 3.3.1 基本特性 我们已经在第1、2章见过很多有关字符串的操作。除了支持序列化数据的通用操作(如双向索 引、比较大小、计算长度、元素访问、切片、成员测试等)外,字符串类型数据还支持一些特有的操作方 法,如在print() 、format() 中使用过的字符串格式化、字符串的查找和替换操作等。字符串属于不可变 (可哈希)序列,所以不能直接对字符串对象进行修改操作。字符串支持切片操作,切片操作也只能访 问其中的元素而无法修改其中的字符,即使字符串对象提供了更新方法如replace(), 但这也不是对原 字符串直接进行“原地”修改和替换(因为它是不能修改原值的), 而是返回一个新字符串作为替换后的 结果,原来的字符串保持不变。 下面通过一些例题和实际操作介绍字符串的特性。 例3.对于给定的字符串,将其转换为列表后看看能否修改其值,再看看能否直接修改原字符串 7 中的某个字符。 【提示与说明】字符串可通过list() 方法转换为列表,之后可以对列表内容进行修改,但字符串本 身的内容是不能修改的。图3.说明它是不 13 给出了针对上述要求的结果。第2行代码计算其哈希值, 可变的数据类型,因此第9行代码会出错( 13 中最后一行的出错提示,1体现的 请参见图3.这也是表3. 字符串不可变特性的一个示例); 但用第3行代码将其转换为列表后,就可以用第5行代码的方法修改 其中的某个值了。 图3.字符串的内容不可变、不可修改特性 13 例3.字符串是有序序列,因此它支持双向索引、切片、计算长度、元素访问、成员测试等。请设 计一个字符串,通过切片显示子串;通过for循环有序地显示各位置上的字符;判断某个字符是否在字 符串中。 【提示与说明】14 所示。注意第2~它也支持双向索引。可以 8 代码实现如图3.8行的切片方法, 78 79 利用字符串的有序性,通过for循环顺序显示各个字符,可以使用in操作符进行成员测试。详见图3.14中 的代码注释。 你能仿照图3.14中的例子,把给定的字符串通过for循环反向显示出来吗? 图3.14 字符串的有序、双向索引等特性 3.3.2 常用的字符串内置方法 下面介绍字符串常用内置方法的使用。假设给定字符串变量mystr,表3.3中的各个内置方法可 以完成相应的操作。 80 表3.3 字符串的查找、索引、计数、编码、解码等方法 方 法说 明 mystr.find(givenstr,beg=0,end=len(string)) 以及mystr.rfind(givenstr,beg=0,end=len (string)) find:从mystr左侧开始检测指定的字符串givenstr是否包含在其 中,如果包含,则返回其开始时的索引位置,否则返回-1;如果指 定了从beg开始到end结束的范围,则在这个范围内进行检测 rfind:从mystr右侧开始检测,方法同find mystr.index(givenstr,beg=0,end=len(string)) 以及mystr.rindex(givenstr,beg=0,end=len (string)) index:从mystr左侧开始检测指定的字符串givenstr是否包含在 其中 rindex:与index用法类似,只不过是从尾部(右侧)开始检测 mystr.count(givenstr,beg=0,end=len(string)) 返回指定的字符串givenstr在给定的mystr中出现的次数。如果 有beg或者end区间参数,则返回在指定区间内出现的次数 mystr.encode(encoding='UTF-8',errors=s' trict') 以encoding参数指定的编码格式编码mystr字符串;errors是设置 不同错误的处理方案,默认为s' trict' mystr.decode(encoding='UTF-8',errors=s' trict') 以encoding参数指定的编码格式解码mystr字符串;errors是设置 不同错误的处理方案,默认为s' trict' 例3.9 针对表3.3中的字符串方法设计字符串,对某个字符进行查找、统计某个字符在特定范围内 出现的次数;分别将其按UTF-8、GBK进行编码和解码,观察一下按不同编码方式进行编码后的效果。 图3.15 字符串编码、查找、计数等方法的使用 【提示与说明】 可以先通过sys包中的getdefaultencoding()方法显示系统默认的字符编码方式 (注意:某个方法是在对象后使用点操作符调用的)。图3.15展示了上面几种方法的示例。第5、6行 代码是关于find() 、rfind() 的用法,即分别从左侧和右侧查找指定的字符;在count() 计数方法中,是可 以指定计数范围的(第8行代码,终点即字符串长度,可以使用求字符串的长度len() 方法实现); 第9、 10 行代码是字符串编码的方法。 表3.4字符串的大小写、拼接等方法 方法说明 mystr.islower() 以及 mystr.isupper() islower返回字符串是否为小写。如果字符串中包含至少一个区分大小写的字符且所有区分大小 写的字符都是小写,则返回逻辑真值True,否则返回逻辑假值False isupper返回字符串是否为大写。如果字符串中包含至少一个区分大小写的字符且所有区分大小 写的字符都是大写,则返回逻辑真值True,否则返回逻辑假值False lower() 以及 upper() 转换字符串中所有字符为小写 转换字符串中所有字符为大写 mystr.join(seq) 该方法接收一个序列参数seq,seq是要连接的元素序列、字符串等,即指定字符串mystr作为分隔 符,将其散播到seq所有的元素间 例3.针对表3.设计字符串判断它是否均为小写字符; 则 10 4中的字符串方法, 若不是小写字符, 将其转换为小写字符。以指定字符串作为分隔符,将它散播到另一个字符串中并显示这个新构成的字 符串。统计某个字符中的最大值、最小值、长度等。 【提示与说明】在字符串后面跟一个点运算符再接相应的方法即可使用。图3. 16 展示了上面几 个字符串常用内置方法的结果。ir()、r() 的用法参见图3.7行代码;n() sloweisupe16 中的第4、joi方法 可用于散播指定的字符到原始字符串中,用法参见图3.max()、min()、len() 方法 16 中的第9行代码; 不是用点操作符,而是在其后的参数括号中填写字符串,用法参见第11 行、第13 行代码。注意此例显 示出的字符串的不可变性。 图3.字符串部分内置方法使用示例 16 81 表3.5字符串的替换、分隔、开始或结束字符、字符串清洗等方法说明 方法说明 str.replace(old,new[,max]) 把字符串str中的old子串替换成新串new。如果指定了替换次数max,则替换次数最大不 超过max次 以子串s1为分隔符分割字符串str(此例中s1是空格); 如果给定了num 值,则仅截取num st.pit(1",num) rsls=" +1 个子串 startwith() 检查字符串str是否以指定子串substr开头:若是则返回True,否则返回False; beg=0,e(s) (r) (a) nd=?)(s) st.ttwth(btr, rsius 如果指定了区间范围beg和end的值,则在指定区间范围内进行检查 以及 endwith() 的用法与startwith的用法类似,只不过它是检测str是否以指定的子串substr beg=0,e(n) nd=?))(u) (s) st.dwih(sr, retbt 结尾 字符串清洗方法,即删除字符串头尾指定字符(默认删除头尾空格、回车符、换行符、Tb制 lstrip() strip() 表符等,也可指定删除特定字符)。stip方法去掉原字符串左右两边的空白字符后返回新的(a) rstrip() 字符串;rstrip和lstrip分别去掉字符串(r) 右边和左边的空白字符后返回新的字符串 例3.给定一个字符串。针对表3.完成如下任务:①将其中某些字符替换 11 5中的字符串方法, 为新的设定字符,并比较一下“指定次数的替换”和“不限次数的全替换”有什么区别;②分别以空格和 指定的某个字符为分隔符分隔原始的字符串;③判断某个字符串是否以某个特定字符开头;④完成数 据清洗。 【提示与说明】上述方法均使用点操作符,代码实现如图3.17 所示,主要代码后有说明文字,可以 参考。使用replace() 方法替换时可以指定次数,也可不指定;使用split() 方法时,若不写分隔的参数, 则默认以空格作为分隔符;将字符串迭代遍历并追加到列表的方法如第18~20 行代码所示。需要注 strip lstrip rstri 意的是,()、()、p() 方法均不对原字符串进行改变,而只是返回清洗后的结果,若需要保 留清洗后的结果,往往需要将其赋值给其他变量。 图3.替换、分割、开始或结束字符、字符串清洗等方法的使用示例 例3.从键盘输入一个英文句子,统计这句话中有多少英文单词,并计算这句话中每个单词的 17 12 thisisanexample”, 75 。 平均长度。例如,输入“ 需要输出单词数为4,这4个单词的平均字母数是3. 82 83 【提示与说明】 由于英文单词都是以空格分隔的,因此可以使用split()方法完成单词分隔并统计 数量(分隔字符就是空格);通过len()方法能迭代计算出每个单词的长度,通过计算,就能得到每个单 词的平均字母数。代码实现如图3.18所示。 图3.18 split()方法使用示例 3.4 列表和元组 3.4.1 列表和元组的主要异同点 列表的使用非常广泛,它是升级版的“数组”;元组可以看成“轻量级”列表,虽然它有和列表不一样 的特点(如元素的不可变性等),但从表面看,列表、元组内的各个元素都是以逗号隔开的。元组除了定 界符是圆括号外,好像与列表很相似。其实,元组与列表还是有区别的,主要区别是列表是动态的,可 增、删、改其元素;但元组是静态的,内容不可变。因此,如果定义了一系列常量值且仅是对它们进行遍 历而不能对其元素进行任何修改,可以使用元组而不用列表存储。在某些情况下(如在要求不能改变 数据本身等应用场合下)使用元组更合适、更安全。由于列表和元组有一些异同点,为方便同学们更好 地理解和区分二者的操作,这里将列表和元组放在一起介绍。 首先看看相同点:列表和元组都是Python内置的用来存储一连串元素的序列化容器,相同点 如下。 (1)都属于有序序列,从最左边第一个元素开始往右排序,序号分别是0,1,2,…;从最右边第一个 开始往左排序,序号分别是-1、-2、-3,…,详见图3.11的切片操作。但对于无序的集合来说是无法 进行切片操作的,这是因为集合的所有元素均没有索引,切片也就无从谈起。 (2)使用内置函数len()统计元素个数,max()求最大值,min()求最小值,sum()求数字元素和,运 算符in测试是否包含某个元素(返回一个逻辑值),count()统计指定元素的出现次数,index()获取指 定元素首次出现的位置;元组也可以像列表那样作为map()、filter()、zip()、reduce()等函数的参数。 (3)列表和元组中的元素可重复(这点与集合元素不同);元素可以是同一类型的,也可以是不同类 型的,例如可分别为整数、实数、字符串等基本类型,甚至可以是列表、元组、字典、集合以及其他自定义 类型的混合对象;可以在列表及元组中再嵌套列表、元组、字典、集合等序列化数据;可以分别使用list()和 tuple()方法分别将其他类型的数据转换为列表和元组,请参考图3.6和图3.11中的代码。 下面介绍列表和元组的切片方法。切片会返回列表或元组的某个元素“子集”。假设有一个名为x 的列表(或元组),x[start:end【:step】]会返回从start开始到end结束的、以step为步长的一个列表 “子集”。x[start:end【:step】]外层的中括号表示切片符,即使是元组,切片也用方括号而不用圆括 号;而其内部的step步长的中括号【】是可选项,表示step这个参数可省略不写,当步长step为空时, 默认step为1;其实,这里的起始start、终止end和步长step参数也都可以为空:当起始参数start为 空时,默认从列表(或元组)头开始(首位置的索引为0), 但当起始位置start大于列表(或元组)总长度 时会返回空;当终止参数end为空时,默认到列表(或元组)的尾部,但当终止参数end大于原列表(或 元组)的总长度时,会返回列表的全部数据。可见,基于x[start:end【:step】]的列表(或元组)切片操 作可以方便地对列表(或元组)x中的数据进行各种抽取、反转等切片操作。 例3.给定一个含有一组自然数的列表或元组。请完成如下操作: 13 ①显示列表或元组的长度; ②分别只返回奇数位置和偶数位置的列表或元组的子元素; ③使用列表或元组的切片操作,分别显示列表或元组的总长度、全部原始数据、倒序的原始数据、 奇数位置的数据、偶数位置的数据、指定范围内的数据、切片列表前n-1个元素后的结果。 【提示与说明】①使用len(x)方法可以显示列表或元组x的长度。②若返回奇数位置和偶数位 置的数据,只需分别把首位置变更一下即可。③若在print() 中将说明性的字符和列表或元组拼接在一 起显示,则需要将列表或元组x通过str(x)方法完成其由列表或元组类型到字符串类型的转换。切片 列表结果如图3. 19 所示。 图3.列表(元组)的切片操作使用示例 19 你试一试,看如何能把range() 对象转换为一个列表并分别显示倒数第一个、倒数第三个元素? 列表和元组的主要不同点如下。 (1)列表定界符是一对方括号[], 使用[] 表示一个空列表;元组定界符是一对圆括号(), 使用() 表示一个空列表。虽然元组的元素访问和列表一样都是使用方括号,但元组返回的仍然是元组对象。 84 85 (2)列表是可变的序列,当列表的元素增加或删除时,列表对象会自动进行扩展或收缩;列表中的 元素值可以修改,也可以在其尾部追加新元素。但元组是不可变的,因此没有增加元素、修改元素、删 除元素的方法,如果要对元组进行排序,可使用内置函数sorted(元组对象)并生成新的列表对象(这是 因为原始元组是不可变的)。 (3)列表、元组均支持切片操作;对于元组而言,只能通过切片访问元组中的元素,不允许使用切片 修改元组中元素的值。 (4)元组、列表都支持运算符+。对于元组来说,如果只有一个元素,也要加一个逗号,如(3,),仅 有一个整型或实数元素后无逗号的元组相当于直接定义为非元组的整型或实数,因此,若定义为元组, 即使只有一个元素,后面也要加上逗号。但对于列表来说则没有这种特性。如图3.20所示,注意第2 行代码和第5行代码的区别。另外,请注意出错信息。 图3.20 元组元素的定义 1.请你把range()对象转换的列表元素的倒数第二个元素赋予一个新值,再使用列表的append()方 法在末尾追加一个新元素。 2.由range()对象结果生成一个列表。要求:(1)生成其中从第3个元素到最后的切片序列;(2) 生成其中从头到第3个元素的切片序列;(3)生成最后三个子字符串。 3.4.2 列表和元组的常用方法 列表和元组有一些方法是可以共用的。可以通过Python提供的一些方法完成对列表中元素的 增、删、改、弹出、返回索引、逆序、排序等操作。当然,有些方法元组是无法使用的,如增、删、改操作等, 表3.6中列出的方法对于元组而言均不可用。 1.增、删、改列表数据的部分常用方法 增、删、改列表数据的部分常用方法如表3.6所示。 表3.6增、删、改列表数据的部分常用方法说明 方法说明 append(x) 将某个元素x添加至列表的尾部 extend(A) 将列表A中的所有元素添加至当前列表的尾部 insert(index,x) 在列表指定位置index的前面添加元素x,从这个index位置往后的所有元素均后移(索引号增加) remove(x) 在列表中删除首次出现的x元素,删除后该元素之后的所有元素前移一个位置,若同一值在序列中 多次出现,只移除第一个 pop([index]) 弹出(删除并返回)列表中下标为index的元素,如果缺省index参数,则默认其为-1,即弹出列表 的最后一个元素 clear() 删除列表中的所有元素,但保留列表对象(原列表成为空列表) 在增加列表数据的方法中,append(x) 用于向列表尾部追加元素x;extend(A)用于将另一个列表 inserindende A中的所有元素都追加至当前列表的尾部;t(x,x)用于向列表的指定位置ix的前面插入 元素x。在删除数据的方法中,用于删除列表中第一个与指定值相等的元素;pp([x]) remove(x) oinde 用于删除并返回指定位置(默认是最后一个)的元素;clear() 用于清空列表,另外,可以使用del() 方法 删除列表中指定位置的元素。 14 ppenexteninser 例3.建立一个新列表并赋初值;分别通过ad()、d()、t() 方法扩充原来列表的 内容。之后,对扩充后的列表进行元素去重、排序操作。 【提示与说明】21 所示, 注意这些 代码实现如图3.可以从键盘输入一些数据以自定义一个列表, 数据之间要用分隔符隔开,图3.因此用到了spli方法;通过x1. 21 中所示是采用空格作为分隔符, t() appnd() 增加新的内容后,原来的x1列表末尾就追加了这些新元素;1.xed(2)是将x2列表中的 exetnx 内容全部顺序追加到x1列表的尾部,注意列表中的元素是可以重复的;由于列表内容是可变的,因此 可以使用insert() 方法在特定位置追加新的元素。之后,利用notin操作判断重复元素以完成去重操 作。对于不重复的元素使用append() 方法追加到新列表中并使用sort() 方法进行排序。 图3.列表中插入元素、去重元素、排序元素使用示例 21 ang20) ist( ) [] 利用re(方法生成20 以内的数;通过l方法将其转换为列表并赋予变量x;再通过y=定 义另一个列表y;利用for循环依次遍历x中的各个元素,并对每次遍历到的元素加5后,追加到列表y中。 86 例3.15对于一个已经定义好的列表,逐次弹出排在队尾的元素。 【提示与说明】可通过len() 方法求得原列表的长度;通过for循环,依次使用pop() 方法弹出队 尾的元素,如图3.22 所示(x1列表中已经提前存储了字符串、浮点数、表达式、嵌套列表等多种元 素) 。从图3.22 中可以看出,弹出所有元素后,原列表长度为1;执行clear() 方法后,该列表长度才 清零。 图3.依次弹出列表末尾元素 22 例3.从键盘输入一些数字组成一个列表,再从键盘输入部分数字,将原列表中和这部分数据 16 重复的数据删除(删除输入部分的数据)。 的用法, 23 所示。【提示与说明】此题是练习remove() 代码实现如图3. 图3.e() 方法使用示例 23 remov 2. 和元素计数、排序、复制有关的部分常用方法 和元素计数、排序、7所示。 复制有关的部分常用方法如表3. 表3.和元素计数、排序、复制有关的方法说明 7 方法说明 index(x) 返回列表或元组中第一个值为x的元素的下标;若不存在值为x的元素,则抛出异常信息 count(x) 返回指定元素x在列表或元组中出现的次数 x.reverse() 对列表x中的所有元素逆序排序(元组无此方法) 87 方法说明 x.sort(key=None, reverse=False) 对列表x中的元素进行排序,这里的key是一个关于排序策略的函数;reverse=False为升序, reverse=True为降序 (元组无此方法) 返回复制的列表(元组无此方法) copy() 对表3. 7中部分方法的解释如下。 (1)index() 和count() 两个方法都是和列表或元组的索引和位置有关的:index(x)用于返回指定 元素x在列表或元组中首次出现的位置,如果该元素不在列表或元组中,则抛出异常;用于返 回列表或元组中指定元素x出现的总次数。 count(x) (2)reverse() 和sort() 方法都是和列表排序有关的:reverse() 方法用于将列表所有元素逆序排 序;t() 方法用于按照指定排序函数ky对所有元素进行排序;e() 和st() 方法都是用处理后 sorereversor 的数据替换原来的数据,原列表或元组地址不变且没有返回值。 17 evers 例3.不使用re() 方法,对输入的列表数据进行逆序输出,即第0个和第n1个互换,第 2个和第n-2个互换,以此类推,一直到中间的那个数据为止。 【提示与说明】需要用到for循环,循环次数为元素个数的一半,循环体所做的事情就是对找到的首 尾两个数据进行交换,为此需要找到中间的那个数据的索引。如图3.“” 24 所示,//为向下取整操作符。 图3.数据排序与交换使用示例 24 请思考一下,图3.e() 的上限为什么是(n(t)1) 24 中第2行代码ranglemylis-//2+1? 如果不 加1,会出现什么情况? 例3.首先生成一个具有不同长度元素的列表(元组), 并按数“位”的长度分别进行逆序(位数 多的排在前面,位数少的排在后面)排序;其次将其逆序排序,即位数少的排在前面,位数多的排在后 面;最后统计指定的某个元素首次出现的索引位置及其出现的总次数。 【提示与说明】25 所示。这里需要用到indecount()、sort() 等方法。列表元 18 代码实现如图3.x()、 素排序时需要得到每个元素的长度,因此可将其作为排序的key值,第5行代码使用了lambda匿名函 数(将在第4章介绍); 第6行代码显示的是重新排序后的列表,说明原始列表中元素的顺序已经发生 了变化,执行第7行代码的结果是对这个新顺序的逆序排序,这印证了reverse() 和sort() 方法都是用 处理后的数据替换原来的数据,这也验证了方法sort() 的“原地”操作特性,即用处理后的数据替换原 88