说明:
本文中的题目都来自全国计算机等级考试二级Python语言程序设计考试,且都为易错真题。题目编号为 Python123 平台上的题号,方便大家查找。
面向对象:Python 学习者、Python 二级备考大学生
题干: 以下程序的输出结果是____。
a = [3, 2, 1]for i in a[::-1]: print(i, end=' ')1,2,33 2 11 2 33,2,1<序列>[N:M:K] 是通过对已有序列切片,返回一个新的序列对象。获取从已有序列的 N 号(包含N)到 M 号(不包含 M)的以 K 为步长所对应的元素组成的序列。 题中步长 K 为 -1,N 和 M 省略,则表示:获取由列表 a 从后往前的所有元素组成的一个新的列表,即 [1, 2, 3]。
然后对 [1, 2, 3] 进行遍历,在每输出一个字符后,紧跟的终止符是一个空格符,则最终得到的就是:1 2 3。注意:3的后面还有一个空格。
题干: 执行以下程序后,要输出
46cd44ab结果,该执行的选项是____。
ls = ["ab", "44", "cd", "46"]ls.reverse()print("".join(ls[1:]))print("".join(ls[-1::]))print("".join(ls[-1::-1]))print("".join(ls))Python中列表对象的函数 reverse() 用于将列表中的元素反向排列。所以执行完 ls.reverse() 后,列表 ls 就变成了 ["46", "cd", "44", "ab"],要输出 46cd44ab,可以用空字符串 "" 作为“胶水”,将反向后列表中的多个数据元素(字符串)无缝拼接到一起即可。其它输出都不对。
cd44ab,少了0号位的 46 ;ab,相当于 ls 的最后一个元素;ab44cd46,顺序不对。题干: 以下是某班5名同学的一组个人信息:
采用变量 a 存储以上信息用于统计分析,最适合的数据类型是( )。
存储包含多个结构化的数据,最适合的是列表数据类型。列表可包含多个元素,每一个元素又都是一个单独的子列表。其中,第一个子列表存储的是信息的分类,类似列头;后续每个子列表存储的都是每个同学的个人信息。
比如,变量 a 可以定义成类似如下的一个嵌套二维列表:
a = [ ['学号', '姓名', '性别', '年龄', '身高', '体重'], ['xs001', '张红', '女', '18', '168', '55'], ['xs002', '王丽丽', '女', '19', '165', '60'], ['xs003', '李华', '男', '18', '178', '66'], ['xs004', '赵亮', '男', '19', '175', '65'], ['xs005', '张玲玲', '女', '18', '160', '50'],]题干: 以下代码的输出结果是____。
CList = list(range(5))print(2in CList)range(start, stop[, step]) 表示一个等差序列表示的范围,从整数 start 开始,到整数 stop 结束,但不包含 stop,步长为 step(没有则默认为1)。步长为 1 时,start 省略则默认从 0 开始,range(5) 就等价于 range(0, 5, 1),也等价于 range(0, 5),其中包含5个整数元素:0、1、2、3、4;range(5) 转换成列表后,则 CList 为 [0、1、2、3、4]。
表达式 x in S 用来判断 x 是否为 S 的某个元素,是则返回 True,否则返回 False。因为 2 是 CList 中的一个元素,所以输出 True。
题干: 给以下程序填空,能输出
{0:[90, 'Aele'], 1:[87, 'Bob'], 2:[93, 'lala']}结果的选项是____。
x = [90, 87, 93]y = ("Aele", "Bob", "lala")z = {}for i in range(len(x)): __________print(z)z[i] = [x[i], y[i]]z[i] = x[i], y[i]z[i] = list(zip(x, y))z[i] = x, yi 为0时,x[0] = 90,y[0] = "Aele",则 z[0] 为 [90, 'Aele'],依此类推,构造出的字典中的键值对都满足要求,因此 A 选项正确,其它都有误。z 中的每个元素,都是由 i 和一个元组 (x[i], y[i]) 组成的键值对,即 z为:{0:(90, 'Aele'), 1:(87, 'Bob'), 2:(93, 'lala')},不满足题意中值为列表的要求;list(zip(x, y)) 先将对象 x 和 y 中对应位置的元素打包成一个个的元组,然后返回由这些元组构成的列表。list(zip(x, y)) 得到列表:[(90, 'Aele'), (87, 'Bob'), (93, 'lala')],最终的z为 {0: [(90, 'Aele'), (87, 'Bob'), (93, 'lala')], 1: [(90, 'Aele'), (87, 'Bob'), (93, 'lala')], 2: [(90, 'Aele'), (87, 'Bob'), (93, 'lala')]},不符合要求;z[i] = x, y 构建的字典 z,其中的键为 0、1、2,对应的值为整个 x 和整个 y 组成的元组 ([90, 87, 93], ('Aele', 'Bob', 'lala'))。即 z 最终为:{0: ([90, 87, 93], ('Aele', 'Bob', 'lala')), 1: ([90, 87, 93], ('Aele', 'Bob', 'lala')), 2: ([90, 87, 93], ('Aele', 'Bob', 'lala'))},也不满足要求;题干: 变量
ls = [1, 2, 3, 4, 5, 6, 7, 8, 9],以下执行结果是1 2 3 4 5 6 7 8 9的选项是______。
print("".join(str(ls)))print("".join(ls))print("".join(str(ls).split(",")))print("".join(str(ls).strip("[]").split(",")))A. 先执行 str(ls) 将列表转换成一个字符串 "[1, 2, 3, 4, 5, 6, 7, 8, 9]";然后再用空字符串 "" 将该字符串中的 每个字符 都拼接(join())到一起,得到的字符串 "[1, 2, 3, 4, 5, 6, 7, 8, 9]" 和原字符串完全一样,输出到控制台后,没有两端的引号,但包含了两端的方括号和中间的逗号,不合题意;
B.print("".join(ls)) 语法本身没错,但运行时会报类型错误: TypeError: sequence item 0: expected str instance, int found。因为 join() 的基本格式是:拼接连接符.join(字符串序列),字符串函数join()要求作为参数参与拼接的序列,其每个元素也必须都是字符串,而ls中的元素都是整数,显然没法被拼接;
C.print("".join(str(ls).split(","))) 先把列表 ls 转换成一个字符串 "[1, 2, 3, 4, 5, 6, 7, 8, 9]",然后对得到的字符串按英文逗号 , 切分成一个列表 ['[1', ' 2', ' 3', ' 4', ' 5', ' 6', ' 7', ' 8', ' 9]'],再用空字符串 "" 将得到的列表中的每个字符串都 join()到一起,最终得到字符串 "[1 2 3 4 5 6 7 8 9]",输出到控制台后,没有两边的引号,但两端的方括号还在,也不合题意;
D. 在考试中,如果能快速判断出 A、B、C 都不合题意,为节省时间,可以通过排除法选出 D。但学习时,仍建议大家完整地理解 D 的执行过程。
print("".join(str(ls).strip("[]").split(","))) 这句代码较长较复杂,可以按照“由内到外、从左到右”的方法调用顺序来理解。代码按照下图中从小到大的编号步骤,依次执行(最后一步没有标注,是打印输出步骤4得到的字符串):
为方便初学者理解,可以把长代码拆成中间变量方便理解:
ls = [1, 2, 3, 4, 5, 6, 7, 8, 9]s1 = str(ls)print(s1) # "[1, 2, 3, 4, 5, 6, 7, 8, 9]"s2 = s1.strip("[]")print(s2) # "1, 2, 3, 4, 5, 6, 7, 8, 9"s3 = s2.split(",")print(s3) # ['1', ' 2', ' 3', ' 4', ' 5', ' 6', ' 7', ' 8', ' 9']s4 = "".join(s3)print(s4) # "1 2 3 4 5 6 7 8 9"str(ls),将列表转换成一个字符串 "[1, 2, 3, 4, 5, 6, 7, 8, 9]";[],即删除字符串两边的方括号,得到新的字符串 "1, 2, 3, 4, 5, 6, 7, 8, 9";str.split(',') 按逗号对第2步得到的字符串进行拆分。"1, 2, 3, 4, 5, 6, 7, 8, 9" 按逗号拆分后得到列表 ['1', ' 2', ' 3', ' 4', ' 5', ' 6', ' 7', ' 8', ' 9'],注意:从2开始的每个数字前面都带有一个空格;"".join(['1', ' 2', ' 3', ' 4', ' 5', ' 6', ' 7', ' 8', ' 9']) 用空字符串把列表中的每个字符串都拼接到一起,形成字符串 "1 2 3 4 5 6 7 8 9";print() 输出字符串,得到正确的答案。拓展:实际编程中的推荐写法
虽然本题的正确答案是 D,但是实际编程中,D 选项并不是推荐的写法。D 的写法太绕,如果想把整数列表:ls = [1, 2, 3, 4, 5, 6, 7, 8, 9] 输出成:1 2 3 4 5 6 7 8 9,更推荐用下面两种方法。
方法1:使用 print(*ls)
print(*ls) 会把列表中的元素依次取出来,并默认用空格分隔输出。
方法2:使用 " ".join(map(str, ls))
其中 map(str, ls) 会把列表中的每个整数转换成字符串,然后," ".join(...) 用空格作为连接符,把转换后的这些字符串再拼接到一起。方法2是更符合 join() 的正常使用方式。
题干: 以下程序的输出结果是____。
ss = [2, 3, 6, 9, 7, 1]for i in ss: print(max(ss), end=',') ss.remove(max(ss))这一题不会按你直觉中那样循环 6 次,输出C选项,而是只循环了 3 次。原因在于:代码在迭代过程中修改了正在遍历的列表,导致 for 循环的内部索引和实际列表内容不同步,因此在输出 9,7,6, 后循环就结束了。
虽然 Python 的 for i in ss: 写法看起来像是直接从列表 ss 中依次拿元素,但在底层,它实际上是通过一个内部迭代器来工作的。对于列表来说,这个迭代器维护着一个整数索引(index),默认从 0 开始,每次循环开始时,做如下几步:
ss[index] 作为当前元素;index 加 1;index 已经 ≥ 列表的长度len(ss),则循环结束。关键点:迭代器的索引 index 不会因为你修改了列表而自动调整/同步。 而通过 list.remove() 删除元素后,会把后面的元素整体往前"挪一位",于是索引和元素之间的对应关系就被打乱了。
用一个表格清晰追踪每一轮循环开始时的状态,如下表所示:
进入第 4 轮时,index = 3,但此时 len(ss) = 3,也就是说合法索引只有 0, 1, 2。迭代器发现 index 已经越界,于是直接结束循环,根本不会再执行 print。所以最终输出就是 9,7,6, 。
如果代码的本意是:“按从大到小的顺序依次打印并移除最大值,直到列表为空”,则下面几种写法都可以:
方法1:用 while 循环判断列表是否为空
ss = [2, 3, 6, 9, 7, 1]while ss: print(max(ss), end=',') ss.remove(max(ss))# 输出:9,7,6,3,2,1,方法2:遍历列表的副本,避免修改原列表影响迭代
ss = [2, 3, 6, 9, 7, 1]for i in ss.copy(): # 或ss[:],都是返回一个浅拷贝 print(max(ss), end=',') ss.remove(max(ss))方法3:对列表原地排序后依次弹出最后(最大)一个元素,逻辑也很清晰
ss = [2, 3, 6, 9, 7, 1]ss.sort() # 原地排序,从小到大while ss: print(ss.pop(),end=',') # 每次弹出最后面最大的一个,并输出经验:永远不要在用
for循环遍历一个列表的同时,对该列表执行append、remove、pop等可能会改变列表长度的操作。
题干: 以下程序的输出结果是____。
deffunc(x=[], y=[6, 7]): x.append(8) y.append(8)return (x + y)a, b = [1, 2], [3, 4]t = func(x=a)t = func(y=b)print(func(), end=";")[8, 6, 7, 8, 8];[8, 6, 7, 8, 8][8, 8, 6, 7, 8, 8];[8, 8, 6, 7, 8, 8]这是一道关于函数参数的默认值为可变对象时的知识点。这种题纯粹是不想让人拿满分的,题面代码也不符合一般的 Python 用法;这种题目倒是经常作为 Python 技术面试时考核应聘者的难题之一,看不懂的可以暂时搁置。
以下解析,涉及概念较复杂,初学 Python 如果暂时看不懂也没关系,期末和二级考试最多就是一分单选题(还很可能就是原题),实际开发中需用到这种情形时再深入学习不迟。
碰到问题,要学会先查看官方文档中是否有权威解释,再去网上搜索、问AI啥的。在Python官方文档的 Tutorial 的“4.9. 函数定义详解”的“4.9.1. 默认值参数”中,有相关的如下一番话(取自最新的 Python 稳定版 v3.14.5):
重要警告: 默认值只计算一次。默认值为列表、字典或类实例等可变对象时,会产生与该规则不同的结果。例如,下面的函数会累积后续调用时传递的参数:
deff(a, L=[]): L.append(a)return Lprint(f(1))print(f(2))print(f(3))输出结果:
[1][1, 2][1, 2, 3]不想在后续调用之间共享默认值时,应以如下方式编写函数:
deff(a, L=None):if L isNone: L = [] L.append(a)return L
在Python中,当函数使用可变对象(如列表或字典)作为默认参数时,在函数定义时,只会被初始化一次,并在后续的调用中重用。就是说,如果默认参数在之后的函数调用时被修改(函数体中),将会基于本次调用前最新的值,而非函数定义的参数列表中最初的默认值。
这是 Python 中最经典的 “陷阱” 之一,根源在于 Python 函数对象的 定义时求值(definition-time evaluation) 机制,以及 Python 对函数与对象的整体设计哲学。感兴趣的同学可以深入研究,这儿就不再赘述。