Karinoya 学习室

资格考试 · Cloud / AI / Python 合格实验室

异常、类与标准库

可以用简体中文阅读题目和解说。讲义(解说文章)仅有日文版。

查看日文版(含讲义) →

第1题 | try语句的顺序

执行以下代码时,按从上到下的顺序输出的行是哪一项?

def calc(a, b):    try:        print("try")        r = a / b    except ZeroDivisionError:        print("except")    else:        print("else")    finally:        print("finally")calc(9, 3)
  1. try、else、except
  2. try、else、finally
  3. try、except、else
  4. try、except、finally
正确答案B. try、else、finally

9 / 3不会引发异常,因此不会进入except。只有在没有发生异常时才会执行的是else子句,因此try之后会执行else。finally子句无论有无异常都一定会执行,因此最后运行。因此结果是try、else、finally这3行。含有except的选项,是误以为发生了异常时的排列,这次调用不会产生ZeroDivisionError。缺少finally的选项,是把finally误解为『发生异常时的善后处理』时的排列。

第2题 | else子句

关于try语句的else子句被执行的条件,正确的是哪一项?

  1. 无论是否发生异常,总是最后执行
  2. 只有在try节发生异常时才会执行
  3. 在与任何except节都不匹配时执行
  4. 只有在try节没有发生异常时才会执行
正确答案D. 只有在try节没有发生异常时才会执行

try语句的else子句,只有在try节没有引发异常地执行到最后时才会执行。与if语句的else是『条件为假时』相对,try语句的else是站在『顺利成功』一侧的,方向相反,可以这样来记。发生异常时执行的是except节。无论是否发生异常都一定会执行的是finally节。如果与任何except都不匹配,并不会落到else,而是该异常会继续向调用方传播。

第3题 | finally优先

执行以下代码时的输出是哪一项?

def f():    try:        return "T"    finally:        print("F")def g():    try:        return "T"    finally:        return "N"print(f(), g())
  1. 输出2行:F 和 T T
  2. 输出2行:F 和 T N
  3. 输出2行:T T 和 F
  4. 输出2行:T N 和 F
正确答案B. 输出2行:F 和 T N

f()准备好要return的值之后,在真正返回之前会先执行finally节。因此F会先显示,返回值仍保持T。g()的finally节中含有return,这会覆盖try节中的return,因此返回值变为N。print会先对f()和g()求值,再汇总到一行显示,因此F那一行在前,T N那一行在后。得到T T的选项,是认为finally的return会被忽略时的答案;F排在后面的选项,是认为finally在return之后才运行时的答案。

第4题 | 无法被捕获的异常

写成except Exception的except节无法捕获的异常是哪一项?

  1. FileNotFoundError
  2. ZeroDivisionError
  3. KeyboardInterrupt
  4. ModuleNotFoundError
正确答案C. KeyboardInterrupt

KeyboardInterrupt直接位于BaseException之下,并未继承Exception。issubclass(KeyboardInterrupt, Exception)为False。它是用户按下Ctrl-C想要中止程序时产生的,为了不被写得较宽泛的except Exception不小心吞掉,特意把层级分开。SystemExit也处于同样的位置。其余三个都是Exception的后代:ZeroDivisionError在ArithmeticError之下,FileNotFoundError在OSError之下,ModuleNotFoundError在ImportError之下,因此都能被except Exception捕获。

第5题 | 异常的父类

IndexError与KeyError共同的最近父类是哪一项?

  1. ReferenceError
  2. ArithmeticError
  3. AttributeError
  4. LookupError
正确答案D. LookupError

IndexError和KeyError都具有『用下标或键去查找却没有找到』这一相同性质,因此被放在共同的父类LookupError之下。写成except LookupError,就能把列表越界访问和字典中不存在的键这两种情况一并接住。ArithmeticError是ZeroDivisionError等的父类,用于计算本身无法成立的场合。AttributeError是属性找不到时的异常,ReferenceError是弱引用所指对象已消失时的异常,两者都直接位于Exception之下,与LookupError属于不同的谱系。

第6题 | except的顺序

执行以下代码时,正确的输出是哪一项?

try:    x = 8 / 0except ArithmeticError:    print("A")except ZeroDivisionError:    print("Z")
  1. 什么都不输出就停止
  2. 只输出Z
  3. 输出A和Z
  4. 只输出A
正确答案D. 只输出A

8 / 0会抛出ZeroDivisionError,而ZeroDivisionError是ArithmeticError的子类。except节会按从上到下的顺序匹配,一旦匹配就停止,因此先写的except ArithmeticError会先接住它,只输出A。后面的except ZeroDivisionError没有出场的机会。由于只会执行匹配到的那一个except,A和Z不会同时输出。原则是把较窄的(子类)放在前面、较宽的(父类)放在后面,本题的代码正好把这个顺序反了过来。

第7题 | 多个异常

执行以下代码时显示的1行是哪一项?

def conv(s):    try:        return int(s) * 2    except (ValueError, TypeError) as e:        return type(e).__name__print(conv("21"), conv("abc"), conv(None))
  1. 42 ValueError TypeError
  2. 42 TypeError TypeError
  3. 42 ValueError ValueError
  4. 42 TypeError ValueError
正确答案A. 42 ValueError TypeError

在except后面写用圆括号括起来的元组,只要匹配其中任意一个就会执行。conv("21")中int("21")得到21,因此返回42。conv("abc")虽然能作为字符串传入,但无法解释为数值,因此会是ValueError。conv(None)不是int()能接受的类型,因此是TypeError。类型正确但值不合适则是ValueError,类型本身就不对则是TypeError,这一区分正好对应答案的排列。

第8题 | 重新抛出

使用自定义异常和不带参数的raise的以下代码,其输出是哪一项?

class LoadError(Exception):    passdef load():    try:        raise LoadError("bad")    except LoadError:        print("inner")        raisetry:    load()except Exception as e:    print("outer", type(e).__name__)
  1. 只有inner,outer不会输出
  2. inner和outer Exception共2行
  3. inner和outer LoadError共2行
  4. 输出inner之后在LoadError处停止
正确答案C. inner和outer LoadError共2行

在except内部只写raise而不带参数,会把当前正在处理的异常原样重新抛给调用方。因此显示inner之后,LoadError会传到load()的外部,被外层的except Exception接住。由于LoadError继承自Exception,能被except Exception捕获,因此不会停止。type(e).__name__返回的是实际被抛出的异常类的名字,因此显示的不是捕获一方的类型名Exception,而是LoadError。只输出inner,是没有重新抛出、而是当场吞掉时的输出。

第9题 | 未被捕获的异常

执行以下代码会得到怎样的结果?

def get(seq, key):    try:        return seq[key]    except IndexError:        return "not found"print(get([1, 2, 3], 1))print(get({"a": 1}, "z"))
  1. 显示2之后,在KeyError处停止
  2. 输出2行:2 和 not found
  3. 输出2行:2 和 KeyError
  4. 输出2行:2 和 None
正确答案A. 显示2之后,在KeyError处停止

第一次调用是列表的下标1,因此返回2。第二次是查找字典中不存在的键,因此会引发KeyError,但except中写的只有IndexError。IndexError和KeyError虽然都是LookupError的子类,但彼此之间并非父子关系,因此IndexError的except接不住KeyError。未被捕获的异常会传到调用方,由于没有人接住,程序会停止。想同时接住两者,应写成except LookupError,或者用元组把两者并列。

第10题 | assert

关于assert语句的说明,正确的是哪一项?

  1. 条件为假时抛出AssertionError,在python3 -O下会被禁用
  2. 条件为假时抛出AssertionError,即使在python3 -O下也一定会执行
  3. 条件为真时抛出AssertionError,在python3 -O下会被禁用
  4. 条件为假时抛出ValueError,即使在python3 -O下也一定会执行
正确答案A. 条件为假时抛出AssertionError,在python3 -O下会被禁用

assert 条件, 消息 会在条件为假时抛出AssertionError。条件为真时什么都不会发生。重要的是,若以python3 -O这样的优化模式启动,assert语句会被整体移除,一行也不会执行。因此不应把assert用于检查用户输入这一目的,那种用途应该用if判断条件后抛出raise ValueError(...)。可以把assert理解为在开发过程中记录内部前提的工具,这样区分二者的角色比较合适。

第11题 | self

关于方法的第1个参数self的说明,正确的是哪一项?

  1. 用于引用父类属性而保留的名字
  2. 调用它的实例自身被自动传入的参数
  3. 用于生成实例并返回给调用方的参数
  4. 类本身会被自动传入的保留名字
正确答案B. 调用它的实例自身被自动传入的参数

写成 d.speak() 时,Python会把它改写为 Dog.speak(d) 来调用。此时进入第1个参数位置的,正是发起调用的实例本身。self这个拼写只是约定俗成,并非语言保留的名字。规范只规定了第1个参数接收自身这一位置,因此即使自己写成 Dog.speak(d) 也能同样运作。创建并返回实例是__new__和类调用机制的职责,__init__只是对已经创建好的实例进行初始化。引用父类需要使用super()。

第12题 | 类变量

改写类变量的以下代码,其输出是哪一项?

class Dog:    kind = "canine"    def __init__(self, name):        self.name = namea = Dog("Rex")b = Dog("Mia")Dog.kind = "wolf"print(a.kind, b.kind)
  1. wolf wolf
  2. canine wolf
  3. canine canine
  4. wolf canine
正确答案A. wolf wolf

kind是写在class正下方的类变量,由该类创建出的所有实例共享同一个。a和b自己的__dict__中都没有kind,因此读取该属性时会一路追溯到类那一侧,看到相同的值。对 Dog.kind 的赋值改写的正是这个被共享的唯一对象,因此已经创建好的a和b两者都会看到wolf。创建实例时并不会把值复制一份,因此不会得到保留canine的选项。

第13题 | 属性的阴影

在实例一侧赋值的以下代码,其输出是哪一项?

class Dog:    kind = "canine"    def __init__(self, name):        self.name = namea = Dog("Rex")b = Dog("Mia")a.kind = "husky"print(a.kind, b.kind, Dog.kind)print(sorted(a.__dict__))
  1. husky husky husky 以及 ['name']
  2. husky canine canine 以及 ['kind', 'name']
  3. husky canine canine 以及 ['name']
  4. husky husky husky 以及 ['kind', 'name']
正确答案B. husky canine canine 以及 ['kind', 'name']

对 a.kind 赋值不会改写类变量,而是在a的实例字典中新建一个名为kind的条目。此后读取a.kind时会优先找到这个新条目,从而遮住了类变量,形成『阴影』。b和Dog.kind仍保持原来的canine不变。因此a.__dict__中除了__init__放入的name之外还并列着kind,排序后得到['kind', 'name']。全部变成husky的选项,是误以为对实例的赋值会改写类变量时的答案;只剩['name']的选项,是认为不会产生阴影时的答案。

第14题 | 被共享

把列表设为类变量的以下代码,其输出是哪一项?

class Basket:    items = []    def add(self, x):        self.items.append(x)p = Basket()q = Basket()p.add(1)q.add(2)print(p.items, q.items)
  1. p.items 为 [1],q.items 为 [1, 2]
  2. p.items 为 [1, 2],q.items 为 [2]
  3. p.items 为 [1, 2],q.items 为 [1, 2]
  4. p.items 为 [1],q.items 为 [2]
正确答案C. p.items 为 [1, 2],q.items 为 [1, 2]

self.items.append(x) 并不是对self.items赋值,而只是调用了通过self.items找到的那个对象。由于p和q都没有自己的items,找到的都是类变量那同一个列表。结果无论是从p添加的1,还是从q添加的2,都进入了同一个列表,从哪个实例来看都是[1, 2]。如果想让每个实例各自拥有独立的容器,需要在__init__中通过 self.items = [] 来赋值创建,这样就会分别得到[1]和[2]。

第15题 | super()

使用继承与super()的以下代码,其输出是哪一项?

class Animal:    def __init__(self, name):        self.name = name    def cry(self):        return self.name + " ?"class Cat(Animal):    def __init__(self, name, color):        super().__init__(name)        self.color = color    def cry(self):        return self.name + " meow"c = Cat("Tama", "black")print(c.cry(), c.color, isinstance(c, Animal))
  1. Tama meow None True
  2. Tama ? black True
  3. Tama meow black False
  4. Tama meow black True
正确答案D. Tama meow black True

Cat的__init__调用了super().__init__(name),因此父类Animal会替它设置好self.name。之后再把black存入self.color,两个属性因此都齐备。cry在子类中被重新定义,父类的实现被覆盖,因此调用的是meow那一个。isinstance(c, Animal)由于Cat继承自Animal,结果为True。如果忘记写super(),name就不会被设置,会引发AttributeError,而不会像选项中那样悄悄变成None这种失败方式。

第16题 | MRO

多重继承的以下代码,其输出是哪一项?

class A:    def who(self):        return "A"class B(A):    def who(self):        return "B"class C(A):    def who(self):        return "C"class D(B, C):    passprint(D().who())print([k.__name__ for k in D.__mro__])
  1. B 与 ['D', 'B', 'C', 'A', 'object']
  2. C 与 ['D', 'C', 'B', 'A', 'object']
  3. B 与 ['D', 'B', 'A', 'C', 'object']
  4. A 与 ['D', 'B', 'C', 'A', 'object']
正确答案A. B 与 ['D', 'B', 'C', 'A', 'object']

方法解析顺序由C3线性化这一规则决定,并非单纯的深度优先。共同的父类A会被放在所有继承自A的子类之后,因此排列为D、B、C、A、object。如果是钻进B就直接走到A的深度优先方式,会得到D、B、A、C、object,但实际并非如此,这正是需要留意的地方。who()会从MRO开头查找,使用最先找到的那个,因此返回B。如果写成 class E(C, B),就会得到E、C、B、A、object,写在前面的优先。

第17题 | MRO的矛盾

在已定义 class B(A) 的情况下,写出 class F(A, B): pass 会发生什么?

  1. 能够定义成功,F().who() 调用的是A的方法
  2. 能够定义成功,__mro__ 会是 F、A、B、object
  3. 能够定义成功,F().who() 调用的是B的方法
  4. 在类定义的时刻发生 TypeError
正确答案D. 在类定义的时刻发生 TypeError

C3线性化有两条规则:要保持列出基类的顺序,并且父类必须排在子类之后。F(A, B)指定要把A排在B之前,但B是A的子类,因此父类A必须排在子类B之后。这两点无法同时满足,因此判定为无法构造出一致的MRO,在对类定义求值的那一刻就会发生TypeError。这不是要等到实际运行才会发现,而是在通过class语句的瞬间就会失败。正确写法应是 class F(B, A),把子类写在前面。

第18题 | 名称修饰

拥有以下划线开头属性的以下代码,其输出是哪一项?

class P:    def __init__(self):        self._one = 1        self.__two = 2p = P()print(p._one, p._P__two)print(sorted(p.__dict__))
  1. 1 2 与 ['_one', '_two']
  2. 1 2 与 ['_P__one', '_P__two']
  3. 1 2 与 ['__two', '_one']
  4. 1 2 与 ['_P__two', '_one']
正确答案D. 1 2 与 ['_P__two', '_one']

以单个下划线开头的_one只是按惯例表示供内部使用,名字本身不会改变,从外部也能照常读取。以两个下划线开头的__two会触发名称修饰,实际的属性名会被改写为_P__two,即『_类名__名字』的形式。因此写成p.__two会得到AttributeError,但写成p._P__two就能读到。__dict__中排列的也是修饰后的名字,因此排序后得到['_P__two', '_one']。只有以两个下划线开头的才会被修饰,_one不在此列。

第19题 | 特殊方法

定义了特殊方法的以下代码,其输出是哪一项?

class Vec:    def __init__(self, x, y):        self.x = x        self.y = y    def __str__(self):        return "V({}, {})".format(self.x, self.y)    def __repr__(self):        return "Vec(x={}, y={})".format(self.x, self.y)    def __len__(self):        return 2    def __add__(self, o):        return Vec(self.x + o.x, self.y + o.y)v = Vec(1, 2)print(v)print([v])print(len(v), v + Vec(3, 4))
  1. Vec(x=1, y=2) 与 [Vec(x=1, y=2)] 与 2 V(4, 6)
  2. V(1, 2) 与 [Vec(x=1, y=2)] 与 2 V(4, 6)
  3. V(1, 2) 与 [Vec(x=1, y=2)] 与 2 Vec(x=4, y=6)
  4. V(1, 2) 与 [V(1, 2)] 与 2 V(4, 6)
正确答案B. V(1, 2) 与 [Vec(x=1, y=2)] 与 2 V(4, 6)

print(v)与str(v)使用的是__str__,因此得到V(1, 2)。而放入列表或字典等容器中显示时,每个元素会使用__repr__,因此得到[Vec(x=1, y=2)]。这一区分正是本题的要点。len(v)是__len__返回的2,v + Vec(3, 4)是__add__返回的新Vec,把它传给print时又会用到__str__,因此显示为V(4, 6)。没有定义__str__的类会用__repr__代替,但反过来__str__不会代替__repr__被使用。

第20题 | __eq__

只定义了__eq__的以下代码,其输出是哪一项?

class Point:    def __init__(self, x):        self.x = x    def __eq__(self, other):        return self.x == other.xp = Point(1)q = Point(1)print(p == q, p is q)print(Point.__hash__ is None)try:    print(len({p, q}))except TypeError:    print("TypeError")
  1. True False 与 False 与 2
  2. True False 与 True 与 TypeError
  3. True False 与 False 与 1
  4. True True 与 True 与 TypeError
正确答案B. True False 与 True 与 TypeError

由于定义了__eq__,p == q 会比较属性x,结果为True,但p和q是分别创建的两个对象,因此p is q仍为False。这里重要的是:定义了__eq__的类,其__hash__会被自动设为None,因此 Point.__hash__ is None 为True。没有哈希值的对象无法用作集合或字典的键,因此试图构造{p, q}会引发TypeError。如果也想把它放入集合,需要自己定义一个在相等时返回相同值的__hash__。

第21题 | math

使用math模块的以下代码,其输出是哪一项?

import mathprint(math.sqrt(16))print(math.floor(-1.5), math.ceil(-1.5))
  1. 4.0 与 -2 -1
  2. 4.0 与 -1 -2
  3. 4 与 -2 -1
  4. 4 与 -1 -1
正确答案A. 4.0 与 -2 -1

math.sqrt即使能整除也会返回float,因此16的平方根不是显示为4而是4.0。math.floor是向数轴下方截断,因此-1.5变为-2;math.ceil是向上方,因此-1.5变为-1。要注意的是,对于负数,朝0方向截断的int(-1.5)会得到-1,这与floor的结果不一致。如果把floor和ceil弄反,就会得到-1 -2的排列;如果误以为sqrt的返回值类型是int,就会得到4。

第22题 | os.path

os.path.splitext('/tmp/z.txt') 与 os.path.basename('/tmp/z.txt') 返回值的组合是哪一项?

  1. ('/tmp/z', '.txt') 与 'z.txt'
  2. ('/tmp', 'z.txt') 与 '/tmp'
  3. ('/tmp/z', 'txt') 与 'z.txt'
  4. ('/tmp/z.txt', '') 与 'z.txt'
正确答案A. ('/tmp/z', '.txt') 与 'z.txt'

os.path.splitext只会把扩展名切分出来,点仍保留在扩展名一侧,返回一个2元素的元组。因此结果是 ('/tmp/z', '.txt'),不会出现点被去掉、变成'txt'的情况。os.path.basename返回路径末尾的名字,因此是'z.txt'。返回目录一侧'/tmp'的是os.path.dirname,如果想要把两者一并得到的元组,应使用os.path.split。可以这样区分记忆:splitext是按扩展名切分,而不是按目录分隔符切分。

第23题 | re的匹配

使用re模块的以下代码,其输出是哪一项?

import reprint(re.match("b", "abc"))print(re.search("b", "abc").group())print(re.findall(r"\d+", "a1b22c333"))
  1. b 与 None 与 ['1', '22', '333']
  2. None 与 b 与 ['1', '22', '333']
  3. b 与 b 与 ['1', '22', '333']
  4. None 与 b 与 ['a1', 'b22', 'c333']
正确答案B. None 与 b 与 ['1', '22', '333']

re.match只从字符串开头开始匹配,'abc'的开头是'a',与'b'不匹配,因此返回None。re.search会在中途也进行查找,因此能匹配到'b',用.group()可以取出匹配到的字符串b。这一区别是re中最常被考查的地方。re.findall会返回一个收集了所有与模式匹配部分的列表,\\d+表示连续的数字,因此结果是['1', '22', '333'],不会连分隔用的字符也包含进去。如果没有匹配,则返回空列表。

第24题 | datetime

使用datetime的以下代码,其输出是哪一项?

import datetimed = datetime.date(2026, 8, 22)print(d)print(d.strftime("%Y/%m/%d"))print(d + datetime.timedelta(days=10))
  1. 2026/08/22 与 2026/08/22 与 2026/09/01
  2. 2026-08-22 与 2026/08/22 与 2026-09-01
  3. 2026-08-22 与 2026/08/22 与 10 days, 0:00:00
  4. 2026-08-22 与 2026/08/22 与 2026-08-32
正确答案B. 2026-08-22 与 2026/08/22 与 2026-09-01

直接print一个date对象,会以ISO格式、用连字符分隔显示为2026-08-22。想改变格式时使用strftime,传入'%Y/%m/%d'会返回用斜杠分隔的字符串2026/08/22。给date加上timedelta(days=10),返回的不是timedelta,而是指向10天后的新date,因此print出来是2026-09-01。跨月的计算由datetime一方负责处理,因此不会出现只是把日期数字加10、变成不存在的日期(如2026-08-32)这种情况。如果认为print(d)会与strftime相同格式,就会误以为是斜杠分隔,但默认显示是连字符分隔。日期相减得到的不是date而是timedelta,其显示会是像10 days, 0:00:00这样的形式。

第25题 | Counter

使用collections.Counter的以下代码,其输出是哪一项?

from collections import Counterc = Counter("abracadabra")print(c.most_common(2))print(c["a"], c["z"])
  1. [('a', 5), ('b', 2)] 与 5 与 KeyError
  2. [('a', 5), ('r', 2)] 与 5 0
  3. [('a', 5), ('b', 2)] 与 5 0
  4. [('a', 5), ('b', 2), ('r', 2)] 与 5 0
正确答案C. [('a', 5), ('b', 2)] 与 5 0

'abracadabra'中a有5个,b和r各有2个,c和d各有1个。most_common(2)会按数量从多到少只返回前2项,形式是(元素, 个数)组成的元组列表,因此是[('a', 5), ('b', 2)]。个数相同的b和r,先出现的排在前面,因此采用b。Counter虽是字典的一种,但查找不存在的元素不会引发KeyError,而是返回0,这一点与普通字典不同。这样设计是为了不必先确认存在与否就能直接计数。

第26题 | namedtuple

使用namedtuple创建的类型的以下代码,其输出是哪一项?

from collections import namedtuplePt = namedtuple("Pt", "x y")p = Pt(1, 2)print(p)print(p.x, p[0], isinstance(p, tuple))
  1. (1, 2) 与 1 1 True
  2. Pt(x=1, y=2) 与 1 1 False
  3. Pt(x=1, y=2) 与 1 1 True
  4. Pt(1, 2) 与 1 1 False
正确答案C. Pt(x=1, y=2) 与 1 1 True

namedtuple创建的类型,显示时会呈现出带有类型名和字段名的Pt(x=1, y=2)这种形式。值既可以按名字读取,也可以按下标读取,因此p.x和p[0]都是1。而且namedtuple是元组的派生类型,因此isinstance(p, tuple)为True。也就是说它是元组的一种,元素事后不能被改写。如果想持有事后可改的值,需要使用会创建替换了值的新实例的_replace,或者定义普通的类。

第27题 | 用尽

把同一个map对象传给list()两次的以下代码,其输出是哪一项?

m = map(str, [1, 2, 3])print(list(m))print(list(m))
  1. 第1行为 ['1', '2', '3'],第2行为 None
  2. 第1行为 ['1', '2', '3'],第2行为 []
  3. 第1行为 [1, 2, 3],第2行为 []
  4. 第1行和第2行都为 ['1', '2', '3']
正确答案B. 第1行为 ['1', '2', '3'],第2行为 []

map返回的不是列表而是迭代器,每次next()只递送一个下一个值,用尽后就没有了。第1次list()就已把3个值全部取出,因此第2次list()会得到一个什么都不剩的空列表。它既不会报错也不会变成None,而是悄悄变空,因此不容易察觉。map(str, ...)会把各元素转成字符串,因此第1次的内容不是数值,而是['1', '2', '3']。同样的情况也会发生在filter、zip、enumerate以及生成器上,如果要遍历多次,应在拿到后立即用list()固定下来。

第28题 | filter与all

使用filter、all、any的以下代码,其输出是哪一项?

print(list(filter(None, [0, 1, "", 2, None, "a"])))print(all([]), any([]))
  1. [1, 2, 'a'] 与 False False
  2. [0, '', None] 与 True False
  3. [1, 2, 'a'] 与 True False
  4. [1, 2, 'a'] 与 False True
正确答案C. [1, 2, 'a'] 与 True False

给filter的第1个参数传入None,会按元素本身的真假来筛选。0、空字符串、None都会被判为假而被剔除,剩下的是[1, 2, 'a'],不是被剔除的那些留了下来。all的规格是只要没有一个假元素就返回True,因此对一个元素都没有的空列表也会返回True。any的规格是只要有一个真元素就返回True,因此为空时返回False。传入空序列时这两者给出相反的结果,正是容易被考查的地方。

第29题 | 生成器

调用含有yield的函数的以下代码,输出行的顺序是哪一项?

def gen():    print("start")    yield 1    print("mid")    yield 2g = gen()print("created")print(next(g))print(next(g))
  1. 按 start mid created 1 2 的顺序
  2. 按 created start mid 1 2 的顺序
  3. 按 created start 1 mid 2 的顺序
  4. 按 start created 1 mid 2 的顺序
正确答案C. 按 created start 1 mid 2 的顺序

即使调用了含有yield的函数,函数体也一行都不会执行。返回的只是一个生成器对象,因此会先显示created。第一次next(g)才会真正从头开始运行,显示start之后在yield 1处停下并返回1。下一次next(g)会从停下的地方继续,显示mid,在yield 2处停下并返回2。这种延迟执行正是与普通函数的决定性区别,如果再调用一次next(g),函数就会结束并引发StopIteration。

第30题 | is与==

并列使用is运算符与==运算符的以下代码,其输出是哪一项?

a = [1, 2]b = [1, 2]c = aprint(a == b, a is b)print(a == c, a is c)
  1. True False 与 True False
  2. True True 与 True True
  3. False False 与 True True
  4. True False 与 True True
正确答案D. True False 与 True True

==是询问值是否相等的运算符,由于内容相同,a和b会得到True。is是询问是否为同一个对象的运算符,a和b是分别创建的两个列表,因此为False。而 c = a 并没有创建新列表,只是给同一个对象起了别名,因此 a is c 为True。也就是说,通过c改写列表,从a这一侧也能看到变化。None是唯一存在的特殊对象,因此写成 x is None 是惯用写法,但想比较值时应始终使用==。

练习:做本页的题目

这是随机出题的练习工具(在启用 JavaScript 时运行)。即使不使用此工具,也可以阅读上方的全部题目和解说。

※ 解说是供学习用的信息。考试的出题范围和制度每年可能变化,请务必确认主办机构的官方公告。

本页面译自日文原文。如译文与原文内容不一致,以日文版为准。 查看日文原文