Karinoya Learning Room

Qualifications · Cloud / AI / Python Success Lab

Exceptions, Classes, and the Standard Library

Read the questions and explanations in English. The lectures (explanatory articles) are available in Japanese only.

View the Japanese version (with lectures) →

Q1 | Order of a try statement

When the following code is run, what are the lines printed, in the order from top to bottom?

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 and else and except
  2. try and else and finally
  3. try and except and else
  4. try and except and finally
AnswerB. try and else and finally

9 / 3 does not raise an exception, so except is not entered. The else clause runs only when no exception occurred, so else runs after try. The finally clause always runs regardless of whether an exception occurred, so it runs last. So the 3 lines are try, else, finally. A choice that includes except is the order for the case where you mistakenly think an exception occurred; ZeroDivisionError does not occur for this call. A choice missing finally is the ordering you would get if you mistook finally for "cleanup for when an exception occurred."

Q2 | The else clause

Which is the correct condition under which a try statement's else clause runs?

  1. It always runs last, regardless of whether an exception occurred
  2. It runs only when an exception occurred in the try clause
  3. It runs when none of the except clauses matched
  4. It runs only when no exception occurred in the try clause
AnswerD. It runs only when no exception occurred in the try clause

A try statement's else clause runs only when the try clause finished to the end without raising an exception. It is worth remembering that this is the opposite direction from an if statement's else, which is for "when the condition is false": try's else is on the side of "when things went well." What runs when an exception occurred is the except clause. What always runs regardless of whether an exception occurred is instead the finally clause. When none of the except clauses match, it does not fall through to else; that exception propagates on to the caller.

Q3 | finally takes priority

What is the output when the following code is run?

def f():    try:        return "T"    finally:        print("F")def g():    try:        return "T"    finally:        return "N"print(f(), g())
  1. The 2 lines F and T T are printed
  2. The 2 lines F and T N are printed
  3. The 2 lines T T and F are printed
  4. The 2 lines T N and F are printed
AnswerB. The 2 lines F and T N are printed

f() prepares the value it will return, but before it actually returns, it runs the finally clause. So F is printed first, and the return value stays as T. g() has a return inside its finally clause, and that overwrites the try clause's return, so the return value becomes N. print evaluates f() and g() and then displays them together on one line, so the F line comes first, and the T N line comes after. The choice that gives T T is the answer for the case where you think finally's return is ignored, and the choice with F coming later is the answer for the case where you think finally runs after the return.

Q4 | An exception that cannot be caught

Which exception cannot be caught by an except clause written as except Exception?

  1. FileNotFoundError
  2. ZeroDivisionError
  3. KeyboardInterrupt
  4. ModuleNotFoundError
AnswerC. KeyboardInterrupt

KeyboardInterrupt sits directly under BaseException; it does not inherit from Exception. issubclass(KeyboardInterrupt, Exception) is False. It is for when a user tries to stop the program with Ctrl-C, so the hierarchy is separated so it is not accidentally swallowed by a broadly written except Exception. SystemExit sits at the same position. The remaining 3 are all descendants of Exception: ZeroDivisionError is under ArithmeticError, FileNotFoundError is under OSError, and ModuleNotFoundError is under ImportError, so all of them can be caught by except Exception.

Q5 | An exception's parent

What is the nearest common parent class of IndexError and KeyError?

  1. ReferenceError
  2. ArithmeticError
  3. AttributeError
  4. LookupError
AnswerD. LookupError

Both IndexError and KeyError share the same nature of looking something up by an index or a key and failing to find it, so they are placed under the common parent LookupError. Writing except LookupError lets you catch both an out-of-range access on a list and a nonexistent key on a dictionary together. ArithmeticError is the parent of things like ZeroDivisionError, for situations where a computation itself cannot be carried out. AttributeError is for when an attribute cannot be found, and ReferenceError is for when the target of a weak reference has disappeared; both sit directly under Exception and belong to a different lineage from LookupError.

Q6 | Order of except

Which is correct as the output when the following code is run?

try:    x = 8 / 0except ArithmeticError:    print("A")except ZeroDivisionError:    print("Z")
  1. Nothing is printed, and it stops
  2. Only Z is printed
  3. A and Z are printed
  4. Only A is printed
AnswerD. Only A is printed

8 / 0 raises ZeroDivisionError, but ZeroDivisionError is a child class of ArithmeticError. except clauses are matched in order from the top, and matching stops at the first one that matches, so the except ArithmeticError written first catches it, and only A is printed. The later except ZeroDivisionError never gets a turn. Only one matching except runs, so both A and Z being printed does not happen. The principle is to place the narrower one (the child class) first and the broader one (the parent class) later, and this question's code has that order reversed.

Q7 | Multiple exceptions

What is the one line displayed when the following code is run?

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
AnswerA. 42 ValueError TypeError

Writing a tuple in parentheses after except makes it run if any one of them matches. conv("21") gives int("21") as 21, so it returns 42. conv("abc") can be passed as a string but cannot be interpreted as a number, so it becomes ValueError. conv(None) is not a type that can be passed to int(), so it becomes TypeError. The distinction that the type is correct but the value is inappropriate gives ValueError, and the type not matching at all gives TypeError, and that distinction is exactly the order of the answer.

Q8 | Re-raising

What is the output of the following code, which uses a custom exception and a bare 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. Only inner is printed, and outer is not printed
  2. The 2 lines inner and outer Exception are printed
  3. The 2 lines inner and outer LoadError are printed
  4. It prints inner and then stops at LoadError
AnswerC. The 2 lines inner and outer LoadError are printed

Writing just raise, with no argument, inside an except block re-raises the exception currently being handled, as is, to the caller. So after inner is printed, LoadError propagates outside load(), and the outer except Exception catches it. LoadError inherits from Exception, so except Exception can catch it and it does not stop. type(e).__name__ returns the name of the exception class that was actually raised, so it is LoadError that is printed, not the type name of the catching side, Exception. Only inner appearing would be the output for when it was swallowed on the spot without being re-raised.

Q9 | An uncaught exception

What kind of result does running the following code produce?

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. It prints 2 and then stops at KeyError
  2. The 2 lines 2 and not found are printed
  3. The 2 lines 2 and KeyError are printed
  4. The 2 lines 2 and None are printed
AnswerA. It prints 2 and then stops at KeyError

The first call is a list index of 1, so 2 is returned. The second looks up a key that is not in the dictionary, so KeyError is raised, but what is written in except is only IndexError. IndexError and KeyError are both children of LookupError, but they are not parent and child of each other, so an except for IndexError cannot catch a KeyError. The uncaught exception propagates to the caller, and since there is no one to receive it, the program stops. If you want to catch both, write except LookupError, or line up both in a tuple.

Q10 | assert

Which of the following correctly describes the assert statement?

  1. If the condition is false, it raises AssertionError, and it is disabled under python3 -O
  2. If the condition is false, it raises AssertionError, and it always runs even under python3 -O
  3. If the condition is true, it raises AssertionError, and it is disabled under python3 -O
  4. If the condition is false, it raises ValueError, and it always runs even under python3 -O
AnswerA. If the condition is false, it raises AssertionError, and it is disabled under python3 -O

assert condition, message raises AssertionError when the condition is false. When the condition is true, nothing happens. The important point is that when started in an optimized mode such as python3 -O, the assert statement is removed entirely, and not even a single line of it runs. So assert must not be used for the purpose of validating user input; for that purpose, you check the condition with if and raise ValueError(...) instead. It is best to remember assert as a tool for leaving a record of internal assumptions during development, and to keep that role separate.

Q11 | self

Which of the following correctly describes self, the first parameter of a method?

  1. A name reserved for referring to the attributes of a parent class
  2. An argument that automatically receives the instance itself on which it was called
  3. An argument for creating an instance and returning it to the caller
  4. A reserved name that automatically receives the class itself
AnswerB. An argument that automatically receives the instance itself on which it was called

When you write d.speak(), Python reinterprets it as Dog.speak(d) before calling it. What goes into the position of the first argument here is the instance itself that made the call. The spelling self is merely a convention; it is not a name reserved by the language. Since only the position of the first argument receiving the instance itself is specified by the language, writing Dog.speak(d) yourself works the same way too. Creating and returning an instance is the job of __new__ and the mechanism behind calling a class, and __init__ merely initializes an instance that has already been created. To refer to a parent class, super() is used.

Q12 | Class variables

What is the output of the following code, which rewrites a class variable?

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
AnswerA. wolf wolf

kind is a class variable written directly under class, and every instance created from that class shares the same single one. Neither a nor b has kind in its own __dict__, so when reading the attribute, they both go up to the class and see the same value. An assignment to Dog.kind rewrites that one shared value, so wolf becomes visible from both a and b, which were already created. Because the value is not copied at the time an instance is created, canine remaining is not one of the choices.

Q13 | A shadow on the attribute

What is the output of the following code, which assigns on the instance side?

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, and ['name']
  2. husky canine canine, and ['kind', 'name']
  3. husky canine canine, and ['name']
  4. husky husky husky, and ['kind', 'name']
AnswerB. husky canine canine, and ['kind', 'name']

Assigning to a.kind does not rewrite the class variable; it creates a new entry called kind in a's instance dictionary. From then on, reading a.kind finds this new entry first, casting a shadow over the class variable. b and Dog.kind stay as the original canine. So a.__dict__ has kind lined up alongside the name put in by __init__, and sorting it gives ['kind', 'name']. The choice where everything becomes husky is the answer for the case where you mistakenly think assigning to the instance rewrites the class variable, and the choice giving only ['name'] is the answer for the case where you think no shadow is created.

Q14 | Shared

What is the output of the following code, which makes a list a class variable?

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 is [1], q.items is [1, 2]
  2. p.items is [1, 2], q.items is [2]
  3. p.items is [1, 2], q.items is [1, 2]
  4. p.items is [1], q.items is [2]
AnswerC. p.items is [1, 2], q.items is [1, 2]

self.items.append(x) is not an assignment to self.items; it is just calling a method on the object reached via self.items. Neither p nor q has its own items, so what they both reach is the very same single list held as the class variable. As a result, both the 1 added via p and the 2 added via q go into that same list, and it comes out as [1, 2] no matter which one you look at. If you want a separate container for each instance, you assign it inside __init__ by writing self.items = []. Then it would split into [1] and [2].

Q15 | super()

What is the output of the following code, which uses inheritance and 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
AnswerD. Tama meow black True

Cat's __init__ calls super().__init__(name), so the parent, Animal, sets self.name for it. After that, self.color is set to black, so both attributes end up in place. cry is redefined in the child, so the parent's implementation is overridden, and meow is what gets called instead. isinstance(c, Animal) is True, since Cat does inherit from Animal. If you forget to write super(), name would never get set and it would become AttributeError, so it does not fail quietly in a way that would give a result of None.

Q16 | MRO

What is the output of the following code, which uses multiple inheritance?

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, and ['D', 'B', 'C', 'A', 'object']
  2. C, and ['D', 'C', 'B', 'A', 'object']
  3. B, and ['D', 'B', 'A', 'C', 'object']
  4. A, and ['D', 'B', 'C', 'A', 'object']
AnswerA. B, and ['D', 'B', 'C', 'A', 'object']

Method resolution order is decided by a rule called C3 linearization, not simple depth-first search. The common parent A is placed after every child that inherits from A, so the order becomes D, B, C, A, object. If it were depth-first, going into B and then straight to A, it would be D, B, A, C, object, but that is not what happens, and this is the point to be careful of. who() searches from the front of the MRO and uses the first one it finds, so B is what is returned. If you write class E(C, B), it becomes E, C, B, A, object; whichever is written first takes priority.

Q17 | An MRO conflict

With class B(A) already defined, what happens if you write class F(A, B): pass?

  1. The definition succeeds, and F().who() calls A's version
  2. The definition succeeds, and __mro__ becomes F, A, B, object
  3. The definition succeeds, and F().who() calls B's version
  4. A TypeError occurs at the point of the class definition
AnswerD. A TypeError occurs at the point of the class definition

C3 linearization has two rules: preserve the order in which the base classes are listed, and place a parent after its children. F(A, B) specifies putting A before B, but B is a child of A, so the parent A must come after the child B. Since these two cannot be satisfied at the same time, it is judged that a consistent MRO cannot be built, and a TypeError occurs at the moment the class definition is evaluated. It fails the instant the class statement is processed, not only once you try running it. Writing it correctly means writing the child first, as in class F(B, A).

Q18 | Name mangling

What is the output of the following code, which has attributes starting with an underscore?

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, and ['_one', '_two']
  2. 1 2, and ['_P__one', '_P__two']
  3. 1 2, and ['__two', '_one']
  4. 1 2, and ['_P__two', '_one']
AnswerD. 1 2, and ['_P__two', '_one']

_one, which starts with a single underscore, is only a convention indicating it is for internal use, and its name does not change; it can be read normally from outside. __two, which starts with two underscores, undergoes name mangling, and its actual attribute name is rewritten to _P__two, that is, _ClassName__name. So writing p.__two results in AttributeError, but writing p._P__two lets you read it. What appears in __dict__ is also the mangled name, so sorting gives ['_P__two', '_one']. Only names starting with two underscores are mangled; _one is not subject to it.

Q19 | Special methods

What is the output of the following code, which defines special methods?

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), and [Vec(x=1, y=2)], and 2 V(4, 6)
  2. V(1, 2), and [Vec(x=1, y=2)], and 2 V(4, 6)
  3. V(1, 2), and [Vec(x=1, y=2)], and 2 Vec(x=4, y=6)
  4. V(1, 2), and [V(1, 2)], and 2 V(4, 6)
AnswerB. V(1, 2), and [Vec(x=1, y=2)], and 2 V(4, 6)

print(v) and str(v) use __str__, so it becomes V(1, 2). When displayed inside a container such as a list or dictionary, however, __repr__ is used for each element, so it becomes [Vec(x=1, y=2)]. This distinction in usage is the key point of this question. len(v) is the 2 returned by __len__, and v + Vec(3, 4) is a new Vec returned by __add__; passing that to print uses __str__ again, so V(4, 6) is displayed. In a class where __str__ is not defined, __repr__ is used as a substitute, but the reverse — __str__ being used in place of __repr__ — never happens.

Q20 | __eq__

What is the output of the following code, which defines only __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, and False, and 2
  2. True False, and True, and TypeError
  3. True False, and False, and 1
  4. True True, and True, and TypeError
AnswerB. True False, and True, and TypeError

Since __eq__ is defined, p == q compares the attribute x and gives True, but p and q are separately created objects, so p is q stays False. The important point here is that a class that defines __eq__ automatically has its __hash__ set to None, so Point.__hash__ is None is True. An object with no hash cannot be used in a set or as a dictionary key, so trying to create {p, q} results in TypeError. If you also want to be able to put it into a set, you need to define your own __hash__ that returns the same value for equal objects.

Q21 | math

What is the output of the following code, which uses the math module?

import mathprint(math.sqrt(16))print(math.floor(-1.5), math.ceil(-1.5))
  1. 4.0, and -2 -1
  2. 4.0, and -1 -2
  3. 4, and -2 -1
  4. 4, and -1 -1
AnswerA. 4.0, and -2 -1

math.sqrt returns a float even when the value divides evenly, so the square root of 16 is displayed not as 4 but as 4.0. math.floor rounds down toward the number line, so -1.5 becomes -2, and math.ceil rounds up, so -1.5 becomes -1. Note that with negative numbers, int(-1.5), which rounds toward 0, gives -1, which disagrees with floor's result. Mixing up floor and ceil gives the order -1 -2, and assuming the return type of sqrt is int gives 4.

Q22 | os.path

What is the combination of values returned by os.path.splitext('/tmp/z.txt') and os.path.basename('/tmp/z.txt')?

  1. ('/tmp/z', '.txt'), and 'z.txt'
  2. ('/tmp', 'z.txt'), and '/tmp'
  3. ('/tmp/z', 'txt'), and 'z.txt'
  4. ('/tmp/z.txt', ''), and 'z.txt'
AnswerA. ('/tmp/z', '.txt'), and 'z.txt'

os.path.splitext splits off only the extension, keeping the dot attached to the extension side, returning a 2-element tuple. So it becomes ('/tmp/z', '.txt'). The dot is not dropped, giving 'txt'; that does not happen. os.path.basename returns the name at the tail of the path, so it is 'z.txt'. What returns the directory side, '/tmp', is instead os.path.dirname, and when you want a tuple with both together, you use os.path.split. It helps to remember the distinction that splitext splits on the extension, not on a directory separator.

Q23 | Matching with re

What is the output of the following code, which uses the re module?

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

re.match only matches from the start of the string, so since the start of 'abc' is 'a', it does not match 'b' and returns None. re.search searches even partway through, so it matches 'b', and .group() extracts the matched string b. This difference is the most commonly tested point about re. re.findall returns a list gathering all the parts that matched the pattern, and since \d+ means consecutive digits, it becomes ['1', '22', '333']. The separator characters are not included. If there is no match, it returns an empty list.

Q24 | datetime

What is the output of the following code, which uses 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, and 2026/08/22, and 2026/09/01
  2. 2026-08-22, and 2026/08/22, and 2026-09-01
  3. 2026-08-22, and 2026/08/22, and 10 days, 0:00:00
  4. 2026-08-22, and 2026/08/22, and 2026-08-32
AnswerB. 2026-08-22, and 2026/08/22, and 2026-09-01

Printing a date object directly displays it in ISO format, with hyphens, as 2026-08-22. strftime is what is used to change the format, and passing it '%Y/%m/%d' returns the slash-separated string 2026/08/22. Adding timedelta(days=10) to a date returns not a timedelta but a new date pointing 10 days later, so printing it gives 2026-09-01. Computation that crosses a month boundary is taken care of on the datetime side, so it does not become a nonexistent date like 2026-08-32, which you would get by just adding 10 to the day number. If you think print(d) uses the same format as strftime, you get slash-separated output lined up, but the default display uses hyphens. When you subtract one date from another, what is returned is not a date but a timedelta, and its display takes a form like 10 days, 0:00:00.

Q25 | Counter

What is the output of the following code, which uses collections.Counter?

from collections import Counterc = Counter("abracadabra")print(c.most_common(2))print(c["a"], c["z"])
  1. [('a', 5), ('b', 2)], and 5, and a KeyError
  2. [('a', 5), ('r', 2)], and 5 0
  3. [('a', 5), ('b', 2)], and 5 0
  4. [('a', 5), ('b', 2), ('r', 2)], and 5 0
AnswerC. [('a', 5), ('b', 2)], and 5 0

'abracadabra' contains a 5 times, b and r 2 times each, and c and d once each. most_common(2) returns just the top 2 by count, as a list of element-and-count tuples, so it is [('a', 5), ('b', 2)]. Between b and r, which are tied in count, whichever appeared first comes first, so b is chosen. Counter is a kind of dictionary, but looking up an element that is not present does not raise KeyError; it returns 0, which is a difference from an ordinary dictionary. This design means you do not need to check for existence before tallying.

Q26 | namedtuple

What is the output of the following code, which uses a type built with 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), and 1 1 True
  2. Pt(x=1, y=2), and 1 1 False
  3. Pt(x=1, y=2), and 1 1 True
  4. Pt(1, 2), and 1 1 False
AnswerC. Pt(x=1, y=2), and 1 1 True

A type made by namedtuple takes the form Pt(x=1, y=2), with the type name and field names attached, when displayed. The value can be read either by name or by index, so p.x and p[0] are both 1. And since namedtuple is a subtype of tuple, isinstance(p, tuple) is True. That is, it is a kind of tuple, and its elements cannot be rewritten afterward. If you want to hold a value you can change, you either use _replace, which creates a new instance with a value swapped in, or you define an ordinary class.

Q27 | Single use

What is the output of the following code, which passes the same map object to list() twice?

m = map(str, [1, 2, 3])print(list(m))print(list(m))
  1. Line 1 is ['1', '2', '3'], line 2 is None
  2. Line 1 is ['1', '2', '3'], line 2 is []
  3. Line 1 is [1, 2, 3], line 2 is []
  4. Both line 1 and line 2 are ['1', '2', '3']
AnswerB. Line 1 is ['1', '2', '3'], line 2 is []

What map returns is not a list but an iterator; each call to next() hands over the next value, and once it is exhausted, that is it. Since the first list() call takes out all 3 values, the second list() call has nothing left, and it becomes an empty list. It does not become an error or None; it just quietly becomes empty, which makes it easy to miss. map(str, ...) turns each element into a string, so the first result is not numbers but ['1', '2', '3']. The same thing happens with filter, zip, enumerate, and generators, so if you plan to iterate more than once, fix it into a list with list() right after receiving it.

Q28 | filter and all

What is the output of the following code, which uses filter, all, and any?

print(list(filter(None, [0, 1, "", 2, None, "a"])))print(all([]), any([]))
  1. [1, 2, 'a'], and False False
  2. [0, '', None], and True False
  3. [1, 2, 'a'], and True False
  4. [1, 2, 'a'], and False True
AnswerC. [1, 2, 'a'], and True False

Passing None as the first argument to filter filters by the truthiness of the element itself. 0, an empty string, and None all evaluate as false, so they drop out, and what remains is [1, 2, 'a']. It is not the case that the dropped ones remain. all is specified to return True as long as there is not even one false element, so for an empty list, with no elements at all, it is True. any returns True if there is at least one true element, so for an empty one it is False. The point that these two give opposite answers when passed an empty sequence is often tested.

Q29 | A generator

What is the order of the lines printed by the following code, which calls a function containing yield?

def gen():    print("start")    yield 1    print("mid")    yield 2g = gen()print("created")print(next(g))print(next(g))
  1. In the order start mid created 1 2
  2. In the order created start mid 1 2
  3. In the order created start 1 mid 2
  4. In the order start created 1 mid 2
AnswerC. In the order created start 1 mid 2

Even calling a function that contains yield does not run a single line of its body. What comes back is only a generator object, so created is printed first. Only with the first next(g) does it finally start moving from the top, printing start, then stopping at yield 1 and returning 1. The next next(g) resumes from where it stopped, prints mid, and then stops at yield 2, returning 2. This lazy execution is the decisive difference from an ordinary function, and calling next(g) once more finishes the function, resulting in StopIteration.

Q30 | is and ==

What is the output of the following code, which lines up the is operator and the == operator?

a = [1, 2]b = [1, 2]c = aprint(a == b, a is b)print(a == c, a is c)
  1. True False, and True False
  2. True True, and True True
  3. False False, and True True
  4. True False, and True True
AnswerD. True False, and True True

== is the operator that asks whether the values are equal, so since a and b have the same contents, it is True. is is the operator that asks whether it is the same object, and since a and b are two separately created lists, it is False. Meanwhile, c = a does not create a new list; it just gives another name to the same object, so a is c is True. That is, changing the list through c is also visible through a. None is a special object that exists only once, so writing x is None is the idiomatic way, but when you want to compare values, always use ==.

Practice: answer the questions on this page

This practice tool asks questions in random order (it works when JavaScript is enabled). You can still read all the questions and explanations above without it.

* The explanations are information for study purposes. Exam scope and systems change from year to year, so always check the official announcements of the organization that administers the exam.

This page is a translation of the Japanese original. If the translation and the original differ, the Japanese version takes precedence. View the Japanese original