Python3エンジニア認定基礎試験～模擬試験で勉強する傾向と対策～

#Python 3 エンジニア認定基礎試験
一般社団法人Pythonエンジニア育成推進協会が実施している民間試験です。

◆受験方法
受験日：通年
申込URL：http://cbt.odyssey-com.co.jp/pythonic-exam.html
受験料金：1万円（税別）　学割5千円（税別）

◆試験概要
試験名：Python3 エンジニア認定基礎試験
（英名：Python 3 Certified Engineer Basic Examination）
資格名：Python3 エンジニア認定基礎試験合格者
（英名：Python 3 Basic Grammar Certification）
概要：文法基礎を問う試験
問題数：40問（すべて選択問題）
試験時間：60分
合格ライン：正答率70％
出題範囲：オライリー・ジャパン「Pythonチュートリアル 第3版」および一般的知識

◆模擬試験
https://diver.diveintocode.jp/exam

##この記事について
筆者は執筆時点(2019/4/29)ではまだ試験は受けておらず、5/11に受験を申し込みした段階です。
上記の模擬試験(全体に無料公開されている)をやってみたので、勉強した内容を記事にしました。
記事にまとめることで自分自身の理解を高めることが目的ですが、同じ試験を受ける人の役に立てれば嬉しいです。
間違った記載もあるかもしれないので、上級者の方は適宜指摘していただけると助かります。

##筆者のプログラミングスキル
2018年6月からpythonを勉強開始
2019年3月から競技プログラミングに参加し、Paiza Sランク、AtCoder 緑ランク
DataCampのData Scientistコースを修了
Chainerの勉強会に参加、ディープラーニングの簡単な実装ができる


##模擬試験の試験問題
CBT形式で80問からランダムに40問が出題されます。
正解のみ記述し、選択肢は省略します。表現は少しだけ修正しています。

#####問1 コマンドライン引数を取得するためのモジュールを選択肢の中から選びなさい。

Pythonのプログラム実行時に引数を渡すことができます。
これには`sys`モジュールの`argv`を利用します。

```python:test.py
import sys
args = sys.argv
print(args)
```

下記コマンドで実行します。

```:terminal
python test.py THIS IS A TEST
```

実行結果は以下のようになります。

```:terminal
['test.py', 'THIS', 'IS', 'A', 'TEST']
```

引数はそれぞれ文字列としてリストに格納されており、最初の要素は実行ファイル名になっています。
正解は`sys`です。

#####問2 ファイルの読み書きで使用する「open」関数のモードについて、存在しないものを選択肢の中から選びなさい。

Pythonでファイルを操作するにはopen関数を使います。
1番目の引数は操作対象のファイルパス、2番目の引数は操作モードです。
open関数の戻り値はファイルオブジェクトであり、それに対してread(読み取り)やwrite(書き込み)操作を行います。

```python:example.py
f = open('demofile.txt', 'r')
print(f.read())
f.close()
```

openしたファイルをcloseするのを忘れることがあるので、withを使った書き方の方がいいです。

```python:example.py
with open('demofile.txt', 'r') as f:
    print(f.read())
```

モードは以下を指定します。省略した場合は`'r'`モードになります。

|Character   |Meaning                      |
|:-----------|----------------------------:|
|'r'         |読み出し専用(reading)         |
|'w'         |書き込み専用(writing)         |
|'a'         |ファイルの終端に追記(appending)|
|'r+'        |読み書き両方                  |

今回の問題では'r-'という存在しないモードが選択肢に含まれており、それが正解になります。

#####問3 アクティベート状態から抜けるコマンドを選択肢から選びなさい。

Pythonは標準機能で`venv`コマンドを使って仮想環境を管理することができます。
仮想環境の有効化は`activate`コマンド、抜けるには`deactivate`コマンドを使います。
![venv.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/393886/e22b0075-092f-57fc-0aab-94191258c962.jpeg)
正解は`deactivate`です。

#####問4 以下のプログラムを実行した際の出力結果を選びなさい。

```python:test.py
a = 2
b = 5

c = 3.0 + b, 5 * a

print(c)
```

以下の命令文は同じタプルを作成します。`()`は省略可能です。

```python:years.py
years = (2005, 2006, 2007, 2008)
years = 2005, 2006, 2007, 2008
```

今回の問題文のcに代入する命令は、タプルを作成します。
a = 2
b = 5
が代入されているので`3.0 + b`と`5 * a`をそれぞれ計算すれば良いです。

整数の「3」でも「3.0」と書くと小数なので、int型ではなくfloat型になります。
`b`はint型ですが、`3.0 + b`で演算対象の型が混在しているので、float型に変換されます。

int型とfloat型に関しては、除算`/`は割り切れる場合も常にfloat型を返すことは覚えておくべきです。
整数解を得たい場合は`//`演算子で切り下げ除算を行う必要があります。
競技プログラミングでも整数型で答えを出力すべき問題で、除算`/`を使ったためにfloat型になってしまってwrong answerになることがあるので、注意しなければいけません。

```python
print(10/2)
print(10//2)
```

```
5.0
5
```

今回の問題の正解は`(8.0, 10)`です。

#####問5 以下のプログラムをインタープリタにて実行した際の出力結果を選びなさい。

```python:
(1,3,5) < (1,2,3,4)
```

リストやタプルは、同じシーケンス型オブジェクト同士であれば、比較ができます。
このとき、辞書的順序で比較されます。
(インデックスの小さいものから順に、要素ごとに比較されます。)

比較できる要素がすべて同じ場合は、要素数が少ないタプル < 要素数が多いタプルになります。

```python:
(1,2,3) < (1,2,3,4)
```

```:console
True
```

今回の問題では最初のインデックスは両方が1であるため等しいです。
そのため2つ目のインデックスを比較しますが、`3 < 2`となるため比較文の結果は`False`です。
正解は`False`です。

#####問6 以下のプログラムを実行した際の出力結果を選びなさい。

```python:test.py
print(range(5))
```

range関数はPython2とPython3で少しだけ挙動が変更されており注意が必要です。
Python2ではrange関数はlistを返しますが、Python3ではrange関数はiteratorを返します。

```python:Python2
print(range(5))
print(type(range(5)))
print([i for i in range(5)])
```

```:console
[0, 1, 2, 3, 4]
<type 'list'>
[0, 1, 2, 3, 4]
```

```python:Python3
print(range(5))
print(type(range(5)))
print([i for i in range(5)])
```

```:console
range(0, 5)
<class 'range'>
[0, 1, 2, 3, 4]
```

正解は`range(0, 5)`です。

#####問7 問題文のprint文を実行した場合の、出力結果を選びなさい。

```python:test.py
from math import pi
print('{:5.3f}'.format(pi))
```

format文で小数点以下の桁数が`3`と指定されており、その桁で丸められます。
`5`は全体の文字数を指定しています。
今回の問題の答えは`3.142`です。

#####問8 Pythonインタープリタにて以下のように入力した場合の出力結果として正しいものを選びなさい。

```python:
from math import pi
print([round(pi, i) for i in range(0, 5)])
```

round関数は小数を任意の桁数で丸めます。
第二引数を省略すると整数に丸められ、型もint型になります。

```python:
f = 123.456
print(round(f))
```

```:console
123
```

第二引数を指定すると、小数点以下の桁がその数になるように丸められます。
注意点として0を指定した場合、整数に丸められますがfloat型で返されます。
今回の問題の答えは以下になります。
1つ目の要素が`3`ではなくて`3.0`になることに注意が必要です。

```:console
[3.0, 3.1, 3.14, 3.142, 3.1416]
```

#####問9 以下のプログラムを実行した際の出力結果として正しいものを選択しなさい。

```python:test.py
i = 10

def num(arg=i):
    print(arg)

i = 7

num()
```

`num`という関数にはデフォルト引数が指定されています。
デフォルト引数が作られるタイミングは「関数が呼び出される時」ではなく「関数が作成された時」に1回だけ評価されます。
`num`という関数が作成されたときには`i = 10`なので、`num`関数の`arg`のデフォルト引数は`10`になっています。
`num()`を実行すると`10`が表示されます。
正解は`10`です。

#####類題 下記のプログラムを実行した際の出力結果はどうなるか。

```python:test.py
def dive_into_code(teacher, L=[]):
    L.append(teacher)
    return L

print(dive_into_code('Noro'))
print(dive_into_code('Nakao'))
print(dive_into_code('Miyashita'))
```

関数`dive_into_code`が作成されたときに1回だけ`L=[]`となり、以後の呼び出しで`L`に要素が追加されていきます。

```:console
['Noro']
['Noro', 'Nakao']
['Noro', 'Nakao', 'Miyashita']
```

#####問10 問題文のプログラムを実行した場合の、出力結果を選びなさい。

```python:test.py
class Sample:

  c_list = []

  def add_c_list(self,data):
    self.c_list.append(data)

sample1 = Sample()
sample1.add_c_list("data1")

sample2 = Sample()
sample2.add_c_list("data2")

print("sample1:", end=" ")
for item_data in sample1.c_list:
  print(item_data, end=" ")

print("sample2:", end=" ")
for item_data in sample1.c_list:
  print(item_data, end=" ")
```

class直下で変数やmutableなオブジェクトを定義すると、インスタンス変数ではなくクラス変数となります。
インスタンス化してから更新したとしても、更新がインスタンス間で共有されてしまいます。

このコードを実行した場合の結果は以下のようになります。(これがこの問題の正解になります。)

```:console
sample1: data1 data2 sample2: data1 data2 
```

以下のように`__init__()`内に書いておけばインスタンス変数になるので、上記の問題は発生しません。

```python:test.py
class Sample:
    def __init__(self):
        self.c_list = []

    def add_c_list(self, data):
        self.c_list.append(data)


sample1 = Sample()
sample1.add_c_list("data1")

sample2 = Sample()
sample2.add_c_list("data2")

print("sample1:", end=" ")
for item_data in sample1.c_list:
    print(item_data, end=" ")

print("sample2:", end=" ")
for item_data in sample2.c_list:
    print(item_data, end=" ")
```

```:console
sample1: data1 sample2: data2 
```

今回の問題の正解は`sample1: data1 data2 sample2: data1 data2`です。

##まとめ
基本的に、どれも文法知識をシンプルに問う問題です。
競技プログラミングで求められるようなアルゴリズム力や、複雑な処理を理解する力はまったく要求されません。
難易度としては、簡単な試験に分類されるかなと思います。(名前も基礎試験ですし)
ただ、ちゃんと理解していないと勘違いしやすいところも多いので、知識の確認には有用かなと思いました。

★本試験を受けて知った内容は共有するべきではないと思うので、受験後にはこの記事は追記しない予定です。