Python 3 エンジニア認定基礎試験 例外とクラス 問題

ゆめさくが作った練習問題です。本試験の問題ではありません。

例外とクラスの問題を1⁠5問、小分野ごとにまとめました。選ぶと、その場で正誤と解説が出ます。

ログインなしで解けます。答えはこのブラウザの中だけに残り、どこにも送りません。

この分野の小分野

  • エラーと例外(10問)
  • クラス(5問)

例外とクラスの問題

15 問

選ぶと、すぐに正誤と解説が出ます。選んだ答えは変えられません。

  1. エラーと例外

    問 1構文エラーと例外の違いについて、正しい説明はどれですか。

  2. 問 2次のコードを実行したときの出力として正しいものはどれですか。

    for s in ['7', 'x']:
        try:
            n = int(s)
            print('A', end='')
        except ValueError:
            print('B', end='')
        print('C', end='')
  3. 問 3次のコードを実行したときの出力として正しいものはどれですか。

    def inner():
        print('1', end='')
        int('x')
        print('2', end='')
    
    def outer():
        try:
            inner()
            print('3', end='')
        except TypeError:
            print('T', end='')
        print('4', end='')
    
    try:
        outer()
    except ValueError:
        print('V', end='')
    print('5', end='')
  4. 問 4int(v) で ValueError と TypeError のどちらが起きても、同じ except 節で処理したいとします。Python 3.9 でも 3.12 でも正しく動く書き方はどれですか。

  5. 問 5次のコードを実行したときの出力として正しいものはどれですか。

    def check(n):
        try:
            r = 10 // n
        except ZeroDivisionError:
            print('E', end='')
            return
        else:
            print('L', end='')
        finally:
            print('F', end='')
        print('X', end='')
    
    check(2)
    check(0)
  6. 問 6次のコードを実行したときの結果として正しいものはどれですか。

    def g():
        try:
            1 / 0
        finally:
            return 'F'
    
    print(g())
  7. 問 7except 節の中で、いま処理している例外を記録したあと、その例外をそのまま呼び出し元へ送り直したいとします。except 節の最後に書く文として正しいものはどれですか。

  8. 問 8except ValueError as exc の節の中で raise RuntimeError('読み込み失敗') from exc と書きました。このとき起きることとして正しいものはどれですか。

  9. 問 9次のコードを実行したときの出力として正しいものはどれですか。

    class LimitError(Exception):
        pass
    
    try:
        raise LimitError('上限', 5)
    except LimitError as e:
        print(e.args[1], e)
  10. 問 10with open('data.txt') as f のブロックの中で、行の処理中に例外が起きたとします。ファイル f について正しい説明はどれですか。

  11. クラス

    問 11次のコードを実行したときの出力として正しいものはどれですか。

    x = 'g'
    
    def outer():
        x = 'o'
        def inner():
            nonlocal x
            x = 'n'
        def other():
            global x
            x = 'G'
        inner()
        other()
        print(x, end=' ')
    
    outer()
    print(x)
  12. 問 12次のコードを実行したときの出力として正しいものはどれですか。

    class Team:
        n = 0
        tags = []
    
    a = Team()
    b = Team()
    a.n = 5
    a.tags.append('x')
    print(b.n, b.tags)
  13. 問 13次のコードを実行したときの出力として正しいものはどれですか。

    class Greeter:
        def __init__(self, name):
            self.name = name
        def hello(self):
            return 'hi ' + self.name
    
    f = Greeter('ken').hello
    g = Greeter('mai')
    print(f(), Greeter.hello(g))
  14. 問 14次のコードを実行したときの出力として正しいものはどれですか。

    class Base:
        def __init__(self):
            self.__v = 'B'
        def show(self):
            return self.__v
    
    class Child(Base):
        def __init__(self):
            super().__init__()
            self.__v = 'C'
    
    c = Child()
    print(c.show(), isinstance(c, Base), issubclass(Base, Child))
  15. 問 15yield を含むジェネレータ関数 gen を、it = gen() のように呼び出したときの動きとして正しいものはどれですか。

15 問中 0 問に答えました

解説の一覧

解いたあとに読み返せるよう、全問の解説を並べています。問いを開くと解説が出ます。

エラーと例外

問1構文エラーと例外の違いについて、正しい説明はどれですか。

正解は「構文エラーは実行の前の解析で見つかるので、そのファイルの文は 1 つも実行されない」です。構文エラーはパーサがコードを解析する段階で見つかるエラーです。スクリプトはまず全体が解析されるので、たとえば 1 行目に print('a')、2 行目に while True print('b') と書いたファイルを動かすと、a すら表示されずに SyntaxError で止まります。一方、例外は文法が正しい文を実行している最中に起きるエラーで、try 文で捕まえられます。try 文で囲んでも、同じファイルの構文エラーは実行前に見つかるので捕まえられません。文法が正しくても 1⁠0 / 0 のように実行中に例外は起きるので、例外が書き間違いを表すとは限りません。ZeroDivisionError や NameError は実行中に起きる例外で、構文エラーではありません。 出典 https://docs.python.org/ja/3/tutorial/errors.html#syntax-⁠errors

問2次のコードを実行したときの出力として正しいものはどれですか。
for s in ['7', 'x']:
    try:
        n = int(s)
        print('A', end='')
    except ValueError:
        print('B', end='')
    print('C', end='')

正解は「ACBC」です。try 節の途中で例外が起きると、try 節の残りは飛ばされ、型が合う except 節が動いたあと try 文の後ろへ進みます。1 回目の s は '7' なので int('7') は成功し、A を出してから try 文の後ろの C を出します。2 回目の s は 'x' なので int('x') で ValueError が起き、try 節に残る print('A') は飛ばされます。except 節で B を出し、try 文の後ろの C を出します。並べると ACBC です。ACC は 2 回目でも A まで進むと考えた誤りで、例外の後ろの文は動きません。ABCBC は例外が起きても try 節を最後まで動かすと考えた誤りです。ACB は except 節のあと処理が終わると考えた誤りで、except 節のあとは try 文の後ろへ続きます。 出典 https://docs.python.org/ja/3/tutorial/errors.html#handling-⁠exceptions

問3次のコードを実行したときの出力として正しいものはどれですか。
def inner():
    print('1', end='')
    int('x')
    print('2', end='')

def outer():
    try:
        inner()
        print('3', end='')
    except TypeError:
        print('T', end='')
    print('4', end='')

try:
    outer()
except ValueError:
    print('V', end='')
print('5', end='')

正解は「1V5」です。except 節で指定した型と合わない例外は、その try 文の外側へ渡されます。関数の中で処理されなかった例外は、呼び出し元の try 文まで伝わります。inner() は 1 を出したあと int('x') で ValueError を起こし、後ろの print('2') は動きません。outer() の try 文には except TypeError しか無く型が合わないので、例外はそのまま outer() の外へ出ていき、outer() の中の print('3') も print('4') も飛ばされます。一番外側の except ValueError が受け取って V を出し、そのあと 5 を出します。よって 1V5 です。1T4⁠5 は型が違っても except 節が動くと考えた誤りです。1V4⁠5 は outer() の try 文の後ろの文が動くと考えた誤りで、outer() は例外で抜けています。1⁠3V5 は呼び出しから戻った直後の文が動くと考えた誤りです。 出典 https://docs.python.org/ja/3/tutorial/errors.html#handling-⁠exceptions

問4int(v) で ValueError と TypeError のどちらが起きても、同じ except 節で処理したいとします。Python 3.9 でも 3.1⁠2 でも正しく動く書き方はどれですか。

正解は「except (ValueError, TypeError):」です。except 節の式は、例外のクラスか、例外のクラスを並べたタプルでなければなりません。丸括弧で囲んでタプルにすると、どちらの例外にもマッチします。int('x') の ValueError も int(None) の TypeError も、この except 節で捕まえられます。ValueError or TypeError は先に式として評価され、結果は ValueError だけになるので TypeError は捕まえられません。ValueError and TypeError も同じように評価され、結果は TypeError だけです。リストで並べた形は例外のクラスでもタプルでもないので、例外とマッチさせる時点で TypeError が起きます。なお丸括弧を省ける書き方は 3.1⁠4 からで、3.9 と 3.1⁠2 では構文エラーです。 出典 https://docs.python.org/ja/3/reference/compound_stmts.html#except-⁠clause

問5次のコードを実行したときの出力として正しいものはどれですか。
def check(n):
    try:
        r = 10 // n
    except ZeroDivisionError:
        print('E', end='')
        return
    else:
        print('L', end='')
    finally:
        print('F', end='')
    print('X', end='')

check(2)
check(0)

正解は「LFXEF」です。else 節は try 節で例外が起きなかったときだけ動き、finally 節は try 文を抜けるときに必ず動きます。check(2) では 1⁠0 // 2 が成功するので else 節の L、finally 節の F、try 文の後ろの X の順に出て LFX です。check(0) では ZeroDivisionError が起きて except 節の E を出し、return で関数を抜けようとします。抜ける前に finally 節が動いて F を出しますが、return なので後ろの X は動きません。合わせて LFXEF です。LFXEFX は return のあとも X まで進むと考えた誤りです。LXFEF は finally 節が try 文の後ろの文より後に動くと考えた誤りです。LFXE は return で抜けるときは finally 節が動かないと考えた誤りです。 出典 https://docs.python.org/ja/3/tutorial/errors.html#defining-⁠clean-⁠up-⁠actions

問6次のコードを実行したときの結果として正しいものはどれですか。
def g():
    try:
        1 / 0
    finally:
        return 'F'

print(g())

正解は「F と表示され、例外では止まらない」です。try 節で起きた例外を処理する except 節が無いとき、その例外はふつう finally 節が動いたあとで再び送出されます。ただし finally 節が return を実行した場合は例外が再送出されず、return の値がそのまま関数の戻り値になります。このコードでは 1 / 0 の ZeroDivisionError が捨てられ、g() は 'F' を返すので F と表示されて正常に終わります。混乱を招くので勧められない書き方です。ZeroDivisionError で止まる、F と表示したあと止まる、の 2 つは finally 節の return でも例外が再送出されると考えた誤りです。None は return が無視されると考えた誤りで、実際は finally 節の return の値が返ります。 出典 https://docs.python.org/ja/3/tutorial/errors.html#defining-⁠clean-⁠up-⁠actions

問7except 節の中で、いま処理している例外を記録したあと、その例外をそのまま呼び出し元へ送り直したいとします。except 節の最後に書く文として正しいものはどれですか。

正解は「raise」です。except 節の中で式を付けずに raise とだけ書くと、いま処理している例外がそのまま再送出されます。例外が起きたことを記録はしたいが、処理は呼び出し側に任せたい、というときの書き方です。たとえば int('abc') の ValueError を except 節で受けて raise すると、呼び出し側の except ValueError で同じ ValueError を受け取れます。raise Exception() は新しい別の例外を作って送るので、元の例外の型が Exception に変わってしまいます。return e は例外を戻り値として返すだけで、例外は送出されません。throw は Python のキーワードではないので構文エラーになります。 出典 https://docs.python.org/ja/3/tutorial/errors.html#raising-⁠exceptions

問8except ValueError as exc の節の中で raise RuntimeError('読み込み失敗') from exc と書きました。このとき起きることとして正しいものはどれですか。

正解は「RuntimeError が送出され、その __cause__ 属性に exc が入る」です。raise 文の from 節は例外の連鎖に使います。送出されるのは raise の直後に書いた RuntimeError で、from の後ろに書いた例外インスタンス exc は、送出される例外の __cause__ 属性に入ります。こうすると RuntimeError が ValueError の直接の結果であることがトレースバックにも示されます。呼び出し側で RuntimeError を捕まえて type(e.__cause__).__name__ を表示すると ValueError と出ます。exc が送出されるという説明は向きが逆です。例外が 2 つ同時に送出されることはありません。元の例外の情報を捨てるのは from None と書いた場合で、from exc では残ります。 出典 https://docs.python.org/ja/3/reference/simple_stmts.html#the-⁠raise-⁠statement

問9次のコードを実行したときの出力として正しいものはどれですか。
class LimitError(Exception):
    pass

try:
    raise LimitError('上限', 5)
except LimitError as e:
    print(e.args[1], e)

正解は「5 ('上限', 5)」です。ユーザー定義の例外は Exception を継承したクラスとして作ります。例外を作るときに渡した引数は、例外インスタンスの args 属性にタプルとして入ります。ここでは args が ('上限', 5) なので、e.args[1] は 5 です。また組み込みの例外は __str__ を定義しており、print(e) は args を表示します。引数が 2 つ以上あるときはタプルの形 ('上限', 5) のまま表示されます。よって 5 ('上限', 5) です。5 上限 5 は引数が空白でつながって表示されると考えた誤りです。5 LimitError は print(e) でクラス名が出ると考えた誤りです。上限 ('上限', 5) は添字 1 を最初の要素と取り違えた誤りで、添字は 0 から数えます。 出典 https://docs.python.org/ja/3/tutorial/errors.html#handling-⁠exceptions

問10with open('data.txt') as f のブロックの中で、行の処理中に例外が起きたとします。ファイル f について正しい説明はどれですか。

正解は「例外が起きても起きなくても、ブロックを抜けるときに必ず閉じられる」です。with 文は、ファイルのようにクリーンアップの処理が決まっているオブジェクトを、使い終わったらすぐ正しく片付けることを保証します。ブロックの中で問題が起きても起きなくても、with 文を抜けるときにファイル f は必ず close されます。たとえばブロックの中で 1 / 0 を起こし、外側の except 節で f.closed を表示すると True になります。例外のときに自分で close を呼ぶ必要があるのは、with 文を使わずに open した場合です。プログラムの終わりまで開いたままになりうるのは、for line in open(...) のように with を使わない書き方の問題点として説明されています。正常に終わったときも with 文はファイルを閉じます。 出典 https://docs.python.org/ja/3/tutorial/errors.html#predefined-⁠clean-⁠up-⁠actions

クラス

問11次のコードを実行したときの出力として正しいものはどれですか。
x = 'g'

def outer():
    x = 'o'
    def inner():
        nonlocal x
        x = 'n'
    def other():
        global x
        x = 'G'
    inner()
    other()
    print(x, end=' ')

outer()
print(x)

正解は「n G」です。nonlocal 文は、名前が外側の関数のスコープにあり、そこで再束縛されることを示します。global 文は、名前がモジュールのグローバルスコープにあり、そこで再束縛されることを示します。inner() は nonlocal x により outer の x を 'n' に変えます。other() は global x によりモジュールの x を 'G' に変えますが、outer の x には触れません。そのため outer の中の print は n、最後の print は G を出します。G G は global が outer の x も書き換えると考えた誤りです。o G は nonlocal でも outer の x が変わらないと考えた誤りです。n g は global x への代入がモジュールの x に届かないと考えた誤りです。 出典 https://docs.python.org/ja/3/tutorial/classes.html#python-⁠scopes-⁠and-⁠namespaces

問12次のコードを実行したときの出力として正しいものはどれですか。
class Team:
    n = 0
    tags = []

a = Team()
b = Team()
a.n = 5
a.tags.append('x')
print(b.n, b.tags)

正解は「0 ['x']」です。クラス変数はすべてのインスタンスで共有され、インスタンス変数はインスタンスごとに別々です。a.n = 5 は代入なので、a に n というインスタンス変数が新しく作られるだけで、クラス変数 Team.n は 0 のままです。b.n はインスタンス変数が無いのでクラス変数を見て 0 です。一方 a.tags.append('x') は代入ではなく、クラス変数のリストそのものを変えます。b.tags も同じリストを指すので ['x'] です。5 ['x'] と 5 [] は、a.n = 5 がクラス変数を書き換えると考えた誤りです。0 [] は、append でもインスタンスごとに別のリストが変わると考えた誤りです。リストのような変更できるオブジェクトをクラス変数にすると、このように全インスタンスで共有されます。 出典 https://docs.python.org/ja/3/tutorial/classes.html#class-⁠and-⁠instance-⁠variables

問13次のコードを実行したときの出力として正しいものはどれですか。
class Greeter:
    def __init__(self, name):
        self.name = name
    def hello(self):
        return 'hi ' + self.name

f = Greeter('ken').hello
g = Greeter('mai')
print(f(), Greeter.hello(g))

正解は「hi ken hi mai」です。インスタンスからメソッドを参照すると、インスタンスと関数をまとめたメソッドオブジェクトになり、変数に入れて後から呼べます。f は Greeter('ken') と結びついたメソッドオブジェクトなので、f() はそのインスタンスを最初の引数として渡し hi ken を返します。また x.f() は Class.f(x) と同じ意味なので、Greeter.hello(g) は g を self として渡し hi mai を返します。hi mai hi mai は、f が最後に作ったインスタンスを見ると考えた誤りです。TypeError で止まるのは、f() に引数が足りないと考えた誤りで、インスタンスは自動で渡されます。hi ken hi ken は、Greeter.hello(g) の g が無視されると考えた誤りです。 出典 https://docs.python.org/ja/3/tutorial/classes.html#method-⁠objects

問14次のコードを実行したときの出力として正しいものはどれですか。
class Base:
    def __init__(self):
        self.__v = 'B'
    def show(self):
        return self.__v

class Child(Base):
    def __init__(self):
        super().__init__()
        self.__v = 'C'

c = Child()
print(c.show(), isinstance(c, Base), issubclass(Base, Child))

正解は「B True False」です。先頭にアンダースコアが 2 つある __v は名前マングリングにより、Base の中では _Base__v、Child の中では _Child__v に置き換わります。Child() では super().__init__() で _Base__v に 'B'、続けて _Child__v に 'C' が入り、2 つは別の属性になります。show は Base の中で書かれているので _Base__v を読み B を返します。c は Child のインスタンスで Child は Base の派生クラスなので isinstance(c, Base) は True、Base は Child のサブクラスではないので issubclass(Base, Child) は False です。C を選ぶのは、子の self.__v が親の属性を上書きすると考えた誤りです。True True は issubclass の向きを逆に読んだ誤りです。 出典 https://docs.python.org/ja/3/tutorial/classes.html#private-⁠variables

問15yield を含むジェネレータ関数 gen を、it = gen() のように呼び出したときの動きとして正しいものはどれですか。

正解は「next(it) を呼ぶまで本体は動かず、止まった位置と局所変数を覚えて再開する」です。ジェネレータ関数を呼ぶと、本体はまだ動かずにジェネレータというイテレータが返ります。next() を呼ぶと本体が動き出し、最初の yield まで進んで値を返して一時停止します。停止中も局所変数と止まった位置は保たれ、次の next() でその続きから再開します。たとえば本体の先頭で a を表示するジェネレータを it = gen() と作り、c を表示してから next(it) を呼ぶと、c、a の順に出ます。本体が最後まで動いてリストを返すのは、list(gen()) のように全部取り出した場合です。毎回先頭からやり直すことはなく、続きから再開します。ジェネレータでは __iter__ と __next__ が自動で作られるので、自分で定義しなくても for 文で使えます。 出典 https://docs.python.org/ja/3/reference/expressions.html#yield-⁠expressions

ほかの分野

Python 3 エンジニア認定基礎試験の頁に、6分野の入口と試験の案内があります。

続きはゆめさくで

試験の知識を仕事につなげる一歩を、ここにまとめています。