Python3 エンジニア認定基礎試験の認定模擬問題(PRIME STUDY)を解説④

849



前回の続きで「Python3 エンジニア認定模擬問題の解説」です。
問31-40までです。

PRIME STUDY様の認定模擬問題のリンクはこちらです→https://study.prime-strategy.co.jp/


問31.
次の実行結果を得たい場合、コードの【A】【B】【C】【D】【E】に入る組み合わせとして適切なものはどれか。


[ 実行結果 ]
Magatama is a
Saya's
reliable
partner

[コード]
class kusanagi(Exception):
    pass

def raise_character(a):
    print("【A】")
    raise kusanagi
    print("【B】")

def func(name: int):
    try:
        print(name, "【C】")
        raise_character(name)
    except kusanagi:
        print("【D】")
        raise Exception

name = "Magatama"
try:
    func(name)
except Exception:
    print("【E】")

・【A】reliable【B】Saya's【C】is a【D】goofy【E】partner
・【A】reliable【B】goofy【C】is a【D】Saya's【E】partner
・【A】Saya's【B】reliable【C】is a【D】goofy【E】partner
・【A】Saya's【B】goofy【C】is a【D】reliable【E】partner
・【A】goofy【B】reliable【C】is a【D】Saya's【E】partner

解説:
ちょっとややこしい(めんどくさい)問題です。
内容としては例外処理に関して問われていますね。
ポイントは「raise」文で、これは自分でわざと例外を発生させることが出来ます。
「raise kusanagi」だと「kusanagi」という例外を発生させています。
この「kusanagi」はPythonに組み込まれている例外ではなく、プログラムの中で作られている例外です。
そう、例外は自分で作ることもできるのです。
書き方は、クラスとほとんど同じで、例外名(クラス名)と()の中にExceptionと書くだけです。
(Exceptionとはシステム終了以外の例外の派生元です。つまり例外クラスとはExceptionクラスを継承したクラスのことをそう呼んでいるに過ぎません)

class 例外名(Exception):
    """中身はなくて良いので基本的にpassだけ書く"""
pass

みたいな感じです。

以上のことを元に、プログラムの流れをおっかけていきましょう。
まず、最初に処理が実行されるのはtryの中の「func(name)」です。
(下から三行目ですね)
関数funcに、nameが指定されています。
nameは少し上で「"Magatama"」が代入されていますので、「func("Magatama")」ということになります。

関数funcは9〜15行目に定義されています。
funcには、nameという引数があり、アノテーションとして「:int」(整数)が書かれていますので「この引数nameには整数を入れるべき」という意味になります。
ただ、アノテーションはあくまで解説であり、強制力はないため「"Magatama"」という文字列でもエラーにはなりません。スルーされます。
関数funcの中では、またtryがあり、その中で「print(name, "【C】")」が実行されています。
問題文の出力結果を見ると、最初に「Magatama is a」を出力する必要があるので、【C】は「is a」で確定します。
(namaの中は"Magatama"なので、自動的に連結されてMagatama is aになりますね)
その次の行で、「raise_character(name)」という関数が呼び出されています。
この関数raise_characterは、4〜7行目で定義されていますね。
関数raise_characterには引数aがあり、そこに"Magatama"が入りますが、この引数aは使われていないので、特に意味がありません。
関数raise_characterの中では、最初に「print("【A】")」が実行されています。2個目の出力は「Saya's」なので、【A】は「Saya's」で確定します。
その下で「raise kusanagi」が実行されていますので、kusanagiという例外が実行されます。
ここで大事なポイントとして、例外が発生すると、それ以降の処理は中断されるということです。
つまりその下のprint("【B】")は実行されません。(なので、この時点で【B】が何かはわかりません)
現在は関数funcのtry文で、関数raise_characterの処理を実行したら「kusanagi」という例外が発生したので、
処理は13行目の「except kusanagi:」に移ります。
ここで、print("【D】")が実行されていますので、【D】は3つ目の出力である「reliable」であると確定します。
その下で「raise Exception」が実行されています。
つまりExceptionという例外が発生します。
このExceptionは組み込みクラスなので、自分で作らなくても存在しています。
この時点で関数funcは終わり、20行目の「except Exception:」に移ります。
ここでは「print("【E】")」が実行されていますね。
これが4つ目の出力になりますので、【E】は「partner」であることが確定します。
まだ、【B】がわかっていませんでしたが、消去法で残った「goofy」が【B】であることが確定します。

つまり答えは4つ目の選択肢ですね。


問32.
次のスクリプトの実行結果として正しいものはどれか。なお各選択肢内は実際は改行されているものとして読み替えること。


loc = "1"
def scope():
    loc = "2"
    def do_local():
        loc = "3"
    def do_nonlocal():
        nonlocal loc
        loc = "4"
    def do_global():
        global loc
        loc = "5"

    do_local()
    print("【A】", loc)
    do_nonlocal()
    print("【B】", loc)
    do_global()
    print("【C】", loc)

print("【D】", loc)
scope()
print("【E】", loc)

・【A】 3 【B】 3 【C】 5 【D】 2 【E】 5
・【A】 3 【B】 2 【C】 5 【D】 2 【E】 1
・【A】 2 【B】 3 【C】 4 【D】 2 【E】 1
・【D】 1 【A】 2 【B】 4 【C】 4 【E】 5
・【D】 3 【A】 4 【B】 5 【C】 1 【E】 2


解説:
まーためんどくさい問題だなぁって感じです。
とりあえず変数のスコープ(参照範囲)についての問題で、変数locには何が入っているのか?がポイントです。
とりあえず「同じ名前の変数でも、関数の外と中では別の変数扱い」と考えればわかりやすいはずです。
問題のコードは1行目で「loc = "1"」が実行された後、次の処理は20行目の「print("【D】", loc)」です。
なので、最初の出力は「【D】 1」になります。
(関数の中のlocは別の変数扱いなので、無視です)
選択肢をみると「【D】 1」から始まるのは、4つ目だけなので実はこの時点で答えが確定します…(これで良いのかこの問題?)

まぁとりあえず最後までやってみましょう。
次に実行されるのは21行目の「scope()」です。
なので、処理は関数scopeの中に移り、「loc = "2"」が実行されます。
ここポイントです。
この時点でlocは2が代入されましたが、ここは関数scopeの中なので、外の変数locとは別の変数です。
理解しやすくするために、ここでは関数の外のlocを「loc(外)」とし、関数scopeの中の変数locを「loc(scope)」と記載します。
その下の4行目から11行目は、また別の関数の定義なので、まだ処理は行われません。
次の処理は13行目になりますが、13行目では「関数do_local()」を実行していますので、処理はdo_local()に移り、
「loc = "3"」が実行されます。
はい、またポイントです。
今度は「関数do_local()」の中の変数なので、「loc(外)」とも「loc(scope)」とも別の、第3の変数です。
とりあえず「loc(do_local)」と呼びましょう。
ちょっと整理すると、

「loc(外)」の中身は1
「loc(scope)」の中身は2
「loc(do_local)」の中身は3

という状況です。

「関数do_local()」はこれで終わるので、処理は14行目の「print("【A】", loc)」に進みます。
はい、この時の「loc」はなんでしょうか?というのが、この問題のポイントになってきます。
この「print("【A】", loc)」の時は、一番外ではなく、「関数scope」の中に入っていて、でも「関数do_local」の中にはいない、つまり「関数scope」の"空間"にいますので、
「print("【A】", loc)」のlocは「loc(scope)」のことです。つまり、2が入っています。
なので「【A】 2」が出力されます。

このように「変数が使える範囲(参照範囲)」をスコープと言って、
変数や関数など、基本的に同じネームスペース(名前空間)にいないと、直接呼び出すことができません。

現実世界で例えるなら、

「外」は「家の外」
「関数scopeの中」は「scopeという家の中」
「関数scopeの中で定義された、関数do_localの中」は「scopeという家の中の、do_localと書かれた部屋の中」

という感じです。
それぞれ、壁があるので見れない、と考えてください。
(スコープと名前空間は基本的に同じ意味と考えて問題ありません)

次に、「do_nonlocal()」が実行されますので、処理は6行目の関数do_nonlocal()に移ります。
関数の中では「nonlocal loc」と書かれています。
この「nonlocal」が重要で、これは「この関数内のlocは、一つ外の空間のlocとして扱う」という宣言です。
つまり、8行目の「loc = "4"」のlocは、「loc(scope)」のことになります。
関数do_nonlocalの中(部屋の中)ではありますが、その壁を壊したようなイメージです。
ただし、まだ家の外は見えませんので、「loc(外)」ではない、という感じです。
次に16行目の「print("【B】", loc)」が実行されますが、このlocは「loc(scope)」なので「【B】 4」が出力されます。
その次に17行目の「do_global()」で関数do_globalが実行されます。
関数do_globalの中では「global loc」というのが書かれています。
これが最後のポイントで「global」は「この関数内のlocは、一番外の空間のlocとして扱う」という宣言です。
つまり、部屋(関数do_global)の壁だけじゃなく、家(関数scope)の壁ごと壊した感じです。
なので、その下の「loc = "5"」のlocは「loc(外)」のことになります。
関数do_globalは終わり、次に18行目の「print("【C】", loc)」が実行されますが、このlocは「loc(scope)」のことなので、4のままです。
ということで「【C】 4」が出力されます。
これでやっと「関数scope」が終わり、処理は22行目の「print("【E】", loc)」に来ます。
このlocは「loc(外)」のことですね?「loc(外)」は先ほど5が代入されていましたので、「【E】 5」が出力されることになります。
なので答えは「【D】 1 【A】 2 【B】 4 【C】 4 【E】 5」になります。


問33.
次の実行結果を得たい場合、コードの【A】【B】の行および【C】に入る組み合わせとして適切なものはどれか。なお【A】は★aの行と同じ数の空白でインデントされている。


[ 実行結果 ]
Need Speed?
I'm Saya.
Need Speed?
I'm David.

[ コード ]
class kusanagi():
    def s(self):
        print("Need Speed?") …★a
        【A】
    def m(self):
        print("I'm Saya.")

class wexal(kusanagi):
        def 【B】:
            print("I'm David.")

k = kusanagi()
w = wexal()
k.s()
w.【C】

・【A】self(m) 【B】m(self): 【C】s()
・【A】self(m) 【B】self(m): 【C】s(self)
・【A】self.s() 【B】m(self): 【C】s(self)
・【A】self.m() 【B】m(self): 【C】s()
・【A】self.m() 【B】self(m): 【C】s()

解説:
クラスの継承に関しての問題ですね。
これは、すべて説明しきるのはかなり大変なので、サクッとだけ。
まず、「クラスkusanagi」と、「kusanagiクラスを継承したクラスwexal」が用意されており、
「k = kusanagi()」と「w = wexal()」でそれぞれインスタンスが作られています。
そして、最初に「k.s()」が実行され、その中の「print("Need Speed?")」で、最初のNeed Speed?が出力されていますね。
その次の出力は「I'm Saya.」なので、6行目の「print("I'm Saya.")」を実行するために、
まずは5行目の「def m(self):」を実行しないといけない事がわかります。
なので【A】には「self.m()」が入ります。
※「self(m)」だと呼び出し方がおかしいのでエラーです。self.s()だと、無限に「print("Need Speed?")」が実行されることになります。
これで「k.s()」の実行が終わります。

この次から少し悩みポイントですが、3つ目の出力は、また「Need Speed?」なので、再度「k.s()」が実行されるのか?という感じがしますが、
問題文では「w.【C】」なので、wを使わないといけないようです。
このwは「kusanagiクラスを継承したクラスwexal」のインスタンスなので、実は「k.s()」と同じ機能が継承されています。
つまり「w.s()」とすると「k.s()」と同じように、「def s(self):」のメソッドsが実行され、Need Speed?が出力されます。
ただ、最後の出力は「I'm David.」です。
「k.s()」と同じであれば、Need Speed?と、I'm Saya.が出力されてしまいます。
そのためにI'm Saya.を出力する「メソッドm」だけを作り直必要があります。これをオーバーライドと言います。
それが9行目の【B】の部分です。
オーバーライドは、継承元(kusanagi)と同じ名前のメソッドを作れば、自動的に作り変えられます。
つまり【B】は「m(self)」になります。
以上で、正解は4つ目の選択肢だとわかります。


問34.
次のファイル「script.py」を作成し、コマンドライン上で「python3 script.py one two three four five」を実行したときの結果として正しいものはどれか。


[ script.py ]
import sys
print(sys.argv[0:4])

・['script.py', 'one', 'two', 'three']
・['script.py', 'one', 'two', 'three', 'four']
・['python3', 'script.py', 'one']
・['python3', 'script.py', 'one', 'two']
・['python3', 'script.py', 'one', 'two', 'three']

解説:
コマンドライン引数に関しての問題です。
ポイントは「sys.argv」の部分で、プログラム実行する際に、コマンドを打ち込むと、この「sys.argv」の中にリスト形式で収められます。
「sys.argv」の最初の要素には必ず「ファイル名」が入り、そのまま左から順番に他のコマンドが入っていきます。
(python3の部分だけは入らない、と覚えれば簡単です)
つまり以下のように処理されます。

「sys.argv[0]=script.py」
「sys.argv[1]=one」
「sys.argv[2]=two」
「sys.argv[3]=three」
「sys.argv[4]=four」
「sys.argv[5]=five」

今回はスライスで「sys.argv[0],sys.argv[1],sys.argv[2],sys.argv[3]が指定されていますので、選択肢の1つ目です。


問35.
次の正規表現を用いたコードの【A】の部分に入れたときエラーとなるものはどれか。


import re
prog = re.compile('(K|S)u(r|s)(a|o)nf?(a|o)(o|m)?g?i?(saya)?', re.IGNORECASE)
【A】
print(ret[0])

・ret = prog.search('KUSANAGISAYA')
・ret = prog.search('Kuronami')
・ret = prog.search('SuZunone')
・ret = prog.search('SUSANOO')
・ret = prog.search('kusanomi')


解説:
正直解説にも疲れてきたところで正規表現かぁという気持ちです。
正規表現とは、特定のルールで指定する文字の指定方法です。
一見頭が痛くなりそうですが、意味がわかると単純な仕組みです。
ポイントは「prog = re.compile('(K|S)u(r|s)(a|o)nf?(a|o)(o|m)?g?i?(saya)?', re.IGNORECASE)」で、progという名前で正規表現オブジェクトを作成しています。
正規表現オブジェクトは「search」メソッドなどで、文字列を与えると、設定したルールに合うかを判定してくれます。
この正規表現は、個人情報などの入力フォームなどでよく使われます。
例えば「メールアドレスを入力してください」の欄に「キノコード19歳です」などと入力しても「正しいメールアドレスを入力してください」みたいに表示されると思います。
あれは裏側で「@は含まれているか?ひらがなや漢字が含まれているか?」などとルールを決めて、ルール外の入力データは登録できないようにしています。
その時になどにも正規表現は使われます。

では、実際に正規表現を理解するためのポイントをまとめます。
(とりあえず問題に出てくるものだけです)

・(a|b)というような書かれ方は、aかbのどちらかという意味
→例えば「(K|S)u」の場合は「Ku」か「Su」という意味。(それ以外は認めない)

・?は一つ前の文字が0か、1を表す
→たとえば「nf?」は「nf」か「n」という意味。?は1つ前の文字にだけ影響する。

・(abcd)という書かれ方は、(abcd)をまとめている書き方。これだけだと意味はないが?などと絡めると意味が変わる
→たとえば「(abcd)?」は、「abcd」か「」かになる。?の影響が一つ前のdだけじゃなく、abcd全部に影響する。

この3つだけで、この問題は理解できます。
なお、「compile」メソッドの第2引数で「re.IGNORECASE」というのが指定されており、これは「大文字か小文字かは区別しない」と意味になります。
つまり「(K|S)u」の場合は「Ku」「Su」「KU」「SU」「ku」「su」「kU」「sU」のどれでもOKとなります。

問われているのは「どれがエラーになるか?」なので、このルールに合わない文字列を探します。
選択肢では、「ret = prog.search('KUSANAGISAYA')」などと書かれていますが、注目するのはsearchメソッドの値の「'KUSANAGISAYA'」の文字列部分だけです。
それぞれの選択肢の文字列を、正規表現のルールと照らし合わせていきましょう。

「'KUSANAGISAYA'」はOK
「Kuronami'」もOK
「SuZunone'」は駄目
「SUSANOO'」もOK
「kusanomi'」もOK

なので、選択肢の3つ目が正解です。
具体的には「SuZunone」の3文字目は必ずrかsになるはずですが、Zの時点で間違いだとわかります。
さらに4文字目もaかoしか駄目なのにuだし、7と8文字目もおかしいので、慣れているとパッと見でもわかるようにはなっている問題ですね。


問36.
モジュールに関する次の記述のうち誤っているものはどれか。


・processingモジュールを使うと、コード処理の実行時間を計測できる。
・smtplibモジュールを使うと、任意のインターネット上のホストにメールを送ることができる。
・randomモジュールを使うと、疑似乱数を生成することができる。
・urllib.request モジュールを使うと、URLにあるデータを取得することができる。
・statisticsモジュールを使うと、数値データの基本統計量(平均、中央値、分散など)を取得することができる。

解説:
processingとはプログラミングで画面上に絵を描写して、動かしたりする統合開発環境です。
プログラミング学習のために簡単なゲームのようなものを作る場合にも使われたり、グラフを表示させたりする時によく使われます。
なので、1つ目の選択肢は誤りです。
「コード処理の実行時間を計測」するのはtimeモジュールの「time.process_time()」メソッドです。


問37.
今日の日付を次の実行結果のように得たい場合、スクリプトの1行目【A】と2行目の【B】に入る適切なものはどれか。


[ 実行結果 ]
2020-07-02

[ スクリプト ]
【A】
today = 【B】
print(today)

・【A】import date 【B】datetime.date(today)
・【A】from datetime 【B】date.today()
・【A】from datetime import date 【B】date.today()
・【A】import datetime from date 【B】datetime.today()
・【A】import date from datetime 【B】datetime.today()

解説:
今日の日付を「西暦-月-日」の形式で出力するためのコードを選ぶ問題です。
この表示形式はdatetimeモジュールのdateクラスのtodayメソッドになりますが、importとformの使い方がポイントの問題かと思います。
まず「import ○○」は特定のモジュールをすべて読み込みます。
それに対し、「from ○○ import △△」は「fromで指定したモジュールのクラスなど一部だけを読み込む」方法です。
つまり、今回は「datetimeモジュール」の「dateクラス」だけを読み込めば良いので、【A】は「from datetime import date」です。
これで「date.○○」の書き方で、dateクラスのメソッドなどが使えるようになるので、【B】は「date.today()」です。

余談:
pythonでは、外部のモジュールを読み込んで使うことが非常に多いので、「fromとimport」は頻繁に使われます。
ただ、今回の問題はimportだけでもできます。

import datetime

today = datetime.date.today()
print(today)

とすると、同じ結果になります。
ただ、基本的に使わないクラスまで読み込むのは良くないの、普通はfromでdateクラスだけ指定しています。
意外と最初はわかりづらいので、慣れておきましょう。


問38.
loggingモジュールのメッセージの優先度として正しいものはどれか。左から順に優先度が高いものとする。


・ERROR、CRITICAL、WARNING、INFO、DEBUG
・ERROR、CRITICAL、WARNING、DEBUG、INFO
・CRITICAL、ERROR、WARNING、DEBUG、INFO
・CRITICAL、ERROR、WARNING、INFO、DEBUG
・CRITICAL、WARNING、ERROR、INFO、DEBUG

解説:
logging(ロギング)とは起こった出来事の記録を取ることです。
つまりlog(ログ)を取ることがロギングです。
このログには、内容に応じて5つの優先度(重要度)、レベルが設定されています。

CRITICALは、プログラムが実行を続けられないことを表します。
ERRORは、ソフトウェアが、ある機能を実行できないことを表します。
WARNINGは、想定外のことが起こった、または問題が近く起こりそうであることを表します。
INFOは、想定通りのことが起こったことの確認。
DEBUGは、問題を診断するときにのみ関心があるような、詳細な情報。

なので、4つ目の選択肢が正解です。


問39.
仮想環境とパッケージに関する次の記述のうち正しいものはどれか。


・pip install でパッケージ名を指定し、そのパッケージ名の後ろに「=」とバージョン名を付けると、そのバージョンのパッケージをインストールできる。
・「pip upgrade パッケージ名」とすることで、当該パッケージを最新バージョンにアップグレードすることができる。
・pip freezeはその仮想環境にインストールされたすべてのパッケージを表示する。pip listも同様の働きをするが、両者は出力形式が異なる。pip listはその仮想環境にインストールされたすべてのパッケージを、pip install向けの形式で出力する。
・pip uninstall にパッケージ名を指定すると、その仮想環境からパッケージを削除できる。削除対象となるパッケージの複数指定はできない。
・仮想環境を作成、管理するのに使われるスクリプトはpyvenvである。

解説:
今回は「正しいもの」を選ぶ問題です。
1つ目の選択肢は「「=」とバージョン名を付けると」の部分が誤りで、正しくは「「==」とバージョン名を付けると」です。
2つ目は、「「pip upgrade パッケージ名」とすることで」が誤りで、正しくは「pip install --upgrade パッケージ名」または「pip install -U パッケージ名」です
3つ目は、「pip listはその仮想環境にインストールされたすべてのパッケージを、pip install向けの形式で出力する。」が誤りで、この説明はpip freezeの説明です。
つまり、正しくは「pip freezeはその仮想環境にインストールされたすべてのパッケージを、pip install向けの形式で出力する。」になります。
4つ目、「削除対象となるパッケージの複数指定はできない。」が誤りで、正しくは、カンマ区切りで「pip uninstall パッケージ1, パッケージ2」とすると複数指定できます。
ということで、正解は5つ目の選択肢です。



問40.
次の記述に関して正しいものはどれか。


・デフォルト設定ではユーザーディレクトリの「.pyhistory」ファイルにヒストリが保存される。ヒストリは対話型インタープリタセッションで利用できる。
・[Ctrl]+[t]キーを押すと補完機能が呼び出せる。この機能はPythonの文(命令)の名前、現在のローカル変数、使用できるモジュール名を検索するものである。
・拡張された対話型インタープリタとしてBythonがある。これはオブジェクト探索、高度なヒストリ管理などの機能を持つ。
・IPythonは「pip install ipython」でインストールできる。IPythonの対話モードはipythonコマンドで起動できる。終了時はdeactivateコマンドを実行すればよい。
・変数とモジュールの補完機能は、インタープリタの起動時に自動で有効になっている。

解説:
今回も「正しいもの」を選ぶ問題です。
1つ目の選択肢は、誤りは「「.pyhistory」ファイル」の部分で、正しくは「.python_historyファイル」です。
2つ目は、「[Ctrl]+[t]キーを押すと補完機能が呼び出せる」が誤りで、正しくは「[Tab]キーを押すと補完機能が呼び出せる」です。
3つ目は、「Bython」が誤りで、「Bpython」か、または「Ipython」か正しいです。
4つ目は、「終了時はdeactivateコマンドを実行すればよい。」が誤りで、正しくは「終了時はquit()を実行すればよい。」です。
deactivateはvirtualenvで作った仮想環境の終了のコマンドです。


以上で、1-40問の解説終わりです。