himanago

Azure・C#などのMS系技術やLINE関連技術など、好きな技術について書くブログ

Python3エンジニア認定基礎試験に合格したので振り返り(C#er目線で感想など)

はじめに

タイトルの通りです。昨年末、12/27のことですが、駆け込みで試験を受けてきました。その振り返りです。
Python 3 エンジニア認定基礎試験」です。
www.pythonic-exam.com

経緯

昨今のPythonの人気・需要の高まりから、「Pythonを勉強したい」という声は日に日に多く聞かれるようになり、社内で育成担当をしている身としても、もういい加減無視できなくなってきたわけです。
Pythonに関してはこれまでまったく触ったことがなく、「Pythonはインデントでブロックを定義する」とか「機械学習や統計の分野で多く使われる」とかそんな程度の知識しかない状態。
なんとか効率よく基本を押さえたい…と思い見つけたのがこの試験。

今年の6月あたりだったか、割引キャンペーンで教材のオライリー本が実質無料でついてくるセットがあったので買ったのですが、なかなか時間が取れず、有効期限が年内だったので駆け込みでぎりぎり受けてきた…という経緯です。

学習内容

教材である『Pythonチュートリアル(第3版)』をざざーっと読みました。基本、それだけ。
模擬試験がここで公開されているので、それも本を読む前に1回見ました。
先に見てしまったのは、どんなふうな問題が出るのか確認したうえで本を読むほうが効率がいいと思ったからです。 ぬいぐるみほしさに合格体験記を載せてもらいました。めちゃくちゃ本名ですが、ぬいぐるみのためなら仕方ありません(本名公開が条件)。 www.pythonic-exam.com

ちなみにここでは「Python歴3日」、と書きましたが、模擬試験を見て最初に教材を開いたのが12/25で、試験を受けたのが12/27、その日の夜に合格体験談を書いて送った、というようなスピード感です。そして12/28朝には合格体験談が公開されてました。速い!
模擬試験を見て、この試験では教材の記述、特にサンプルコードがけっこう重視されているということがわかり、その内容をしっかり押さえておけば合格点の7割は簡単にとれるだろう、ということがわかったので、そのつもりで教材を読みました。合計の勉強時間は5~6時間か、もっと少ないかも…。

試験自体はかなり簡単という印象だったものの、合格体験談にも書いたようにこの試験は基礎的な知識を得るためのきっかけ・指針としてかなり役立ちました。やっぱり目標や締め切りがあると、わかりやすくそこに向かっていけるものですからね。

Pythonチュートリアルを読んで印象に残ったこと

せっかくなので、Pythonチュートリアルを読んでPythonの特徴を勉強した中で、印象に残ったことを書いておきます。
教材のページ数と、教材と同内容のWeb版の「Pythonチュートリアル」の章番号、リンクを載せておきます。
※ 前提:これまで経験した主な言語は(学習した順に) C, VBA, Java, JavaScript, C#, Groovy など。
※ 特に最近よく使うのはC#なので、C#との比較も触れられるところは触れます。

リストの内包表記

教材P.44(5.1.3. リストの内包表記)。

squares = []
for x in range(10):
    squares.append(x**2)

squares = [x**2 for x in range(10)]

と書けるというもの。ショートコーディングがはかどりそうで慣れたらきっと便利。
2乗を ** 2 で書けるのを含めてシンプルでいいですね。

ちなみに、C#でも1行。

var squares = Enumerable.Range(0, 10).Select(x => Math.Pow(x, 2));

LINQやっぱりいいなぁ。

ループ変数のスコープ

上記の「リスト内包」の利点として説明されていた、Pythonにおけるループ変数の注意点。
Pythonはループで変数のスコープが閉じないので、さきほどのリスト内包を使わないほうのコードでは変数 x があとまで残ってしまう。そのため、リストの内包表記を使いましょうということだけど、これはうっかり気を抜くと危ないな、と思いました。
特に宣言がないので、スコープがどうなってるのかはちゃんと確認しないとまずそう。 C#とかだと、

for (int i = 0; i < 10; i++)
{
    // iが使える
}
// ここでiは使えない

とやった場合 i のスコープはループ内だけにできる。まぁ、こんな素朴なforはあんまり使うなってことなんでしょうかね(PythonC#も)。
ちなみにスコープ問題でいうとC#でも、forでのブロックとスコープの感覚だと「?」となるのがout-var(outでの変数宣言)のスコープ。

if (int.TryParse(str, out var intVal)
{
    // 当然intValが使える
}
// ここでもintValが使える!

これは、もともと↓と書いていたものと同じ動きをさせたいからこうなってる、と理解してしまえばOK。

int intVal;
if (int.TryParse(str, out intVal)
{
    // 当然intValが使える
}
// ifブロックの外で宣言しているものだから当然intValが使える

タプル

教材P.48(5.3. タプルとシーケンス)。
タプルは最近C#でも入りましたが便利な機能ですね。
Pythonの場合は手軽に

t = 12345, 54321, 'hello!'

でタプルが作れるのと、
要素0のタプルは

empty = ()

で作れるということ、
それから要素1のタプルは

singleton = 'hello',   

のように末尾にカンマを付ければいい、というのがおもしろいなと思いました。

パッキング / アンパッキング

まずは教材P.48(5.3. タプルとシーケンス)あたり。
出てきたのは「引数リストのアンパック」「タプル・パッキング」「シーケンス・アンパッキング」という用語。

タプル・パッキングは、

t = 12345, 54321, 'hello!'

で3つの要素が t へタプルとしてパックされることをいい、シーケンスアンパッキングは

x, y, z = t

t の要素がx, y, zそれぞれに展開されて代入される、という話。

それに関連して、「多重代入はタプルパッキングとシーケンスアンパッキングの組み合わせにすぎない」というのがなかなかおもしろい話だなと思いました。
多重代入はこういうの。

x, y, z = 12345, 54321, 'hello!'

3つの変数に3つの値が代入されるだけだけど、読み解けば右辺がタプルになり、左辺にアンパックされて代入される、ということになるわけですね。

あとは似た用語で引数リストのアンパックとかもありました。
教材P.35(4.7.4. 引数リストのアンパック)あたり。

range(3, 6)

と渡すような関数の引数に、

args = [3, 6]
list(range(*args)) 

というふうにargsに入った要素を展開してそれぞれの引数に渡せる、というもの。 * を左側に書くことで引数リストのアンパックを実現できる。

ディクショナリのループ

教材P.53(5.6. ループのテクニック)。
ディクショナリ(辞書型)ではitems() メソッドを使うと、キーとそれに対応する値を同時に取り出せる。

knights = {'gallahad': 'the pure', 'robin': 'the brave'}
for k, v in knights.items():
    print(k, v)

これはC#でもありますね。KeyValuePairでまわすやりかた。C#だとforeachにそのまま入れるだけ。

var knights = new Dictionary<string, string>
{
    { "gallahad", "the pure" },
    { "robin", "the brave" }
};

foreach (var pair in knights)
{
    Console.WriteLine("{0} {1}", pair.Key, pair.Value);
}

C#で「Dictionaryオブジェクトをforeachで回して」っていうとだいたい上のやり方か、↓みたいなKeysで回す方法のどっちかを書いてきますね。

var knights = new Dictionary<string, string>
{
    { "gallahad", "the pure" },
    { "robin", "the brave" }
};

foreach (var k in knights.Keys)
{
    Console.WriteLine("{0} {1}", k, knights[k]);
}

比較の連鎖

教材P.54(5.7. 条件についてもう少し)。
a < b == c で、「a が b より小さく、かつ b と c が等しいかどうか」がテストできるというもの。
こういう書き方ができるのはおもしろいですね。

比較結果の代入

教材P.55(5.7. 条件についてもう少し)。

string1, string2, string3 = '', 'Trondheim', 'Hammer Dance'
non_null = string1 or string2 or string3
non_null

とやると'Trondheim'が出力される。
SQLのCOALESCEみたいな動きができておもしろい。

C#だとこうかな。

string string1 = null;
string string2 = "Trondheim";
string string3 = "Hammer Dance";
string nonNull = string1 ?? string2 ?? string3;
Console.WriteLine(nonNull);

相互参照の相対インポート

教材P.66(6.4.2. パッケージ内参照)。
パッケージ内の相互参照の相対インポートはカレントモジュールからの参照になるので、メインモジュールとして使われるものは絶対インポートで書かないとだめ。

from . import echo
from .. import formats
from ..filters import equalizer

ファイルの読み込み(ループ)

教材P.78(7.2ファイル. を読み書きする)。
for line in f: の書き方が楽でいいなと思いました。

for line in f:
    print(line, end='')

ファイルの読み込み(with)

教材P.79(7.2. ファイルを読み書きする)。
withで自動でクローズしてくれる。

with open('workfile') as f:
    read_data = f.read()

C#のusingとか、Javaのtry-with-resourcesみたいなイメージ。

例外の投げ方

教材P.86(8.4. 例外を送出する)。
例外は raise で投げる。

try:
    raise NameError('HiThere')
except NameError:
    print('An exception flew by!')
    raise

raiseはなにもかかないとそのまま上に投げてくれる。
C#のthrowも似たような感じだけど、 C#ではthrow;throw ex; で大きな違いがあってかなりの注意が必要…。
Pythonではそういう気遣いはいらなそう?

あと try-else いいな。 for-else もだけど、 finally と別にこれがあると幅が広がりそう。

def divide(x, y):
    try:
        result = x / y
    except ZeroDivisionError:
        print("division by zero!")
    else:
        print("result is", result)
    finally:
        print("executing finally clause")

Pythonは多重継承できる

教材P.93(9.5.1. 多重継承)。
Pythonは多重継承可。そういえば多重継承って使ったことないな…。

クラス変数、インスタンス変数

教材P.102(9.3.5. クラスとインスタンス変数)。
self.~ ってやらないとstaticになる。これも知らないとあぶない。。

class Dog:

    kind = 'canine'         # 全インスタンスで共有

    def __init__(self, name):
        self.name = name    # インスタンスごと

隠蔽の手段がない

教材P.104(9.4. いろいろな注意点)。
Pythonではデータ隠蔽を強制できるものは存在しない、とのこと。

まじかー。
こういう、仕様の穴?を規約とか慣習でカバーするのはあまり好きじゃないなあ。
…まぁ、いま業務では、privateって書いても問答無用でpublicになる言語1を使ってますけど。。

10章の出題密度がすごい

教材P.115から(10. 標準ライブラリミニツアー)。
試験範囲をみると、教材でいうとわずか8ページしかない第10章から4問(全体の10%)も出題されます。
なので、ここは特に真剣に読みましたね。
ここが重視されてるのは、Pythonの使い道を知るうえで大事だから…?

doctestがお手軽ですごい

教材P.120(10.11. 品質管理)。

def average(values):
    """Computes the arithmetic mean of a list of numbers.

    >>> print(average([20, 30, 70]))
    40.0
    """
    return sum(values) / len(values)

import doctest
doctest.testmod()   # automatically validate the embedded tests

こんなかんじでコメントに埋め込みテストが書ける。手軽でいいなぁ。

おわりに

実は試験当日もはじまるまで教材を読んでた(1周目)くらいなので、勉強がうすいところも多々ありました。
それでもスコアは 875/1000(合格点700点)とれて無事合格できました。
試験自体は簡単な印象だったので、Python経験者にはものたりないかな?というかんじ。

自分のように、他の言語での開発経験があって、それとの比較でささっと勉強するうえではチュートリアルは教材としていいし、試験は目標としてもちょうどいいのかなと思いました。
この試験は続編?のデータ分析試験というものが今後始まるようなので、こちらもチェックしておこうと思います。


  1. Groovyのこと