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

11689


Python3 エンジニア認定基礎試験の認定模擬問題として、
認定模擬問題(DIVE INTO EXAM)
認定模擬問題(PRIME STUDY)
の2つが認定されているようです。

そのうちDIVE INTO EXAM様の模擬問題は解説があるのですが、PRIME STUDY様の問題は解説がなかったので、勉強がてら解説をまとめたいと思います。
もし、Pythonに興味がある方や、キノコードでやるPython学習の範囲がどんなものなのか、の参考にしてください。
(第1回から第3回まであるようなので第3回だけやります)
PRIME STUDY様の認定模擬問題のリンクはこちらです→https://study.prime-strategy.co.jp/

本試験と同じで、全部で40問あり、制限時間は60分です。
基本的にすべて選択問題のようです。

長くなるので、まずは10問です。



問1.
Pythonの特徴に関する次の記述のうち、誤っているものはどれか。

・Pythonの特徴の一つが可読性の高さである。複雑な操作も単一文で表記可能であり、文のグルーピングがインデントで行われるためコードの見通しが良いなど、プログラムを小さく読みやすく書けるようになっている。ただ変数の宣言は必要であり行わないとエラーとなる。
・機械学習、AI、データ分析の分野でPythonが用いられる理由の一つは、Numpyやpandas、scikit-learnなど機械学習向けのサードパーティ製パッケージやそれを用いた環境(Jupyter Notebookなど)が充実していることである。
・Pythonは柔軟な配列や集合、ディクショナリといった、高水準のデータ型を組み込みで持つ。データ型の一般性が高いためPythonの対応可能な問題領域はAwkやPerlと比較して広い。
・Pythonは簡単に使えるとはいえ本格的なプログラム言語であり、大きなプログラムを書くために提供された構造やサポート、エラーチェック機構が、シェルスクリプトなどに比べはるかに多く存在する。
・ PythonはWindows、MacOS、Linuxなど多くの環境で動作する、拡張可能なフリーのオープンソースソフトウェアである。

解説:
一つ目の選択肢の「ただ変数の宣言は必要であり〜」の部分が誤りです。Pythonは変数の宣言が不要です。

余談:
重箱の隅をつつきますけど、「宣言」って正確にはメモリ領域を確保することだと思うので、Pythonは「変数の宣言は不要」というより「初期化とセットで自動的に宣言される」わけです。
つまり「ただ変数の宣言は必要であり〜」の部分も、あながち誤っていないような感じになったちゃうので、ちょっと問題文を変えた方が良い気がします…。


問2.
Pythonインタープリタに関する次の記述のうち、誤っているものはどれか。

・ Pythonモジュールを呼び出すには、「python -m モジュール名 [引数] …」という方法があり、例えば「python -m timeit -h」を実行すると、timeitモジュールの詳細が出力される。
・インタープリタの起動方法として、「python -cmd コマンド [引数] …」という方法があり、例えば「python -cmd 'print("hello")'」を実行すると、「hello」が出力される。
・対話モードの終了方法には、関数の入力によるものと、キー操作によるものとがある。前者の具体的な方法は、quit()の入力である。後者の具体的な方法は、ファイル終端キャラクタの入力である。
・インタープリタがスクリプト名(スクリプトのファイル名)と続く引数群を知らされると、これらは文字列のリストとなる。import sys を実行することで、このリストにアクセスできる。
・デフォルトでは、PythonのソースファイルはUTF-8でエンコードしてあるものとして扱われる。

解説:
まずインタープリタとは「通訳者」という意味で、Pyhtonで書いたプログラムを、コンピュータが理解できる言語に変換するためのものです。(正確にはもう少し複雑ですが)
「Pythonのプログラムを実行する」という意味と「インタープリタを起動する」という意味は、ほとんど同じと考えてください。
インタープリタの起動(Pythonのプログラムを実行)するには、事前にプログラムを書いたファイル(拡張子はpy)を用意して「python ○○.py」のように実行するか、
「python -c コマンド」(コマンドの部分にPythonのコードを直接書く)という方法があります。
つまり上から2つ目の「python -cmd コマンド [引数] …」は誤りで、「python -c コマンド [引数] …」が正しいです。


問3.
数値に関する次の記述のうち、誤っているものはどれか。

・ほとんどの演算子は左から演算が行われるが、例外的にべき乗は右から演算が行われる。例えば「48 // 6 // 4」の演算結果は「2」であり、「2 ** 1 ** 3」の演算結果は「2」である。
・切り下げ除算を行って整数解を得たい場合(剰余を捨てたい場合)は「//」演算子を使い、剰余のみを得たい場合は「%」演算子を使う。
・等号(=)は、変数を代入するのに使う。変数に代入すると参照先が代入され、値のコピーは行われない。
・対話モードでは、最後に表示した式は変数「_」(アンダースコア)に代入される。
・整数はint型、小数点を伴う数はfloat(浮動小数点数)型を持つ。演算対象の型が混合していた場合、浮動小数点数は整数に変換される。また除算は常にfloatを返す。

解説:
int型とfloat型の演算の場合、結果はfloat型に変換されます。(暗黙的な型変換などという)
なので、一番下の選択肢の「演算対象の型が混合していた場合、浮動小数点数は整数に変換される」は誤りです。
他のプログラミング言語などでもそうですが、基本的に暗黙的な型変換は「大きい領域を持つ型に合わせられる」と覚えとくと忘れにくいです。
※整数int型は0と1の間に1つ分の数字しかないが、float型は0と1の間に無限の数が存在する、つまり領域が大きい。

余談:
ただ、その後の「除算は常にfloatを返す」は正しいので注意です。
Pythonの演算では「10 / 5」などの整数で割り切れる除算でも結果はfloat型になります。
つまり「10 / 5」は「2.0」になります。
int型として返したい場合は「10 // 5」と書くのが楽です。※「//」は余りを切り下げ除算の演算子
切り捨てなので「10 // 3」だと3になります。
ただし、結果が負の数になる場合は注意です。
Pythonの場合、負の数の切り下げは、負の最大値側に丸められるので「-10 // 3」は「−4」になります。
(負の数なので、切り下げが切り上げみたいに見える、と思うとわかりやすいです。負の数の除算はプログラミング言語によって結果が異なることがあります)

他の方法として「int(-10 / 3)」というように「int( )」で囲むと明示的にint型への型変換となり、その場合は「-3」になります。
なので、int型で結果が欲しい時は「int( )」を使うようにした方が良いかもです。


問4.
次のコードの実行結果として正しいものはどれか。

v = 1
w = 2
v, w = w + 1, v + 3
x = w ** 2 + 1
y = x - 8 / 2
z = y % 5
print(w, y, z)

・6 2.5 0.5
・6 9 4.0
・6 33.0 3.0
・4 9 0.1
・4 13.0 3.0

解説:
「おい、基本的な算術演算子を理解してるだろうな?」という確認を、1つにぶちこんだような問題です笑
ポイントは「v, w = w + 1, v + 3」の複数の変数に同時に代入する、というところと、「**」と「%」の演算子の意味でしょうか。
「**」は累乗(べき乗)の演算子で、
「%」は剰余(割り算の余り)だけを求める演算子です。
演算子の意味がわかっていれば、そのまま計算するだけなので、一番下の選択肢になります。


問5.
文字列に関する次の記述のうち、誤っているものはどれか。なお「¥」はバックスラッシュに読み替えること。

・Pythonの文字列は改変ができない「変更不能体(immutable)」なものであるが、文字列のインデックス指定(連番による指定)やスライシング(切取)は可能である。
・列挙された文字列リテラルは連結される。例えば対話モードで「>>> 'Py' 'thon'」とした場合には、間にスペースを挟んだ形で自動的に連結され「'Py thon'」(yとtの間に半角スペース)となる。
・トリプルクオート「"""」を使うと、文字列リテラルを複数行にわたって書くことができる。docstring(ドキュメンテーション文字列)でもこの記述方法が使われる。
・バックスラッシュを前置した文字を特殊文字として解釈させないようにするには、raw文字列を使う。具体的には最初の引用符の前に「r」を置いて「print(r'C:¥some¥name')」のように記述する。
・文字列は「*」で繰り返すことができる。「'He' + 3 * 'y'」は対話型インタープリタで出力「'Heyyy'」が得られる。

解説:
2つ目の選択肢の「間にスペースを挟んだ形で自動的に連結され「'Py thon'」(yとtの間に半角スペース)となる」という部分が誤りです。
自動的に「'Py' 'thon'」と書いた場合に自動的に連結されるのは正しいのですが、間にスペースがあっても無視されて連結されます。
つまり「'Py thon'」ではなく「'Python'」になります。
なんか妙に細かい部分の問題です笑


問6.
以下の結果を得たい場合、コードの【A】に入るものとして正しいものはどれか。

[実行結果]
Simlx

[ コード ]
Zen = 'SimpleIsBetterThanComplex'
print('{}{}{}'.format(【A】))

・Zen[0:2], Zen[-5], Zen[-1]
・Zen[0:2], Zen[-2], Zen[-1]
・Zen[0:3], Zen[-3], Zen[-1:]
・Zen[0:3], Zen[-2], Zen[-2:-1]
・Zen[0:4], Zen[-3], Zen[-2]

解説:
ポイントは2つ
まず2行目のformat関数を理解が必要ですね。
format関数は、一見意味不明なのですが結構簡単です。
たとえば「print('僕は{}歳です!'.format(12))」と書けば「僕は12歳です!」と出力されます。
つまり、{}の部分が「format(12)」の12に置き換わるって感じです。
{}を変更させる書き方だと思ってください。
「print('僕は{}!{}歳です!'.format('チヒロ', 12))」とすれば「僕はチヒロ!12歳です!」というように、複数指定することができます。
今回の場合は{}が3つ並んでいますので、3つの値を指定することになりますね。

次のポイントは「スライスでの指定」です。
Pythonでは文字列を配列(リスト)のようにインデックス番号で指定できます。
一番左を0から数える所も配列と同じです。('は含まれない)
つまり「Zen[0]」は「S」(一番左の文字)のことになります。
負の数は末尾から数えるので「Zen[-1]」は一番右の「x」のことになります。
ここまでは普通のインデックス番号での指定(参照)です。
スライスとは「Zen[0:2]」みたいに「:コロン」を使った指定方法です。
「Zen[0:2]」の場合「Zen[0]からZen[1]」が指定されます。つまり「Si」です。
「Zen[0]からZen[2]」ではないので注意してください。
0番から2番まで(2自身は含まれない)ということです。
Zen[:]のように数字が指定していない場合は「Zen[先頭:末尾]」というように判断してくれるので、Zen[-1:]だと「Zen[-1:末尾]」となり、つまり「x」になります。

これさえわかれば後は簡単です。
実行結果は「Simlx」にしたいので、
Zen[0:3](つまりSim)と、Zen[-3](つまりl)と、Zen[-1:](つまりx)なので、3番目の選択肢が正しいです。


問7.
次の変数Zenに関して指定した場合、実行時にエラーとなるものはどれか。

Zen = 'NowIsBetterThanNever'

・Zen[:]
・Zen[60:80]
・Zen[10]
・Zen[:500] + 'Z'
・Zen[10] = 'X'

解説:
これもスライスの問題、と見せかけてPythonでの文字列の扱いに関してですね。
Pythonでは文字列は配列のようにインデックス番号で指定(参照)は出来ますが、代入はできません。
つまり、選択肢の中で唯一代入をしようとしている一番下の選択肢がエラーになります。

余談:
Pythonの文字列に代入できないのは、文字列がimmutable(イミュータブル:変更不可)だからです。
なので文字列そのものを変更(代入)することは出来ません。
ただし、変数はもちろん変更できるので、Zen = 'NowIsBetterThanNever'の後に、Zen = 'あああああ'みたいに代入して、文字列を変えることは出来ます。
変数はmutable(ミュータブル:変更可能)だからです
このイミュータブルとミュータブルは、どのプログラミング言語にもある概念ですが、Pyhtonの場合はこれを意識しなければいけないことが多いと思います。


問8.
次のコードの実行結果として正しいものはどれか。

a, b = 8, 10
while a > 0:
    print(b, end=',')
    a -= 2
    b -= a

・10,4,0,-2,
・10,8,6,4,2,0
・10,8,6,4,2,0,-2
・10,4,0,
・10,4,

解説:
whileは条件がtrueの間、実行される繰り返しです。条件はa > 0なので、変数aの数が0より大きい間、繰り返し続けます。
処理を追っていくと、最初は変数aは8、変数bは10が代入されます。
次にwhileの条件が評価されます。変数aは0より大きいので、繰り返しの中が実行されます。
whileの繰り返しの中で、最初に「print(b, end=',')」がありますので、「10,」が出力されます。
末尾の「end=','」は「出力結果の末尾に,を足すよ」という意味です。まぁあまり気にしなくていいです。
その下の「a -= 2」は「a = a - 2」という式を短く書く書き方です。
「=」の優先順位は必ず一番最後なので「a = 8 - 2」→「a = 6」という意味になり、変数aには6が代入されます。
その下の「b -= a」は「b = b - 6」と同じなので「b = 10 - 6」という計算がされ、変数bには4が代入されます。
1回目の繰り返しが終わったので、処理は上に戻り、whileの条件を確認します。
まだ変数aは0より大きいので、同じことをもう一度します。
「print(b, end=',')」で「4,」が出力されます。(変数bの中身は4に変わってるので)
次に「a -= 2」は「a = a - 2」→「a = 6 - 2」→「a = 4」となり、変数aには4が代入されます。
その下の「b -= a」は「b = b - a」→「b = 4 - 4」→「b = 0」となり、変数bには0が代入されます。
一番下まで来たので、また上に戻り、条件が評価されます。
まだ変数aは4なので、もう一回同じことをやります。

という流れで、最終的に一番上の選択肢の出力結果になります。

余談:
「print(b, end=',')」の「end=''」部分ですが、これは「改行したくない時」によく使われます。
例えば「print(b)」だけだと、出力結果が、
10
4
0
-2
になってしまいます。
言い方を変えると、endを指定しない場合はprintは自動的に「print(b, end='\n')」という処理をします。
\nは改行コードと言って、改行を表す特殊な書き方です。(改行以外にも色々あります)
このendを指定する書き方は、複数の出力結果を、1行で表示したい場合などによく使われます。
また「print(b, end='')」のように何も指定しないと「1040-2」というように、すべてひっついて1行で表示されますので、
今回のように「print(b, end=',')」みたいに「,」で区切ったり、「print(b, end=' ')」のように半角スペースで区切ったりします。


問9.
次のコードの出力結果として正しいものはどれか。

fruits = ['apple', 'kiwi', 'plum']
for f in fruits[:]:
    if len(f) < 5:
        fruits.insert(0, f)
        fruits.pop()

print(fruits, end = ' ')

・['kiwi', 'apple', 'plum']
・['plum', 'kiwi', 'apple']
・['plum', 'apple', 'kiwi']
・['apple', 'kiwi', 'plum']
・['apple', 'plum', 'kiwi']

解説:
リストの関数についての問題ですね。
まず「fruits = ['apple', 'kiwi', 'plum']」でリストfruitsが作られ、「for f in fruits[:]:」でリストfruitsの要素数分、繰り返しが行われます。(つまり3回)
またfor文はリストfruitsから、一つずつ要素を取り出して「f」に入れてくれる動きもします。
つまり1回目の繰り返しでは「if len(f) < 5:」は「if len('apple') < 5:」と同じになります。
「len()」関数は()の中に指定したものの長さ(要素数)を返す関数なので、「if 5 < 5:」と同じ意味になります。
if文は条件を満たした時だけ、中の文が実行されますが、1回目の繰り返しでは条件を満たしていないため、中の文「fruits.insert(0, f)」と「fruits.pop()」は実行されません。
これで1回目の繰り返しが終わり、2回目の繰り返しが始まります。この時、「f」の中が入れ替えられて「'kiwi'」が入ることになります。
つまり「if len(f) < 5:」は「if len('kiwi') < 5:」→「if 4 < 5:」と同じ意味になり、条件が満たされるので、中の文が実行されます。
「fruits.insert(0, f)」ではinsertメソッドが使われていますが、これはリストの特定の場所に何かを挿入するメソッドで、
「insert(追加したい場所, 追加したいもの)」という書き方になります。
つまり「fruits.insert(0, f)」はリストfruitsの先頭に「'kiwi'」を追加する、という意味になります。
この時点でリストfruitsは['kiwi', 'apple', 'kiwi', 'plum']となります。
その下の「fruits.pop()」のpopメソッドは「指定した場所の要素を、取り出して、削除する」メソッドです。
たとえば「fruits.pop(1)」なら、2番目の要素('apple')が削除されます。※インデックス番号は0から数えるんでしたね。
「fruits.pop()」のように場所の指定がない場合は「末尾」を指定したことになります。
つまり['kiwi', 'apple', 'kiwi', 'plum']の末尾の要素の['plum']が削除され、リストfruitsは['kiwi', 'apple', 'kiwi']になります。
これで2回目が終わり、3回目が始まります。

ここからがポイントです。
for文の3回目の繰り返しなので、リストfruitsの3つ目の要素が取り出されて「f」の中に入れられることになりますが、先程、リストfruitsは['kiwi', 'apple', 'kiwi']になりましたよね?
つまり3つ目の要素は「'kiwi'」になるので「f」の中には、また'kiwi'が入るように思えますが、実は「'plum'」が中に入れられます。なぜでしょう?
これは「for f in fruits[:]:」の部分が関係しています。
もし、これが「for f in fruits:」であれば、「'kiwi'」が「f」に入ります。
でも、「for f in fruits[:]:」では「'plum'」になります。
実は、「fruits」と「fruits[:]」は「別のオブジェクト(もの)」なのです。
「fruits」は「リストfruits」自体のことを指定しています。※正確にはリストが代入されている変数fruits。
「fruits[:]」はスライスを使って、リストfruitsの最初の要素から、最後の要素までを指定していますが、リストfruits自体を指定しているわけではありません。
「fruits[:]」とすることで、「['apple', 'kiwi', 'plum']」という値は同じものの、「別のリスト」が作成され、それがfor文に渡されます。
なのでリストfruitsが変更されても、「fruits[:]」で作られた別のリストには影響ありません。「['apple', 'kiwi', 'plum']」のままです。
だから3回目の繰り返しで、「f」に入れられるのは「'plum'」になります。
最終的に['plum', 'apple', 'kiwi']が出力されます。

余談:
for文は反復処理(繰り返し)の基本なので、どんなプログラミング言語でもありますが、使い方は大きく違うことがあります。
CやJavaなどは「for(int i = 0; i < 10; i++)」みたいな書き方なので、そっちに慣れている人はかなり戸惑うかな、と思います。
まぁどちらのfor文もリストなどと組み合わせて使うことが多いので、使い道としては共通しています。
このリストのような「複数の要素を持てるオブジェクト」を「イテラブル(反復可能な)オブジェクト」という言い方をします。
リストや文字列、タプルなども「イテラブルオブジェクト」です。
for文はこの「イテラブルオブジェクトとセットで使う」ということを覚えておきましょう。


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

[ 実行結果 ]
Found an even number: 2
Found an odd number: 3
Found an even number: 4
Found an odd number: 5
Found an even number: 6

[ コード ]
for num in range(2, 7):
    if num % 2 == 1:
        print("Found an odd number:", num) …★a
        【A】
    print("Found an even number:", num)


・continue
・break
・pass
・break:
・else:

解説:
これは「偶数か奇数か?」を判定するプログラミングですね。
まず、「for num in range(2, 7):」の「range(2, 7)」は「2から、7までの数字(7は含まないため23456)」のことです。
([2,3,4,5,6]みたいなリストと同じイテラブルオブジェクトですが、リストではなく、range型のデータです)
つまり、このfor文のnumには2,3,4,5,6と順番に入ることになります。
2行目の「if num % 2 == 1:」で「奇数か?」の判定をしています。
numには最初2が入っていますので、「if 2 % 2 == 1:」ですね。
「%」は除算の余り部分だけを求める演算子なので「if 0 == 1:」となり、if文は実行されません。
「print("Found an even number:", num)」が実行されますので、「Found an even number: 2」と出力されます。これで1回目が終わります。
2回目の繰り返しではnumが3になりますので、「if 3 % 2 == 1:」→「if 1 == 1:」となり、if文の中が実行されます。
if文の中では、まず「print("Found an odd number:", num) 」が実行されますので、「Found an odd number: 3」が出力されます。
次が問題の箇所で、この【A】に入るのが何か?という問題です。
出力結果を見る限り、【A】では処理を中断させる必要があります(中断させないと下の「print("Found an even number:", num)」が実行されてしまうため)
ただし、forの繰り返しは継続しないといけません。(まだ2行しか出力出来ていないので)
このようなケースでは「continue」(コンティニュー)が使われます。
continueは、その時点で処理を中断し、次の繰り返しにスキップします。なので正解は一番上の選択肢です。


余談:
continue以外も一応解説すると、
「break」は処理を中断し、繰り返し自体も終わります。強制終了です。
もし繰り返し文が2重(ネスト)で、その内側の繰り返し文にbreakがあった場合は、内側だけが止まって、外側の繰り返しは止まりません。
「pass」は「なにもしない」という意味です。
じゃあ書かんでええやん、と思いますが、文法上何かしら書く必要がある時や、明示的に「ここでは何も処理しない」という意味をコードに書きたい場合などに使います。
「break:」はエラーです。break文に:をつけてはいけません。
「else:」は「〜でなければ」の意味です。
今回のプログラムはelseを使って書く事もできますが、書く位置的に変なので、誤りです。