Python 3 エンジニア認定試験 勉強ノート

概要

Python3エンジニア認定基礎試験の受験勉強を行ったときのまとめノートです。

試験の公式テキスト『Pythonチュートリアル 第3版』 初版 第2刷をベースとしていますが、

  • 難しすぎて理解できなかったところ
  • 説明が足りないと感じたところ

については『みんなのPython 第3版』から適宜補足を追加しています。


Chapter1

Pythonの良い点

  • 括弧でくくる代わりにインデント (コードが見やすい、書きやすい)
  • 変数や引数の宣言が不要

Chapter2

2.1 プロンプトからの抜け方

ショートカットキー

  • Mac,Linux
    Ctrl+D
  • Windows
    Ctrl+Z

関数で抜ける場合
– quit()
– exit()

2.1.1 コマンドを引数として渡しつつPythonを実行

$ python3 -c ‘コマンド’
$ python3 -c ‘print(“kowloon”)’
kowloon

引数

引数はsys.argv[](sysモジュールのargv変数)に入る
何も与えられなければ、sys.argv[0]は空の文字列

2.1.2 対話モード

MacBook-Pro-3:~ kowloon$ python3
Python 3.4.3 (default, Aug 11 2015, 08:57:25)
[GCC 4.2.1 Compatible Apple LLVM 6.1.0 (clang-602.0.53)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>>
>>>             ←プライマリプロンプト
>>> while True:
...    print('a')
...             ←セカンダリプロンプト

2.2.1 ソースコード

UTF-8エンコードであることを想定
ソースコードの2行目で定義する

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

Chapter3

3.1.1 数値

インタプリタは電卓としても使える

  • 整数はint型、小数点があるとfloat(浮動小数点数)型
  • 除算の戻り値は常にfloat
  • 切り下げ除算は //演算子
  • 剰余 : %
  • 累乗 : **
>>> 2 + 2
4
>>> 50 - 5*6
20
>>>
>>> 17 / 3
5.666666666666667
>>>
>>> 17 // 3
5
>>> 17 % 3
2
>>> 5 * 3 + 2
17
>>>
>>> 5 ** 2
25

=で変数に値を代入

>>> a = 5
>>> b = 10
>>> a * b
50

未定義の変数を使うとエラー
ここでいう定義とは、変数に値を入れること

>>> a * z
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'z' is not defined

int と floatが混在した計算の結果はfloatに変換して返る

>>> 3 * 3.75 / 1.5
7.5
>>> 7.0 / 2
3.5

最後に表示した式が変数_(アンダースコア)に入っていて呼び出せる

>>> a = 5
>>> b = 10
>>> a * b
50
>>>
>>> z = 1000
>>> z + _     # a * bがアンダースコア部分に入り、 1000 + 5 * 10 になる
1050

3.1.2 文字列

文字列はシングルクォート、ダブルクォートのいずれかでくくる。
バックスラッシュ\でクォート文字をエスケープする。

>>> 'aaa'
'aaa'
>>> 'aaa\'bbb'  # 文中のシングルクォートをバックスラッシュでエスケープ
"aaa'bbb"
>>> "aaa'bbb"  #ダブルクォートで全体を囲っているので、文中のシングルクォートはエスケープ不要
"aaa'bbb"

\に続く文字が特殊文字として解釈されないようにしたい時は
最初の引用符の前にrをつけてraw文字列にする

>>> print('C:\some\name')    #  \n が「改行」の特殊文字として働く
C:\some
ame
>>> print(r'C:\some\name')  # raw文字列にして特殊文字を解釈させない
C:\some\name

文字列を複数行に渡って書くときはトリプルクォート"""

>>> print("""\  # トリプルクォート直後の改行を無視させるためバッククォートをつける
...     aaa
...     bbb
... """)
    aaa
    bbb

>>>

文字列は+で連結、*で繰り返し
+を使わずとも、列挙するだけで連結される(リテラル同士でのみ可能)
変数とリテラルの連結は+をつかう

>>> 'Python3' + 'チュートリアル' + '第3版'
'Python3チュートリアル第3版'
>>>
>>> 3 * 'vampire, ' + 'Killing with CIWS.'
'vampire, vampire, vampire, Killing with CIWS.'
>>>
>>> prefix = 'Py'
>>> prefix + 'thon'
'Python'
>>>

文字列のインデックス,スライシング

文字列操作の前に、シーケンス型について整理

複数の要素を持っていて、インデックスで要素を指定できる種類のデータ型を
Pythonではシーケンス型と分類しています。
文字列もシーケンス型の仲間です。

『みんなのPython 第3版』p.52

1文字目のインデックスは0
負の数を指定した場合は、末尾からのカウント
[-1]が最後の文字。[-0]ではない。

>>> word = 'Python'
>>> word[0]
'P'
>>> word[5]
'n'
>>>
>>> word[-1]
'n'
>>> word[-6]
'P'

スライシング: 文字列を「切り取る」こと。
キャラクタの範囲を指定することでスライシングする。

>>> word = 'Python'
>>> word[0:2]
'Py'
>>> word[2:5]
'tho'

始点(:の左側)で指定した文字は含まれ、終点(:の右側)で指定した文字は除外される。
第1文字を省略すると「0」が、第2文字の省略が文字列サイズとなる。

>>> word[:2]
'Py'
>>> word[2:]
'thon'
>>> word[-2:]
'on'

スライシングの覚え方:

  • インデックス番号は文字と文字の「間」の部分を指している
  • 最初の文字の左端が「0」になっている

Pythonでは、文字列は変更不可(immutable)
インデックス位置を指定して代入するとエラー

>>> word = 'Python'
>>> word[0] = 'Q'
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'str' object does not support item assignment

len()は文字列の長さを返す

>>> len(word)
6

3.1.3 リスト

  • 角括弧[ ]の中にカンマ区切りで値を指定してリストを作る。
  • 基本的に、1つのリストの中に入れる値は、型をそろえる。
  • 文字列と同じく、リストにはインデックスとスライシングが使える。
  • 文字列と異なり、リストは変更可能(mutable)
>>> list = [1, 2, 3, 4, 5]
>>> list
[1, 2, 3, 4, 5]
>>> list[0]         # インデックスで指定した位置の文字を返す
1
>>> list[-1]
5
>>> list[0:3]     # スライシング
[1, 2, 3]
>>> list[:]
[1, 2, 3, 4, 5]
>>> list[4] = 100  # インデックス4の要素を書き換え(mutable)
>>> list
[1, 2, 3, 4, 100]
>>> list[1:3] = [] # スライシングで指定した位置の値を削除
>>> list
[1, 4, 100]
>>> len(list)      # len()でリストの長さを得る
3

リストは入れ子にできる。
リストの要素自体がリスト

>>> list1 = ['a', 'b', 'c']
>>> list2 = ['x', 'y', 'z']
>>> parent_list = [list1, list2]
>>> parent_list
[['a', 'b', 'c'], ['x', 'y', 'z']]
>>>
>>> parent_list[0]   # リストの1番目の要素を取得(中身はlist1)
['a', 'b', 'c']
>>> parent_list[0][1]  # リストの1番目の要素の中の、2番目の要素を取得
'b'

Chapter4 制御構造

4.1 if文

>>> if x < 0:
...     x = 0
... elif x == 0:
...     print('zero')
... elif x ==1:
...     print('one')
... else:
...     print('more')
...
more

4.2 for文

>>> words = ['a', 'bb', 'ccc']
>>> for w in words:
...     print(w, len(w))
...
a 1
bb 2
ccc 3

4.3 range()関数

range(開始値, 終了値, ステップ数)

  • デフォルトでは0から始まる
  • 一つだけ引数を与えると、「0から始まり、引数の一つ手前の数値まで順に増える数値」が得られる。
  • インデックス指定で、任意の位置から開始可能
  • 3つめの値でステップ数を指定可能
>>> for i in range(5):
...     print(i)
...
0
1
2
3
4

range関数に与えた終端値(上記の例でいうと「5」)は含まれないことに注意。

>>> for i in range(5, 10):   # シーケンス5から始まって、10の手前まで
...     print(i)
...
5
6
7
8
9
>>> for i in range(5, 15, 3):  # 5から開始、15の手前まで、ステップは3
...     print(i)
...
5
8
11
14

range()とlen()の合わせ技

>>> list = ['a', 'b', 'c', 'd']
>>> for i in range(len(list)):   # 変数listにlen()を適用して、得られた長さをrange()に渡す
...     print(i, list[i])        #要素のインデックス番号、要素の中身を表示
...
0 a
1 b
2 c
3 d

こういう場合、enumerate()を使うと位置インデックスと値を同時に得られるのでさらに楽。

>>> list = ['a', 'b', 'c', 'd']
>>> for i, v in enumerate(list):
...     print(i, v)
...
0 a
1 b
2 c
3 d
  • range()が返すオブジェクトは、リストではない。
  • ループにより各シーケンスのアイテム連続的に返す、反復可能体(iterable)である。

4.4 break文、continue文、else節

break文を囲む、最も小さなforループ、またはwhileループを抜けることができる。
breakで抜けた場合は、else節は実行されない。
else節が発動するのは、forループでリストを使い果たす/whileで条件式がFalseになったとき。

continue文はループの残りを飛ばして次の反復に移る。

pass文は何もしない。

4.6 関数の定義 ~ 4.7

def 関数名(引数リスト):
    """ドキュメンテーション文字列(関数の簡単な説明)"""    #ここがdocstring
    処理1
    処理2

コードを書いたらdocstringを入れるのはよいこと。

デフォルトの引数も渡せる。
デフォルト値の評価は関数を定義した時点で、関数定義のスコープ内で行われる。

>>> i = 5
>>> def f(arg=i):     # この時点でデフォルト値:5で評価されている
...     print(arg)
...
>>> i = 100           # 後から異なる値を入れても・・・
>>> f()               # 関数はあくまでも『定義時に渡されたデフォルト値』(5)を使う
5                     # 関数定義スコープを出た後に定義されたi=100は反映されない
  • デフォルト値の評価は1度きり。デフォルト値に可変オブジェクトを指定するときは注意。
  • 関数をコールするときは、位置引数が先、キーワード引数は後にする
  • 位置引数は並び順を守らなければならないが、キーワード引数は順序は自由
>>> def f(width, length):         # 2つのキーワード引数
...     area = width * length
...     print(area)
...
>>>
>>> f(width=10, length=5)         # キーワード=値 で引数を指定してコール
50
>>> def f(height, width=10, length=10):  # 必須の引数1つ、デフォルト値を持つ引数2つ
...     cube = height * width * length
...     print(cube)
...
>>> f(5)         # 必須引数のheightだけ与えても成立する
500
>>>
>>> f(5, length=2, width=100)  # デフォルト値を書き換え&順番入れ替えても成立する
1000

>>> f(length=2, width=100, 5)   # キーワード引数の後に位置引数なので違反
  File "<stdin>", line 1
SyntaxError: non-keyword arg after keyword arg

引数に*名前でタプルを、**名前でディクショナリを関数に渡せる。

>>> def f(*args, **keywords):
...     print('位置引数として渡されたタプル: ', args)
...     print('引数として渡されたディクショナリ:' )
...     for kw in keywords:
...         print(kw, ':', keywords[kw])
...
>>>
>>> f('a', 'b', 'c', dic1='xxx', dic2='yyy', dic3='zzz')
位置引数として渡されたタプル:  ('a', 'b', 'c')
引数として渡されたディクショナリ:
dic3 : zzz
dic1 : xxx
dic2 : yyy

ディクショナリとして受け取った引数は、何もしなければ順序が不定となる。
上記例ではdic1,dic2,dic3の順に指定して関数を実行したのに、
表示はdic3,dic1,dic2となっている。
これが都合悪ければ、引数に対してsorted()をかけるとよい。

>>> def f(*args, **keywords):
...     print('位置引数として渡されたタプル: ', args)
...     keys = sorted(keywords.keys())              # ここにsorted()を追加して対処
...     print('引数として渡されたディクショナリ:' )
...     for kw in keys:
...         print(kw, ':', keywords[kw])
...
>>>
>>> f('a', 'b', 'c', dic1='xxx', dic2='yyy', dic3='zzz')
位置引数として渡されたタプル:  ('a', 'b', 'c')
引数として渡されたディクショナリ:
dic1 : xxx
dic2 : yyy
dic3 : zzz

引数として渡したいものが既にリスト/タプルになっているが、
渡し先の関数が位置指定引数しか受け入れないときは、引数をアンパックしなければならない。
この場合は*演算子を使うことでリストやタプルから値を取り出して関数に渡せる。

>>> list(range(3, 6))   # インデックス3から始まり、6の手前までをリスト化する処理
[3, 4, 5]

>>> args = [3, 6]      # 引数部分をリストオブジェクトでつくり、3, 6を格納
                       # この「3, 6」という情報をlist()に渡して1行目と同じ結果を得たい。

>>> list(range(args))  # エラーになる(listオブジェクトはintとして解釈されない)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'list' object cannot be interpreted as an integer

>>> list(range(*args))   # 先頭に*をつけてargsリストの中身を取り出して引数として渡す
[3, 4, 5]                # 期待の結果(1行目と同等)が得られた

タプルの場合についても同様

>>> tuple = (3, 6)        # 引数をタプルで定義
>>>
>>> list(range(tuple))    # そのままrange()に渡してもエラーになる
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'tuple' object cannot be interpreted as an integer
>>>
>>> list(range(*tuple))   # 先頭に*をつけてタプルの中身を取り出してrange()に渡せばOK
[3, 4, 5]

**演算子でディクショナリの中身を取り出して引数として関数に渡せる。

>>> def f(height, width, length,):
...     cube = height * width * length
...     print(cube)

>>> args = {'height': 10, 'width': 10, 'length': 10}  # 引数をディクショナリで定義

>>> f(args)                              # 関数にディクショナリを与えるとエラー
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: f() missing 2 required positional arguments: 'width' and 'length'

>>> f(**args)           # 先頭に**をつけて中身を取り出してから引数として渡す
1000

4.7.5 lambda(ラムダ)式

lambdaキーワードで名前を持たない関数を作れる。

lambda a, b: a + b

  • lambda関数は一つの式しか持てない
>>> def make_inc(n):
...     return lambda x: x + n
...
>>> f = make_inc(42)
>>> f
<function make_inc.<locals>.<lambda> at 0x101609f28>
>>> f(0)
42
>>>
>>> f(1)
43

4.7.6 ドキュメンテーション文字列

  • 関数の1行目(def~~のすぐ次の行)に、目的を要約して記載する。
  • 行は大文字で始まり、ピリオドで終わること。

4.8 コーディングスタイル

  • インデントはスペース4つ
  • tabは禁止
  • 79文字以下で行を折り返す
  • 演算子の周囲やカンマの後ろにはスペースを入れる。括弧のすぐ内側にはスペース入れない。
  • クラス名はCamelCase、関数及びメソッド名は英子文字とアンダースコア
  • メソッドの第一引数は必ずself
  • UTF-8エンコード

Chapter5 データ構造

5.1 listの基礎

  • list.appendix(x)
    末尾にアイテムを1つ追加
  • list.extend(L)
    リストの末尾に、他のリストLを追加してリストを延長
  • list.insert(i, x)
    指定されたインデックス位置iにアイテムxを挿入する
  • list.remove(x)
    値がxである最初のアイテムを削除。
    該当するアイテムが無ければエラー。
  • list.pop(i)
    指定されたインデックス位置iにあるアイテムをリストから削除しつつ、そのアイテムを戻り値として返す
  • list.clear()
    リストから全てのアイテムを削除する。
    リスト自体は残る。
  • list.index(x)
    値がxである最初のアイテムのインデックスを返す。
    該当するアイテムが無ければエラー。
  • list.count(x)
    リスト中の値xに合致するアイテムの個数を返す。
  • list.sort(key=None, reverse=False)
    リストをソートする。
    コピーは取らず、そのリストオブジェクト自体が変更される。
  • list.reverse()
    リストの要素を逆順にする。
  • list.copy()
    リストのシャローコピーを返す。
>>> testlist = [1, 2, 3, 4, 5]
>>>
>>> testlist
[1, 2, 3, 4, 5]
>>>
>>> testlist.append(6)
>>>
>>> testlist
[1, 2, 3, 4, 5, 6]
>>>
>>> list2 = [101, 102, 103]
>>>
>>> testlist.extend(list2)
>>>
>>> testlist
[1, 2, 3, 4, 5, 6, 101, 102, 103]
>>>
>>> testlist.insert(0, 500)
>>>
>>> testlist
[500, 1, 2, 3, 4, 5, 6, 101, 102, 103]
>>>
>>> testlist.remove(101)
>>> testlist
[500, 1, 2, 3, 4, 5, 6, 102, 103]
>>>
>>> testlist.remove(9999)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: list.remove(x): x not in list
>>>
>>> testlist.pop(0)
500
>>>
>>> testlist
[1, 2, 3, 4, 5, 6, 102, 103]
>>>
>>> testlist.pop()
103
>>> testlist
[1, 2, 3, 4, 5, 6, 102]
>>>
>>> testlist.index(102)
6
>>>
>>> testlist.count(2)
1
>>>
>>> testlist.insert(0, 999)
>>> testlist
[999, 1, 2, 3, 4, 5, 6, 102]
>>> testlist.sort()
>>> testlist
[1, 2, 3, 4, 5, 6, 102, 999]
>>>
>>> testlist.reverse()
>>> testlist
[999, 102, 6, 5, 4, 3, 2, 1]
>>>
>>> testlist.copy()
[999, 102, 6, 5, 4, 3, 2, 1]
>>>
>>> testlist.clear()
>>> testlist
[]

リストをスタック(後入れ先出し)として使う

>>> stack = [1, 2, 3, 4, 5]
>>>
>>> stack.append(6)
>>> stack
[1, 2, 3, 4, 5, 6]
>>>
>>> stack.pop()
6
>>> stack
[1, 2, 3, 4, 5]
>>> stack.pop()
5
>>> stack.pop()
4
>>> stack
[1, 2, 3]

リストをキュー(先入れ先出し)として使う

リストでキュー(先入れ先出し)操作を行うのは低速なのでcollections.dequeが望ましい。

>>> from collections import deque
>>>
>>> queue = deque(['a', 'b', 'c'])
>>> queue.append('y')                   # 末尾に追加
>>> queue.append('z')                   # 末尾に追加
>>>
>>> queue
deque(['a', 'b', 'c', 'y', 'z'])
>>>
>>> queue.popleft()           # 先頭の要素を取り出し
'a'
>>> queue.popleft()           # 先頭の要素を取り出し
'b'
>>> queue
deque(['c', 'y', 'z'])

5.1.3 リスト内包

例:0から9までの数字をそれぞれ二乗してリストに追記するとき

forループを使うと:

>>> squares = []
>>> for x in range(10):
...     squares.append(x**2)
...
>>> squares
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

lambda関数を使うと:

>>> squares = list(map(lambda x: x**2, range(10)))
>>>
>>> squares
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

リスト内包を使うと:

>>> squares = [x**2 for x in range(10)]
>>> squares
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

リスト内包の書き方

[式 for節]
または
[式 if節]

式を後続のfor節/if節で評価した値がリストに入る

式部分にタプル等も持ってこれる

>>> [(x, x**2) for x in range(6)]
[(0, 0), (1, 1), (2, 4), (3, 9), (4, 16), (5, 25)]

結果をタプルとして格納したい場合は、上記のように丸括弧で囲わないとエラー

>>> [x, x**2 for x in range(6)]
  File "<stdin>", line 1
    [x, x**2 for x in range(6)]
               ^
SyntaxError: invalid syntax

入れ子構造になったリストを、2つの入れ子forループで平準化(1次元化)

>>> origin_list = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
>>> [num for elem in origin_list for num in elem]
[1, 2, 3, 4, 5, 6, 7, 8, 9]

1つ目のforループで、入れ子リストの1つ目の要素[1, 2, 3]を取り出し、
2つ目のforループで[1, 2, 3]の1つ目の要素「1」を取り出し。
以下、順に実行すると、単一のリストに全ての値が書き出される。

5.2 del文

del文を使えば、インデックスを指定してリスト内のアイテムを削除できる。
pop文と異なり、値を返さない。
スライス指定での削除、リスト全体の消去にも対応。

>>> testlist = [1, 2, 3, 4, 5, 6]
>>> testlist
[1, 2, 3, 4, 5, 6]
>>>
>>> del testlist[2]    # インデックス位置2にある値を消去
>>>                # del文は返り値が無い(popとの違い)に注意
>>> testlist
[1, 2, 4, 5, 6]
>>>
>>> del testlist[1:3]  # スライス指定で「インデックス1から、3の手前まで」を指定して消去
>>> testlist
[1, 5, 6]
>>> del testlist[:]    # スライス指定で「全て」を指定して消去
>>> testlist
[]
>>> del testlist       # 変数そのものを消去も可能
>>>
>>> testlist
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'testlist' is not defined   # 変数自体が消えたので参照エラー

5.3 タプル

値をカンマで区切り変数に格納するとタプルになる。
タプルは一度定義されたら値を変更不能(immutable)。
ただし、リストのような変更可能オブジェクトをタプルに入れることはできる。

>>> t = 'a', 'b', 'c'   # 外枠の丸括弧無しでもタプルとして格納される
>>> t
('a', 'b', 'c')
>>>
>>> t[0]
'a'
>>> t[0] = 'z'                      # 定義済みタプルは内容変更不可
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'tuple' object does not support item assignment

>>> u = t, (1, 2, 3)          # 2つのタプルを入れ子にする
>>> u
(('a', 'b', 'c'), (1, 2, 3))

アイテム数が0もしくは1だと、区切りのカンマがつけられないので、変数定義時にタプルにできない。

>>> t = 'a'           # 本当はタプルにしたかったのだが、
>>> t
'a'                   # ただの文字列になってしまった

>>> t = ('a')         # ならばと丸括弧で囲ってみたが、
>>> t
'a'                   # やっぱりただの文字列になってしまった

回避策:
– 中身の無い、空のタプル
()とすることで作成可能
– 値が1つだけのタプル
→ 1つの値の後ろにカンマを付けて定義

>>> empty = ()              # 中身の無い丸括弧
>>> empty
()
>>>
>>> one_item_tuple = 'a',    # 末尾にカンマをつける
>>>
>>> one_item_tuple
('a',)

5.4 集合(set)

集合:
重複しない要素を順不同で集めたもの。

集合の用途:
– 値が存在するかどうかの判定
– 重複エントリの排除

集合の作り方:
– 中括弧{ }で囲う
– set()関数を使う
– 中身が空の集合を作るときはset()を使う

>>> testset = {'a', 'b', 'c', 'a', 'c'}  # aとcを2回登場させて、わざと重複させる
>>> testset
{'a', 'c', 'b'}           # 文字の重複が排除されている
>>>
>>> 'a' in testset        # 要素が存在するか判定
True
>>> 'B' in testset
False

5.5 ディクショナリ

  • key : 値のペアで構成された集合、と考える。
  • key : 値のペアをカンマで区切って、{ }でくくる
  • 要素にはkeyによるインデックスがつけられている。
    (= インデックス番号ではなく、keyで指定する)
  • keyに使えるもの
    文字列、数値、タプル(タプルが文字列/数値/タプルだけを含む場合に可能)
  • keyに使えないもの
    可変型のオブジェクト、リスト、
>>> mgmt_data = {"article_id": 161, "post_date": "2017-06-17T17:33:29"}
>>>
>>> mgmt_data['link'] = "https://blog.kowloonet.org/"  # 要素を追加
>>>
>>> mgmt_data
{'link': 'https://blog.kowloonet.org/', 'article_id': 161, 'post_date': '2017-06-17T17:33:29'}
>>>
>>> del mgmt_data['link']    # key名「link」の要素を削除
>>>
>>> mgmt_data
{'article_id': 161, 'post_date': '2017-06-17T17:33:29'}
>>>
>>> list(mgmt_data.keys())   # key値をリストとして取得(ソートはされない)
['article_id', 'post_date']
>>>
>>> 'article_id' in mgmt_data # 特定のkey名があるか判定
True

dict()により「key : 値」ペアのタプルからディクショナリを作る

>>> dict([('police', 110), ('fire', 119), ('time', 117)])
{'police': 110, 'fire': 119, 'time': 117}

ディクショナリもリストのように内包表記ができる

>>> {price: price*1.08 for price in (100, 200, 1000)}  # 定価と消費税込み価格のペア
{200: 216.0, 1000: 1080.0, 100: 108.0}

ループのテクニック

item()を使うことで、keyと値を同時に取得できる。

>>> lamp = {'red': 'stop', 'blue': 'go', 'yellow': 'attention'}
>>> for key, value in lamp.items():
...     print(key, value)
...
yellow attention
red stop
blue go

複数のシーケンスに同時にループをかけるとき、zip()を使うと両者のエントリをペアにできる。

>>> lamp = ['red', 'yellow', 'blue']
>>> action = ['stop', 'attention', 'go']

>>> for l, a in zip(lamp, action):
...     print('If lamp indicates {0}, you should {1}.'.format(l, a))
...
If lamp indicates red, you should stop.
If lamp indicates yellow, you should attention.
If lamp indicates blue, you should go.

シーケンスを逆順にループするときは、正順でシーケンスを指定し、reversed()

>>> for i in reversed(range(1, 10, 2)):
...     print(i)
...
9
7
5
3
1

シーケンスをソート順にループするときはsorted()

>>> tanks = ['M3', 'Type89', 'B1bis', '38t']
>>> tanks
['M3', 'Type89', 'B1bis', '38t']
>>> for t in sorted(set(tanks)):
...     print(t)
...
38t
B1bis
M3
Type89

シーケンスの比較

同じシーケンス型オブジェクト同士は比較ができる。
辞書的順序で比較される。

  • 最初のアイテムを比較
    →値が異なれば大小を判定
    →値が同値の場合は2番目のアイテムを比較
  • 同じアイテムが続く場合は、どちらかのシーケンスがなくなるまで比較が続く
(1, 2, 3)                < (1, 2, 4)
[1, 2, 3]                < [1, 2, 4]
'ABC' < 'C' < 'Pascal'   < 'Python'
(1, 2, 3, 4)             < (1, 2, 4)
(1, 2)                   < (1, 2, -1)
(1, 2, 3)                == (1.0, 2.0, 3.0)
(1, 2, ('aa', 'ab'))     < (1, 2, ('abc', 'a'), 4)

Chapter6 モジュール

モジュールとは:
Pythonの定義や文が入ったファイルのこと。

モジュールがimportされた時:
グローバル変数__name__の値としてモジュール名が入る

testmod.pyというモジュールがあり、これを
import testmod
とインポートすると、
グローバル変数__name__の値としてtestmodが入る

モジュールが「python3 モジュール名.py」として実行された時:
グローバル変数__name__の値として__main__が入る

以下のスクリプトを作成し、mod_test.pyとして保存

$ cat mod_test.py
if __name__ == '__main__':
    print('__name__ is __main__.')
elif __name__ == 'mod_test':
    print('__name__ is mod_test.')

importした場合:

>>> import mod_test
__name__ is mod_test.   # importされたので、グローバル変数__name__の中身はモジュール名
>>>
>>>
>>> import mod_test     # モジュール内の文が実行されるのは初回きりなので、
>>>                     # 同じIPython上でもう一度importしても何も起きない

スクリプトとして実行した場合:

$ python3 mod_test.py
__name__ is __main__.

コンパイル済みPythonファイル

__pycache__フォルダにモジュール名.バージョン名.pyc の名前でコンパイル済モジュールが格納される

$ cd __pycache__/
$ ls
mod_test.cpython-34.pyc

Pythonのバージョンが含まれるため、異なるバージョンでコンパイルされたモジュールが共存可能

パッケージ

パッケージ:
ドット区切りモジュール名を使ってPythonのモジュールを構築する方法。
パッケージの実体は、モジュールとなるファイルを納めて階層化されたディレクトリ。
階層構造の区切りとしてドットが使われる。

モジュール名A.B
「パッケージA」にある「サブモジュールB」の意

ディレクトリをパッケージとして扱わせるにはf__init__.pyを配置する。
__init__.pyがないと、パッケージとして認識されない。
(import時に”ImportError: No Module”となる)

「from パッケージ import アイテム」
アイテムはパッケージのサブモジュール、関数、クラス、変数いずれでもOK。

「import アイテム.サブアイテム.サブサブアイテム」の場合は
サブサブアイテムより上は全てパッケージでなければいけない。

意図しないimportを避けるために
__init__.py__all__というリストを定義し、「import * 」が指定されたときに
インポートすべきモジュールをリスト化しておく。

たとえば
kindle/sale/__init__.py に以下のように記述した場合

__all__ = ['get_price', 'search_item', 'update_item']

from kindle.sale import *とすると、__all__に指定されている3つのサブモジュールがインポートされる。

from パッケージ import 指定のサブモジュールの書き方が好ましいので可能な限りこの指定の仕方をすること。

kindle/
    __init.py__
    sale/
      __init.py__
      get_price.py
      search_item.py
      update_item.py
      ...
    point/
      __init.py__
      get_point.py
      verify_point.py

上記の構成で、get_price.pyモジュールの中で相対指定でインポートするとき

from . import search_item           # 同じ階層のsearch_itemモジュールをインポート
from ..point import get_point.py    # 一つ上の階層に上がって、point配下のget_point.pyをインポート

上記ではimportを行っているモジュール自身からみた、相対指定 をしているが、Pythonアプリのメインモジュールとして使うことがありえるモジュールの場合は、常に絶対インポートで書くこと。


Chapter8 エラーと例外

構文エラー(SyntaxError):
文、式の書き方が誤っている場合

例外(exception):
文、式が構文的には正しいが、実行中にエラーとなった場合など、
「プログラム上で起こる全てのエラー」は例外インスタンスとして生成される。
例外インスタンスは「Exception」という名前のクラスを親とし、種類によって分類されている。

例外を使うことの最大の利点は、「エラーの発生位置とエラー処理を分離することができる」という点です。同じエラーでも、プログラムを停止すべきか実行を続けるべきかは、処理の内容によって変わってきます。処理を行う側でいろいろなケースに対応するのでは、プログラムが煩雑になってしまいます。エラーを例外として外部に伝え、エラーに対する対処を外部にゆだねることで、プログラムとしてもシンプルに保つことができます。

『みんなのPython 第3版』p.295

try…except文

  • 最初にtry: 節の処理を実行
  • 例外が発生しなければexcept節はスキップされ、try文の実行が終了する
  • try節の実行中に例外が発生すると、try節の残りの処理はスキップされる
  • 発生した例外のタイプがexcept節で指定された物と一致すれば、そのexcet節の内容が実行される
  • 発生した例外のタイプがexcept節で指定された物と一致しない場合、その例外はさらに外側にあるtry文に渡されていく。except節が見つからなければ未処理例外(unhandled exception)となりPythonスクリプトの実行が終了する。
  • try – except文の最後尾に、型を指定しないexcept節を書いておけば、ワイルドカード的に振る舞い、「全ての例外」にマッチする

エラーの原因を見つけやすくするため、except節にはなるべく型指定を行うこと。

try…except文の末尾にelse節をつけることができる。
try節で例外が発生しなかった時にelse節が実行される。

try節にコードを追加するより、else節を使った方が良い。

try:
    処理1   # Tryさせたい処理(例えばファイルのOpen)
    処理2   # 処理1がうまくいったときに行う処理
except エラー型:
    エラー処理1

上記↑のように書くのではなく、こうする↓

try:
    処理1   # Tryさせたい処理(例えばファイルのOpen)
except エラー型:
    エラー処理1
else:
    処理2   # try節で例外が出ない(=成功した)ときに行う処理

except節では例外の後ろに変数を指定でき、この変数には例外インスタンスが代入される。
例外インスタンスの引数は.argsに格納される。
例外インスタンスには特殊メソッド__str()__が予め定義されているので、
print(例外インスタンス名)といきなり指定しても表示することができる。

try:
    raise Exception('Error is here!')
except Exception as inst:  # 例外「Exception」のインスタンスを変数instとして定義
    print(type(inst))      # 例外インスタンス「inst」のタイプを表示
    print(inst.args)       # 例外インスタンス「inst」の引数(.args)を表示
    print(inst)            # 例外インスタンス「inst」の引数をargs指定なしで表示
    x = inst.args          # 引数のアンパック
    print('x =', x)        # アンパックした引数を表示


<class 'Exception'>        # タイプは'Exception'クラス
('Error is here!',)        # エラーの中身  タプルになっている
Error is here!             # args指定無しで表示
x = ('Error is here!',)    # アンパックしたエラー引数を表示

8.4 例外の送出

raise文で指定の例外を意図的に発生させることができる。
raiseの引数で送出する例外のタイプを指定できる。

>>> raise ValueError('Error is here!')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: Error is here!

8.5 ユーザ定義例外

ユーザーが独自に例外を定義することができる。

例外は直接、または間接にExceptionクラスから派生したクラスとして作成すべき

>>> class MyError(Exception):           # 独自の例外"MyError"を定義
...     def __init__(self, value):
...         self.value = value
...     def __str__(self):
...         return repr(self.value)

>>> try:
...     raise MyError(2*2)
... except MyError as e:
...     print('User-defined exception has occurred, value:', e.value)
...
User-defined exception has occurred, value: 4

1行目でMyErrorを定義するところで、(Exception)とし、Exceptionクラスを継承させている。
親であるExceptionクラスの処理をそのまま利用しつつ、新しい振る舞いとして
「value属性を生成する」という処理を加えている。

8.6 クリーンアップ動作の定義

try文にはfinally節を追加できる。

try節で例外が起きようが起きまいが、
except節が実行されようがされまいが、
else節が実行されようがされまいが、
finally節の処理はtry文から抜けるタイミングで必ず実行される

>>> try:
...     print('abc')
... except:
...     print('xxxxx')
...
abc        # Try節の中身が実行された
           # Try節の処理が例外なく終わったので、except節はスキップされて何もおきない

>>> try:
...     print('abc')
... finally:
...     print('xyz')
...
abc        # Try節の中身が実行された
xyz        # Try節の処理が例外なく終わったが、finally節は必ず実行される

実践的なプログラミングでは、外部リソース(ファイル、ネットワーク接続など)を、その利用が成功したか否かに関わらず開放するのに使うと良い。
(「ファイルつかみっぱなし、コネクション張りっぱなしのまま異常終了」を防ぐ)

悪い例:

>>> for line in open('test.txt'):
...     print(line)

ファイルを閉じる処理がないので、異常時にファイルが開きっぱなし、が起こりうる。

with文を使うことで、ファイル等のオブジェクトを使用後すぐに適切な方法でクリーンアップすることを保証できる。

良い例:

>>> with open('text.txt') as f:
...     for line in f:
...         print(line)

上記with文の実行後、ファイルfは即時クローズされる。
ブロック内の処理でエラーが発生したとしても、常にクローズされる。


Chapter9 クラス

クラスをひと言で説明すると、プログラムで利用するオブジェクトの設計図と言えます。
(中略)
クラスを定義することは、インスタンスがどのような性質を持っているのかに注目して、クラスの設計図を作ることに他なりません。

(みんなのPython 第3版 p.248-249)

9.2 スコープと名前空間

名前空間

名前空間とは:
名前とオブジェクトの対応付けのこと。

名前空間とは、オブジェクトが所属する「場所」のことです。Pythonの場合は、コードのなかのどのような位置でオブジェクトが最初に定義されたかによって、所属する名前空間が決まります。

『みんなのPython 第3版』p.330

スコープ

スコープとは:
ある名前空間から直接アクセスできる、プログラムテキスト上の範囲。
「直接アクセス」とは、モジュール名などを付与しないでダイレクトに名前指定できること の意

スコープとは、コード上でオブジェクトが有効になる範囲のことを言います。どのような名前がどの範囲で有効と判断されるかは、その名前が所属している名前空間によって決まります。

『みんなのPython 第3版』p.330

関数内で定義された変数は、関数の内部(関数のブロック内)でしか利用できない。
このような変数をローカル変数と呼ぶ。

変数が作られた場所が変数の性質を決定する

>>> def value(num):
...     a = 1              # 関数の中で変数を定義 = ローカル変数
...     print(a, num)
...
>>>
>>> value(100)
1 100
>>> print(a)        #関数を出た時点で変数a, numは消えているので、参照エラーになる
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'a' is not defined
>>> print(num)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'num' is not defined

クラス定義に入ると新しい名前空間が生成され、ローカルスコープとして扱われる。
クラス定義を抜けるとクラスオブジェクトが作られる。
クラス定義で作られた名前空間が中に包まれているので、クラス内の変数や関数などを属性として参照できる。

クラスのインスタンス化

x = MyClass()

MyClassクラスのインスタンスが生成され、ローカル変数xに代入される。

インスタンスが生成されたときに初期実行させたいことを
__init__()メソッドに定義しておく。

def __init__(self):
    self.data = []

なぜ引数にselfがあるのか

selfはインスタンス自分自身を指す。
__init__()に引数としてインスタンス自身(self)を渡しているからこそ、
インスタンスそのものを操作(初期化処理の適用など)できている、と理解する。


Chapter10 標準ライブラリ

OS

>>> import os
>>> os.getcwd()
'/Users/kowloon'
>>>
>>> os.chdir('/Users/kowloon/Desktop')
>>> os.getcwd()
'/Users/kowloon/Desktop'
>>>
>>> os.system('mkdir testdir')
0
>>>

「from os import *」は絶対使わないこと。「import os」とすること
os.open()がビルトイン関数のopenを上書きしてしまうため。

ビルトイン関数dir()でOSモジュールの持つ関数をリストで取得できる。

>>> dir(os)
['CLD_CONTINUED', 'CLD_DUMPED', 'CLD_EXITED',
   (snip)
'write', 'writev']
>>>

ビルトイン関数help()でモジュールのヘルプが参照できる。
このヘルプはモジュールのdocstring内で書かれている。

Help on module os:

NAME
    os - OS routines for NT or Posix depending on what system we're on.

MODULE REFERENCE
    http://docs.python.org/3.4/library/os

    The following documentation is automatically generated from the Python
    source files.  It may be incomplete, incorrect or include features that
    are considered implementation detail and may vary between Python
    implementations.  When in doubt, consult the module reference at the
(snip)

ファイル、ディレクトリ管理にはshutilモジュールが好ましい。

>>> import shutil
>>> shutil.move('testdir', 'testdir2')
'testdir2'

引数

コマンドライン引数はsysモジュールのargv属性にリスト形式で格納される

$ cat argv_test.py

import sys
print(sys.argv)

$ python3 argv_test.py aaa bbb ccc
['argv_test.py', 'aaa', 'bbb', 'ccc']

正規表現

正規表現はreモジュールを使う。

>>> import re
>>>
>>> re.findall(r'\bR[A-Z]*[0-9]*', 'RADIUS IEEE802.3ad H.265 RFC7348')
['RADIUS', 'RFC7348']

正規表現を使うまでもないときは文字列が持つメソッドで十分

>>> texts = 'aaa bbb zzz'
>>> texts.replace('zzz', 'ccc')
'aaa bbb ccc'

数学

mathモジュール

>>> import math
>>> math.pi
3.141592653589793
>>> math.e
2.718281828459045
>>>

randomモジュール

>>> import random
>>>
>>> random.choice(['aaa', 'bbb', 'ccc'])
'bbb'
>>>
>>> random.sample(range(100), 10)
[87, 96, 6, 46, 71, 52, 84, 42, 86, 58]

インターネットアクセス

urllibモジュール

>>> from urllib.request import urlopen
>>> with urlopen('http://google.co.jp') as response:
...     for line in response:
...         print(line)
...

b'<!doctype html><html itemscope="" itemtype="http://schema.org/WebPage" lang="ja"><head>
(snip)

日付と時間

datetimeモジュール

>>> from datetime import date
>>> now = date.today()
>>> now
datetime.date(2017, 6, 23)
>>>
>>> print(now)
2017-06-23
>>>
>>> now.strftime("%m-%d-%y. %d %b %Y is a %A on the %d day of %B.")
'06-23-17. 23 Jun 2017 is a Friday on the 23 day of June.'

データ圧縮

>>> import zlib
>>> s = b'witch which has which witches wrist watch'
>>>
>>> len(s)
41
>>>
>>> t = zlib.compress(s)
>>> len(t)
37
>>>
>>> zlib.decompress(t)
b'witch which has which witches wrist watch'
>>> zlib.crc32(s)
226805979

Chapter12 仮想環境

仮想環境の作成

pyvenv 仮想環境ディレクトリ名

MacBook-Pro-3:~ kowloon$ pwd
/Users/kowloon

MacBook-Pro-3:~ kowloon$ pyvenv tutorial-env
MacBook-Pro-3:~ kowloon$ ll -d tutorial-env/
drwxr-xr-x  6 kowloon  staff  204  6 23 13:23 tutorial-env/

MacBook-Pro-3:~ kowloon$ ls tutorial-env/
bin   include   lib   pyvenv.cfg

MacBook-Pro-3:~ kowloon$ source tutorial-env/bin/activate
(tutorial-env) MacBook-Pro-3:~ kowloon$
(tutorial-env) MacBook-Pro-3:~ kowloon$ python
Python 3.4.3 (default, Aug 11 2015, 08:57:25)
[GCC 4.2.1 Compatible Apple LLVM 6.1.0 (clang-602.0.53)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>>
>>>

pip

pip install パッケージ名
pip install パッケージ名==バージョン番号
pip install --upgrade パッケージ名
pip uninstall パッケージ名
pip show パッケージ名
pip list

Chapter14 補完・ヒストリ

Tabキーで変数、モジュールの補完ができる。

>>> import random
>>>
>>> random.cここでTab
>>> random.choice(    # choiceメソッドが補完された

>>> string  = 'abc'
>>>
>>> string.ここでTab2回
string.__add__(           string.__rmod__(          string.istitle(
string.__class__(         string.__rmul__(          string.isupper(
string.__contains__(      string.__setattr__(       string.join(
string.__delattr__(       string.__sizeof__(        string.ljust(
>>>

ユーザディレクトリの.python_historyファイルに履歴が入る。

>>>exit()
MacBook-Pro-3:~ kowloon$ tail .python_history
exit()
import sys
sys.path
import random
string  = 'abc'
string.
exit()

分からん集

読んでも分からなかったところを負債として記載。

4.2 for文

p.24

反復中のシーケンスを改変する必要があるときは(たとえば選択したアイテムを複製するなど)、コピーを取って反復をかけることが推奨される。シーケンスに反復をかけることで暗黙にコピーが取られたりはしない。これにはスライス表記を使うのがよい

>>> for w in words[:]: # リスト全体のスライスコピーにループをかける
... if len(w) > 6:
... words.insert(0, w)
...
>>> words
['defenestrate', 'cat', 'window', 'defenestrate']

「スライスコピー」の概念を分かっていないので、これで何がどう良くなったのか分からない。

4.6 関数の定義

p.28

関数の実行により新しいシンボル表が導入され、これは関数内のローカル変数に使われる。より正確に言うと、関数内でのあらゆる代入は、その値がローカル変数のシンボル表に格納される。ちなみに変数の参照は、最初にローカルのシンボル表を、 次にそれを含む各関数のシンボル表を、続いてグローバルのシンボル表を、最後にビルトイン名のシンボル表を調べるようになっている。

シンボル表のイメージが湧かないため、言われていることがよくわからない。