Python
python3

【Python入門】いまさらだけどパイソニスタとして必要な文法を網羅してみた

はじめに

Pythonの対象バージョンは3系です。(2系でも通じるものは多いはず)
対象読者レベルは他言語を軽くでも触ったことある人です。「あー、Pythonだとこれはこう書くのねー」が網羅的に分かるようにまとめたつもりです。
ここではWEBアプリケーションフレームワーク(DjangoとかFlaskとか)に関してはほぼ触れません。
@shiracamus 多くの有益なコメントと編集リクエストありがとうございます。

Python開発環境構築

下記(私の記事ですが)はWindowsでの構築となっていますが、Macでもほぼ同じ通りにできます。
今からWindowsでベストなPython開発環境を構築する手順

Pythonについて

今最も勢いに乗っているプログラミング言語。
オブジェクト指向。Pythonでは全てがオブジェクト。
動的言語(スクリプト言語)のため、変数宣言時に型を明示的に定義しない。
AIやデータサイエンス系で話題になることが多いが、汎用言語なのでWeb系とか他の用途にももちろん使える。
インデントでブロックを構成。
命名規則は原則スネーク型(小文字英単語をアンダースコア(_)で繋ぐ)。

長所

  • 学習コストが他言語に比べて低い
  • シンプルに書ける
  • 標準ライブラリでできることが豊富
  • AIやデータサイエンス系サードパーティが豊富(ドキュメントも含め)
  • 64ビットよりも大きな数値を扱える(オーバーフローしにくい)ため学術計算に強い
  • Webアプリケーションフレームワークが豊富

短所

  • C、C++、Javaなどに比べて遅い場合が多い
  • 3系と2系の互換性が無い
  • Webアプリケーションフレームワークの日本語ドキュメントが少ない

標準入出力

標準出力

改行あり

print関数で改行ありの標準出力。

print("Hello world.")
print("How is it going?")
実行結果
Hello world.
How is it going?

改行なし

print関数のendキーワード引数に''を指定して改行なし標準出力。

print("Hello ", end='')
print("world.", end='')
実行結果
Hello world.

あるいはsysモジュールのstdoutオブジェクトのwriteメソッドで改行なし標準出力。

import sys

sys.stdout.write("Hello ")
sys.stdout.write("world.")
実行結果
Hello world.

文字列の中に変数を埋め込む

formatメソッドで文字列の中に変数を埋め込む。
文字列中で変数部分を{}とする。
.formatで変数を指定する。
変数が複数ある場合は、formatメソッドの位置引数の順番と{}内のインデックス(0始まり)を合わせる。

name = "james"
age = 28
print("My name is {0}. I am {1} year-old.".format(name, age))
実行結果
My name is james. I am 28 year-old.

Python3.6以降ではf-stringsで{}内に変数名を直接指定することが可能。

name = "james"
age = 28
print(f"My name is {name}. I am {age} year-old.")
実行結果
My name is james. I am 28 year-old.

標準入力

input関数で標準入力。
input関数に与える文字列パラメータは標準出力される。入力を促すような文字列を与えてあげると親切。

sample_input = input("input something: ")
print(sample_input)
実行結果
input something: 1
1

変数

グローバル変数とローカル変数

グローバル変数とは、どのスコープからもアクセス可能な変数。
ローカル変数とは、その変数を宣言した関数やメソッド内でのみアクセス可能な変数。

# グローバル変数
sample_global = "global"


def sample_print():
    # ローカル変数
    sample_local = "local"
    print(sample_global)
    print(sample_local)


sample_print()
実行結果
global
local

関数やメソッド内でグローバル変数の値を変更しようとしても、それはローカル変数の宣言とみなされてしまう。
下の例では関数外で宣言したsample_globalと関数内で宣言したsample_globalのオブジェクトIDが異なることを示している。

sample_global = "global"


def sample_print():
    sample_global = "global after"
    print(id(sample_global))


sample_print()
print(id(sample_global))
実行結果
2759339571824
2759309763224

グローバル変数の中身を関数内で変更したい場合はglobalを使う。

sample_global = "global"

def sample_print():
    global sample_global
    sample_global = "global after"
    print(sample_global)


print(sample_global)
sample_print()
print(sample_global)
実行結果
global
global after
global after

パブリック変数とプライベート変数

パブリック変数とは、クラス外からもアクセス可能な変数。
プライベート変数とは、その変数を宣言したクラス内からのみアクセス可能な変数。
実はPythonには厳密的なプライベート変数は存在しないが、変数の先頭にアンダーバーを付けることで、それに近いことが実現できる。

変数の先頭にアンダーバー1つ

クラス外部からアクセスはできるが、慣習的にプライベート変数として扱われる。

変数の先頭にアンダーバー2つ

通常ではクラス外部からアクセスできないようになる。
しかしながら、インスタンス名._クラス名__プライベート変数の形式でアクセスできる。したがって、ガチなプライベート変数はPythonには存在しない。

実装例

class SamplePrivate:

    # パブリック変数
    sample_public_var = "This is a public var."

    # 慣習的にプライベート変数
    _sample_private_var = "This is a conventional private var."

    # ほぼプライベート変数
    __sample_private_var = "This is a nearly private var."


sp = SamplePrivate

# 当然ながらパブリック変数にはアクセス可
print(sp.sample_public_var)

# アクセス可だが慣習的に非推奨(Pycharm上でも警告がでるしコード補完もされない)
print(sp._sample_private_var)

# 直接アクセス不可
# 下行のコメントアウトを外すと、"AttributeError: type object 'SamplePrivate' has no attribute '__sample_private_var'"とエラーになる。
# print(sp.__sample_private_var)

# これならアンダーバー2つのプライベート変数にアクセス可(Pycharm上で警告あり)
print(sp._SamplePrivate__sample_private_var)
実行結果
This is a public var.
This is a conventional private var.
This is a nearly private var.

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

クラス変数はクラス内で共通の変数。インスタンス化しなくてもアクセス可能。(Javaでいうstatic変数)
インスタンス変数はインスタンスごとに独立した変数。一般的に__init__メソッドでself引数に渡されたインスタンスに初期値を代入する。

class SampleClass:

    # クラス変数
    sample_class_var = "This is a class var."

    def __init__(self, sample_instance_var):
        # インスタンス変数
        self.sample_instance_var = sample_instance_var


# クラス変数にアクセス
print(SampleClass.sample_class_var)

# インスタンス変数にアクセス
sc = SampleClass("This is a instance var.")
print(sc.sample_instance_var)
This is a class var.
This is a instance var.

データ型

Pythonは動的言語のため変数宣言時に型を宣言しない。
しかし、当然ながら型自体は存在する。

文字列

宣言

文字列はダブルクォート("")あるいはシングルクォート(')で囲んで宣言する。
ダブルクォートとシングルクォートのどちらを使用しても違いはないが、コード内では揃えるべき。

sample_str = "sample"
print(sample_str)
sample_str = 'sample'
print(sample_str)
実行結果
sample
sample

連結(+)

+で文字列同士を結合。

python = "Python"
sample_str = python + " is"
sample_str += " interesting."
print(sample_str)
実行結果
Python is interesting.

繰り返し(*)

*で文字列を繰り返す。

sample_str = "Python" * 3
print(sample_str)
実行結果
PythonPythonPython

分割(split)

splitメソッドで文字列を分割。
パラメータにセパレータを指定。結果はリストに格納される。

sample_str = "TOEIC:790"
sample_list = sample_str.split(":")
print(sample_list[0])
print(sample_list[1])
実行結果
TOEIC
790

セパレータを指定しなかった場合は、空白文字類(タブや改行含む)で分割。

sample_str = "abc def\tghi\njkl"
sample_list = sample_str.split()
print(sample_list)
['abc', 'def', 'ghi', 'jkl']

スライス([::])

スライスで部分文字列抽出。
[start:end:step]形式で、"start"と"end"で開始位置と終了位置を指定する。"step"で飛ばしや逆順を指定できる。
開始インデックスの値は含めるが、終了インデックスの値は含めないことに要注意。

sample_str = "abcdefghij"

# インデックス0番目からから4番目の要素まで
print(sample_str[:5])

# インデックス5番目の要素から末尾まで
print(sample_str[5:])

# インデックス2番目からから6番目の要素まで
print(sample_str[2:7])

# インデックス2番目からから6番目の要素まで1つ飛ばし
print(sample_str[2:7:2])

# 末尾から先頭まで(逆順)
print(sample_str[::-1])
実行結果
'abcde'
'fghij'
'cdefg'
'ceg'
'jihgfedcba'

置換(replace)

replaceメソッドで文字列を置換。

sample_str = "It is fabulous."
sample_str_replaced = sample_str.replace("fabulous", "ridiculous")
print(sample_str_replaced)
実行結果
It is ridiculous.

大文字小文字変換(upper, lower)

upperメソッドで文字列を全て大文字に変換。
lowerメソッドで文字列を全て小文字に変換。
capitalizeメソッドで先頭を大文字に、他を小文字に変換。

sample_str = "aBc"
sample_str_upper = sample_str.upper()
print(sample_str_upper)
sample_str_lower = sample_str.lower()
print(sample_str_lower)
sample_str_capitalize = sample_str.capitalize()
print(sample_str_capitalize)
実行結果
ABC
abc
Abc

開始文字終了文字判定(startswith, endswith)

startswithメソッドで指定した文字列で開始しているかを判定。
endswithメソッドで指定した文字列で終了しているかを判定。

sample_str = "Python is a excellent language."
print(sample_str.startswith("Python"))
print(sample_str.startswith("Java"))
print(sample_str.endswith("language."))
実行結果
True
False
True

文字列内に指定文字が含まれているか判定(in)

inで指定した文字が文字列の中に含まれているかを判定。

sample_str = "abcde"
print("a" in sample_str)
print("g" in sample_str)
実行結果
True
False

桁揃え(rjust, zfill)

rjustメソッドで指定桁数まで指定文字を左詰めに埋める。
zfillメソッドで指定桁数まで0を左詰めに埋める。

sample_str = "1234"

# 10桁で-を左詰め
sample_str_rjust = sample_str.rjust(10, "-")
print(sample_str_rjust)

# 10桁で0を左詰め
sample_str_zfill = sample_str.zfill(10)
print(sample_str_zfill)
実行結果
------1234
0000001234

エスケープシーケンス

バックスラッシュ(\)でエスケープシーケンス。

シングルクォート

print("12\'34")
実行結果
12'34

ダブルクォート

print("12\"34")
実行結果
12"34

バックスラッシュ

print("12\\34")
実行結果
12\34

改行

print("12\n34")
実行結果
12
34

タブ

print("12\t34")
実行結果
12  34

数値

足し算

print(10 + 10)
実行結果
20

引き算

print(10 - 10)
実行結果
0

掛け算

print(10 * 10)
実行結果
100

割り算

print(10 / 10)
実行結果
1.0

割り算の商

print(10 // 3)
実行結果
3

割り算の余り

print(10 % 3)
実行結果
1

指数

3乗の場合
print(10 ** 3)
実行結果
1000

インクリメント(デクリメント)

Pythonでは他言語であるような++や--のインクリメント(デクリメント)演算子が存在しない。
とはいえ、"a = a + 1"とか書くのは少し格好悪いと思う場合は下記のように書いてみる。

a += 1

論理値

sample_boolean = True
print(sample_boolean)

sample_boolean = False
print(sample_boolean)
実行結果
True
False

型変換

キャストで型変換。

sample_str = "10"
sample_int = 10

# 10 + 10 = 20の計算
print(int(sample_str) + sample_int)

# "10"と"10"の文字連結
print(sample_str + str(sample_int))
実行結果
20
1010

型確認(isinstance)

isinstance関数で型やデータ構造の判定。

sample_str = "hello"
sample_int = 1
sample_list = [1, 2]

if isinstance(sample_str, str):
    print("This is string.")

if isinstance(sample_int, int):
    print("This is int.")

if isinstance(sample_list, list):
    print("This is list.")
実行結果
This is string.
This is int.
This is list.

データ構造

リスト

他言語でもおなじみのリスト。
タプルとの違いは、リストは要素を追加・削除できるところ。(ミュータブル)

宣言

[]で宣言

[]でリストを宣言。

sample_list = ["a", "b", "c"]
print(sample_list)
実行結果
['a', 'b', 'c']

list関数で宣言

list関数で空リストを宣言。

sample_list = list()
print(sample_list)
実行結果
[]

内包表記で宣言

内包表記と呼ばれる方法でも宣言できる。
大括弧([])の一番左がリストの要素として追加される。
一見分かりづらいが、慣れればコードを少ない量でかけるメリットがある。

sample_list = [i*2 for i in range(10)]
print(sample_list)
実行結果
[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]

要素の取得

[]でインデックス指定して要素を取得。

sample_list = ["a", "b", "c"]
print(sample_list[0])
実行結果
a

要素の更新

[]でインデックス指定した要素を更新。

sample_list = ["a", "b", "c"]
sample_list[2] = "d"
print(sample_list)
実行結果
['a', 'b', 'd']

要素の追加(append)

appendメソッドで要素をリストの末尾に追加。

sample_list = ["a", "b", "c"]
sample_list.append("d")
sample_list.append("e")
print(sample_list)
実行結果
['a', 'b', 'c', 'd', 'e']

要素の挿入(insert)

insertメソッドで指定したインデックスに要素を挿入。

sample_list = ["a", "b", "c"]
sample_list.insert(2, "B")
sample_list.insert(4, "C")
print(sample_list)
実行結果
['a', 'b', 'B', 'c', 'C']

要素の削除

要素指定して削除(remove)

removeメソッドで要素を指定して削除。

sample_list = ["a", "b", "c"]
sample_list.remove("a")
print(sample_list)
実行結果
['b', 'c']

インデックス指定して削除(del)

delでインデックスを指定して削除。

sample_list = ["a", "b", "c"]
del sample_list[0]
print(sample_list)
実行結果
['b', 'c']

ポップ(pop)

popメソッドで指定したインデックスの要素をポップ。
ポップなので、取り出した後はリストから無くなる。

sample_list = ["a", "b", "c"]
print(sample_list.pop(1))
print(sample_list)
実行結果
b
['a', 'c']

要素の存在確認(in)

inで要素がリストに含まれているかを確認。

sample_list = ["a", "b", "c"]
print("c" in sample_list)
print("Z" in sample_list)
実行結果
True
False

要素のインデックス取得(index)

indexメソッドで指定した要素のインデックスを取得。
ただし、先頭から見つけた最初のインデックスしか返さない。

sample_list = ["a", "b", "c", "c"]
print(sample_list.index("c"))
実行結果
2

要素のカウント(count)

countメソッドで指定した要素がリスト中にいくつあるかを取得。

sample_list = ["a", "b", "c", "b"]
print(sample_list.count("b"))
実行結果
2

要素の連結(join)

joinメソッドで指定した文字列でリスト内の要素を一つの文字列に連結。

sample_list = ["I", "want", "money"]
print(" ".join(sample_list))
実行結果
I want money

リストの長さ(len)

len関数でリストの長さ(要素数)を取得。

sample_list = ["a", "b", "c"]
print(len(sample_list))
実行結果
3

リスト内の最小値と最大値(min, max)

min関数でリスト内の最小値を取得。
max関数でリスト内の最大値を取得。

sample_list = [10, 2, 85, 70]
print(min(sample_list))
print(max(sample_list))
実行結果
2
85

また、リスト内の要素が文字列の場合は原則アルファベット順で大小比較する。

sample_list = ["H", "E", "A", "F"]
print(min(sample_list))
print(max(sample_list))
実行結果
A
H

合計値(sum)

sum関数でリスト内の合計値を取得。

sample_list = [10, 2, 85, 70]
print(sum(sample_list))
実行結果
167

ソート(昇順)

リスト自体を昇順ソート(sort)

sortメソッドでリスト自体をソート。

sample_list = [100, 10, 50, 60]
sample_list.sort()
print(sample_list)
実行結果
[10, 50, 60, 100]

昇順ソート結果のみを返す(sorted)

sorted関数でリストのソート結果を取得。
ただし、リスト自体はソートしない。

sample_list = [100, 10, 50, 60]
sample_list_sorted = sorted(sample_list)
print(sample_list_sorted)
print(sample_list)
実行結果
[10, 50, 60, 100]
[100, 10, 50, 60]

ソート(逆順)

リスト自体を逆順ソート(reverse)

reverseメソッドでリストを逆順にソート。
降順でないことに要注意。

sample_list = [20, 10, 30]
sample_list.reverse()
print(sample_list)
実行結果
[30, 10, 20]

逆順ソート結果のみを返す(reversed, スライス)

reversed関数でリストの逆順ソート結果を取得。
ただし、リスト自体はソートしない。
降順でないことに要注意。

sample_list = [20, 10, 30]
sample_list_reversed = reversed(sample_list)
for value in sample_list_reversed:
    print(value)
print(sample_list)
実行結果
30
10
20
[20, 10, 30]

また、スライスでもリストの逆順ソート結果を取得できる。

test_list = [100, 10, 50, 60]
test_list_reversed = test_list[::-1]
for value in test_list_reversed:
    print(value)
print("-----------")
for value in test_list:
    print(value)
実行結果
60
50
10
100
-----------
100
10
50
60

ソート(降順)

リスト自体を降順ソート(sort/reverse)

sortメソッドのreverseキーワード引数にTrueを指定することにより、降順にソートする。

sample_list = [20, 10, 30]
sample_list.sort(reverse=True)
print(sample_list)
実行結果
[30, 20, 10]

また、一度sort関数で昇順にソートしてからreverse関数で逆順にソートすることにより降順にソートすることもできる。(少しめんどくさい。)

sample_list = [20, 10, 30]
sample_list.sort()
sample_list.reverse()
print(sample_list)
実行結果
[30, 20, 10]

逆順ソート結果のみを返す(sorted)

sorted関数のreverseキーワード引数をTrueにすると降順ソート結果を取得できる。

sample_list = [20, 10, 30]
sample_list_down = sorted(sample_list, reverse=True)
print(sample_list_down)
実行結果
[30, 20, 10]

リスト同士の結合(extend, +)

extendメソッドで2つのリストを結合。

sample_list_1 = ["a", "b", "c"]
sample_list_2 = ["d", "e"]
sample_list_1.extend(sample_list_2)
print(sample_list_1)
実行結果
['a', 'b', 'c', 'd', 'e']

また、+演算子による結合もできる。

sample_list_1 = ["a", "b", "c"]
sample_list_2 = ["d", "e"]
sample_list_1 += sample_list_2
print(sample_list_1)
実行結果
['a', 'b', 'c', 'd', 'e']

リストのリスト

リストの中にリストをいれて入れ子構造にできる。

sample_list_1 = ["a", "b", "c"]
sample_list_2 = ["d", "e"]
sample_list = [sample_list_1, sample_list_2]
print(sample_list)
# 2つ目のリストの1つ目の要素を取得
print(sample_list[1][0])
実行結果
[['a', 'b', 'c'], ['d', 'e']]
d

タプル

リストに似ているが、タプルは要素の追加・削除ができない。
したがって、用意されているメソッドも少なく、実際にはタプルよりもリストの方が使う機会が多いが、関数やメソッドの可変長引数*argsでタプルを利用する場面はある。
タプルを使うメリットとしては、消費メモリが少ない点と一度定義されたものを誤って書き換えられる心配がない(イミュータブル)という点があげられる。
ただし、タプル内にあるミュータブルオブジェクトの内容は変更できるので注意が必要である。

宣言

()で宣言

()で空タプルを宣言。
要素が一つのタプルは要素の後ろにカンマを書く。
複数要素のタプルはカンマで区切って書く。
カンマがあれば括弧は省略可能。ただし、引数のカンマなど区別がつかないとこは括弧が必要。

sample_tuple = ()
print(sample_tuple)
sample_tuple = ("only one",)
print(sample_tuple)
sample_tuple = "English", "Math", "Biology"
print(sample_tuple)
実行結果
()
('only one',)
('English', 'Math', 'Biology')

tuple関数で宣言

引数を省略して空タプルを生成。
リストを引数にしてタプルに変換。

sample_tuple = tuple()
print(sample_tuple)
sample_list = [1, 2, 3]
sample_tuple = tuple(sample_list)
print(sample_tuple)
実行結果
()
(1, 2, 3)

内包表記で宣言

tuple関数を使って内包表記で生成

sample_tuple = tuple(i * 2 for i in range(10))
実行結果
(0, 2, 4, 6, 8, 10, 12, 14, 16, 18)

セット

他言語でもおなじみのセット。
リストと同じように要素を追加・削除することはできるが、重複した要素は持たない。
順番は必ずしも維持しない。

宣言

{}で宣言

{}でセットを宣言。

sample_set = {"a", "b", "c"}
print(sample_set)
実行結果
{'b', 'a', 'c'}

set関数で宣言

set関数で空セットを宣言。

sample_set = set()
print(sample_set)
実行結果
set()

内包表記で宣言

内包表記が用意されている。
中括弧({})の一番左がセットの要素として追加される。

test_set = {i * 2 for i in range(10)}
print(test_set)
実行結果
{0, 2, 4, 6, 8, 10, 12, 14, 16, 18}

要素の追加(add)

addメソッドで要素を追加。
リストのように順序が保持されていないので、末尾に追加というわけではない。

sample_set = {"a", "b", "c"}
sample_set.add("d")
print(sample_set)
実行結果
{'b', 'c', 'd', 'a'}

要素の削除(remove)

removeメソッドで指定した要素を削除。

sample_set = {"a", "b", "c"}
sample_set.remove("a")
print(sample_set)
実行結果
{'b', 'c'}

要素の存在確認(in)

inで要素がセットに含まれているかを確認。

sample_set = {"a", "b", "c"}
print("a" in sample_set)
実行結果
True

辞書

keyとvalueを持つデータ構造。
Javaでいうマップ。

宣言

{}で宣言

{}で辞書を宣言。

sample_dict = {"Google": "Android", "Apple": "iOS"}
print(sample_dict)
実行結果
{'Google': 'Android', 'Apple': 'iOS'}

dict関数で宣言

dict関数で空辞書を宣言。

sample_dict = dict()
print(sample_dict)
実行結果
{}

内包表記で宣言

内包表記で宣言。
中括弧({})の一番左のkey:valueがで辞書の要素として追加される。

test_dict = {str(i): i * 2 for i in range(10)}
print(test_dict)
実行結果
{'0': 0, '1': 2, '2': 4, '3': 6, '4': 8, '5': 10, '6': 12, '7': 14, '8': 16, '9': 18}

要素全て取得(items)

itemsメソッドで辞書内の全ての要素をkey:value形式で取得。

sample_dict = {"Google": "Android", "Apple": "iOS"}
print(sample_dict.items())
実行結果
dict_items([('Google', 'Android'), ('Apple', 'iOS')])

key全て取得(keys)

keysメソッドで辞書内の全てのkeyを取得。

sample_dict = {"Google": "Android", "Apple": "iOS"}
print(sample_dict.keys())
実行結果
dict_keys(['Google', 'Apple'])

value全て取得(values)

valuesメソッドで辞書内の全てのvalueを取得。

sample_dict = {"Google": "Android", "Apple": "iOS"}
print(sample_dict.values())
実行結果
dict_values(['Android', 'iOS'])

key指定でvalueを取得

[]あるいはgetメソッドでkeyを指定してvalueを取得。

sample_dict = {"Google": "Android", "Apple": "iOS"}
print(sample_dict["Google"])
print(sample_dict.get("Google"))
実行結果
Android
Android

要素追加

[]でkeyを指定して、valueを代入することで要素を追加。

sample_dict = {"Google": "Android", "Apple": "iOS"}
sample_dict["Microsoft"] = "Windows"
print(sample_dict)
実行結果
{'Google': 'Android', 'Apple': 'iOS', 'Microsoft': 'Windows'}

要素削除(del)

delを使って指定したkeyの要素を削除。

sample_dict = {"Google": "Android", "Apple": "iOS"}
del sample_dict["Google"]
print(sample_dict)
実行結果
{'Apple': 'iOS'}

要素全て削除(clear)

clearメソッドで辞書内の要素を全て削除。

sample_dict = {"Google": "Android", "Apple": "iOS"}
sample_dict.clear()
print(sample_dict)
実行結果
{}

keyが含まれているかの確認(in)

inで指定した値が辞書のkeyとして登録されているかを確認。

sample_dict = {"Google": "Android", "Apple": "iOS"}
print("Google" in sample_dict)
実行結果
True

辞書同士の結合(update)

updateメソッドで2つの辞書を結合。

sample_dict_1 = {"Google": "Android"}
sample_dict_2 = {"Apple": "iOS"}
sample_dict_1.update(sample_dict_2)
print(sample_dict_1)
実行結果
{'Google': 'Android', 'Apple': 'iOS'}

mutableとimmutable

PythonにはJavaにあるようなプリミティブ型が存在せず、全てが参照型である。(Pythonでは全てがオブジェクト)
それゆえに、渡しは全て「参照の値渡し」となる。その際に、型がimmutable(変更不可能)かmutable(変更可能)なのかで渡された値が変更されると元の値も変更されるのかが関係してくる。mutable(変更可能)とは、「オブジェクトIDを変えずに、要素やメンバ変数を追加・変更・削除などができる」という意味。

immutable

  • int
  • float
  • str
  • tupleなど

渡された値が変更されても元の値は変更されない。
参照の値渡しのため、代入された直後はオブジェクトIDは同一。ただし、値が書き換えられるとオブジェクトIDは変わる。

a = 1
b = a
print(id(a))
print(id(b))
b = 2
print(a)
print(b)
print(id(a))
print(id(b))
実行結果
4555834704
4555834704
1
2
4555834704
4555834736

mutable

  • list
  • dict
  • setなど

渡された値が変更されると元の値も変更される。
参照の値渡しのため、オブジェクトIDは同一。

a = [1, 2]
b = a
b.append(3)
print(a)
print(b)
print(id(a))
print(id(b))
実行結果
[1, 2, 3]
[1, 2, 3]
4527327112
4527327112

浅いコピー

浅いコピー(copyモジュールのcopy関数)で、mutableの型でも渡された値が変更されても元の値を変更しないようにできる。
新規に別のコピーを渡すため、オブジェクトIDが異なる。

import copy
a = [1, 2]
b = copy.copy(a)
b.append(3)
print(a)
print(b)
print(id(a))
print(id(b))
実行結果
[1, 2]
[1, 2, 3]
4414583368
4414583304

深いコピー

例えばlistで多次元を扱う場合、浅いコピーだと渡された値が変更されると元の値も変更される。
浅いコピーでは多次元内の各要素のオブジェクトIDが同一であることが原因だとわかる。

import copy
a = [[1, 2], [3, 4]]
b = copy.copy(a)
b[1].append(5)
print(a)
print(b)
print(id(a))
print(id(b))
print(id(a[0]))
print(id(b[0]))
print(id(a[1]))
print(id(b[1]))
実行結果
[[1, 2], [3, 4, 5]]
[[1, 2], [3, 4, 5]]
4474948360
4474947336
4474946120
4474946120
4474946056
4474946056

深いコピー(copyモジュールのdeepcopy関数)を使うと、各要素のオブジェクトIDも異なり、渡された値が変更されても元の値を変更しないようにできる。

import copy
a = [[1, 2], [3, 4]]
b = copy.deepcopy(a)
b[1].append(5)
print(a)
print(b)
print(id(a))
print(id(b))
print(id(a[0]))
print(id(b[0]))
print(id(a[1]))
print(id(b[1]))
実行結果
[[1, 2], [3, 4]]
[[1, 2], [3, 4, 5]]
4515130120
4515129096
4515127880
4515130504
4515127816
4515130568

制御構文

if文

sample_var = 1
if sample_var == 1:
    print("sample_var is 1")
else:
    print("sample_var is not 1")
実行結果
sample_var is 1

複数条件の場合はandかorで条件を結合する。
他言語であるような&&や||は使えない。

sample_var_1 = 1
sample_var_2 = 3
if sample_var_1 == 1 and sample_var_2 == 2:
    print("sample_var_1 is 1 and sample_var_2 is 2")
if sample_var_1 == 1 or sample_var_2 == 2:
    print("sample_var_1 is 1 or sample_var_2 is 2")
実行結果
sample_var_1 is 1 or sample_var_2 is 2

for文

基本的なfor文

Javaで言う拡張for文の形式。

sample_list = ["a", "b", "c"]
for value in sample_list:
    print(value)
実行結果
a
b
c

ループ数明示(range)

ループの数を指定してfor文を回したい場合はrange関数を利用する。
引数を1つだけ指定する場合は0から始まり引数の値-1まで繰り返される。

for i in range(3):
    print(i)
実行結果
0
1
2

引数を2つ指定することにより、開始インデックスも指定できる。

for i in range(1, 4):
    print(i)
実行結果
1
2
3

インデックス付きfor文(enumerate)

リストをfor文で回す際にインデックスも同時に取得したい場合はenumerate関数を利用する。

test_list = [100, 10, 50, 60]
for index, value in enumerate(test_list):
    print(index, value)
実行結果
0 100
1 10
2 50
3 60

インデックスのデフォルトは0から開始されるが、第二引数で開始インデックスを指定することもできる。

for index, value in enumerate(test_list, 1):
    print(index, value)
実行結果
1 100
2 10
3 50
4 60

複数リストを同時に回す(zip)

複数リストを1つのforループで同時に回したい場合は、zip関数を利用する。

subject_list = ["math", "english", "music"]
score_list = ["90", "85", "20"]
for subject, score in zip(subject_list, score_list):
    print("subject: " + subject)
    print("score: " + score)
    print("---------------")
実行結果
subject: math
score: 90
---------------
subject: english
score: 85
---------------
subject: music
score: 20
---------------

while文

value = 0
while value < 3:
    value += 1
    print(value)
実行結果
1
2
3

break文

value = 0
while True:
    value += 1
    print(value)
    if value == 3:
        break
実行結果
1
2
3

例外処理

まずtryブロックのコードが実行され、そこでエラーが起きると例外処理としてexceptブロックのコードが実行される。finallyブロックは例外の有無にかかわらず実行される。

sample_list = [1, 2, 3]
try:
    print(sample_list[5])
except:
    print("[EXCEPT] list index error")
finally:
    print("[FINALLY] process done")
実行結果
[EXCEPT] list index error
[FINALLY] process done

また、exceptにはあらかじめよくある例外ハンドラが定義されており、それらを指定して書くのが一般的である。

sample_list = [1, 2, 3]
try:
    print(sample_list[5])
except IndexError:
    print("[EXCEPT] list index error")
except Exception:
    print("[EXCEPT] something other error")
finally:
    print("[FINALLY] process done")
実行結果
[EXCEPT] list index error
[FINALLY] process done
sample_list = [1, 2, 3]
try:
    print(sample_list["a"])
except IndexError:
    print("[EXCEPT] list index error")
except Exception:
    print("[EXCEPT] something other error")
finally:
    print("[FINALLY] process done")
実行結果
[EXCEPT] something other error
[FINALLY] process done

関数

処理を関数にまとめて再利用することで効率的なプログラミングができるようになる。

宣言と実行

defで関数の宣言。

def sample_function():
    print("This is a sample function.")

sample_function()
実行結果
This is a sample function.

docstring

必須ではないが、ダブルクォーテーション(あるいはシングルクォーテーショーン)3つで囲まれたdocstringで関数の概要や使い方などのコメントを記載。(この記事では分量削減のため以降省略します。)

def sample_function():
    """
    これはdocstringです。
    ここには関数の概要や使い方や返り値などを記載します。
    :return:
    """
    pass

関数アノテーション

関数の引数や返り値にアノテーション(注釈)となる式を記述する。
docstringとの使い分けとしては、型やデフォルト引数値などは関数アノテーションで記載して、詳細はdocstringに記載するのが一般的。
引数名のあとの:以降に引数に関するアノテーションを記載する。
括弧と末尾のコロン:の間に返り値に関するアノテーションを記載する。
アノテーションの内容が直接関数の動作に影響することはなく、必須でもないが、型情報が明示されていることにより可読性が向上する場合がある。さらに、Pycharmなどを使えば実装時に型のヒントをくれる。

def add_func(a: "int", b: "int default value" = 1) -> "added_value":
    return a + b

print(add_func.__annotations__)
実行結果
{'a': 'int', 'b': 'int default value', 'return': 'added_value'}

引数

引数には位置引数、キーワード引数、デフォルト引数、可変長引数 の4種類がある。

位置引数

いわゆる普通の引数。
関数やメソッドに渡す実引数の位置(順番)に注意する必要がある。

def sample_function(drink, food):
    print("Your drink is " + drink)
    print("Your food is " + food)


sample_function("water", "curry")
実行結果
Your drink is water
Your food is curry

キーワード引数

関数やメソッドの仮引数名を指定して実引数を渡す。
位置引数のように順番を考慮する必要はない。

def sample_function(drink, food):
    print("Your drink is " + drink)
    print("Your food is " + food)


sample_function(food="curry", drink="water")
実行結果
Your drink is water
Your food is curry

デフォルト引数

仮引数のデフォルト値を指定する。
関数・メソッド呼出し時に実引数を渡さなかった場合はデフォルト引数値が使用される。
位置引数とデフォルト引数の同時定義は可能。ただし、位置引数を先に定義する必要がある。
デフォルト値に指定したオブジェクトは再利用されるため、ミュータブルオブジェクトをデフォルト値にしてはいけない。

def sample_function(drink, food="curry"):
    print("Your drink is " + drink)
    print("Your food is " + food)

sample_function("water")
実行結果
Your drink is water
Your food is curry

可変長引数

*args

*argsで可変長な位置引数をタプルとして受け取る。
仮引数名は"args"である必要はないが、慣習的に"args"を使う。
*はC言語などのポインタとは一切関係ないので安心(?)しよう。そんなに難しい話ではない。

def sample_function(sport1, sport2, *args):
    print("Your first favorite sport : " + sport1)
    print("Your second favorite sport : " + sport2)
    print("others : ")
    print(args)


sample_function("basketball", "soccer", "baseball", "swimming")
実行結果
Your first favorite sport : basketball
Your second favorite sport : soccer
others : 
('baseball', 'swimming')

**kwargs

**kwargsで可変長なキーワード引数を辞書として受け取る。
args同様、仮引数名は"kwargs"である必要はないが、慣習的に"kwargs"を使う。

def sample_function(sport1, sport2, **kwargs):
    print("Your first favorite sport : " + sport1)
    print("Your second favorite sport : " + sport2)
    print("others : ")
    print(kwargs)


sample_function("basketball", "soccer", sport3="baseball", sport4="swimming")
実行結果
Your first favorite sport : basketball
Your second favorite sport : soccer
others : 
{'sport3': 'baseball', 'sport4': 'swimming'}

関数内関数

関数内で別の関数を定義する。
ある関数内でのみ複数回行われる処理を関数内関数に記述しておくことでコード量を削減できる。
inner関数内でouter関数の実引数を参照する際には、inner関数の引数からouter関数の引数を辿っていく。

from decimal import Decimal

def convert_seconds(sec):
    h = sec // 60 // 60
    m = sec // 60 % 60
    s = sec % 60
    def to_string(value, unit):
        return ('%g' % value) + " " + unit + ('s' if value > 1 else '')
    return ', '.join((to_string(h, 'hour'),
                      to_string(m, 'minute'),
                      to_string(s, 'second')))

print(convert_seconds(3600))
print(convert_seconds(7325))
print(convert_seconds(7261.7))
実行結果
1 hour, 0 minute, 0 second
2 hours, 2 minutes, 5 seconds
2 hours, 1 minute, 1.7 seconds

クロージャ(高階関数)

処理は同じだがその内部で使用するパラメータが異なる動的に作成された関数。
関数内関数をクロージャに応用できる。inner関数がouter関数のローカル変数を参照できる。
outer関数内の変数を変更する場合にはnonlocalを指定する。

def outer_func(greet):
    count = 0
    def inner_func(name):
        nonlocal count
        count += 1
        print(count, ":", greet, name)
    return inner_func

greet = outer_func("Hello,")
greet("world!")
greet("there.")
greet("Tom.")
実行結果
1 : Hello, world!
2 : Hello, there.
3 : Hello, Tom.

無名関数

ラムダ関数とも呼ばれる。
短い関数はlambda式により一行でスマートに定義できる場合がある。
ソースコードが簡潔になったり、小さい関数の名前を覚えなくても済むメリットがある。
"lambda"の後に引数を指定し、":"(コロン)の後に処理を記述する。

py
sample_lambda = lambda num1, num2: num1 + num2
print(sample_lambda(1, 1))
print(type(sample_lambda))
実行結果
2
<class 'function'>

なお、PEP8:Pythonコーディングスタイルには、「ラムダ式を直接識別子に結びつける代入文を書くのではなくて、常に def 文を使いましょう。」と書いてあり、関数の引数など、その場限りの使い方を推奨している。

data = "AbCaBc"
print(sorted(data))                           # 大文字が前半、小文字が後
print(sorted(data, key=lambda c: c.lower()))  # 大文字小文字無視
print(type(lambda c: c.lower()))
実行結果
['A', 'B', 'C', 'a', 'b', 'c']
['A', 'a', 'b', 'B', 'C', 'c']
<class 'function'>

デコレータ

引数で受け取ったデコレートされる関数を利用した処理をするinner関数を返す関数。
Pythonでは関数も第一級オブジェクト(ファーストクラスオブジェクト)である。
これにより、デコレートされる関数の実装を変更することなく処理を変更することができる。
以下の特徴を持つ。

  • デコレータは関数内関数
  • outer関数の引数は関数(デコレートされる関数)
  • inner関数の引数はargsと*kwargsの2つ(なくてもできるが基本これ)

下の例では、デコレータ(double_func)は引数で受け取った関数の結果を2倍にして出力する。
引数として渡されるデコレートされる側の関数は2つの引数を足し算する。

def double_func(func):
    def new_func(*args, **kwargs):
        result = func(*args, **kwargs) * 2
        print(result)
        return result
    return new_func


def add_num(a, b):
    return a + b


sample_add = double_func(add_num)
sample_add(1, 3)

通常、デコレータを利用する場合は、上記のような記述方法でなく、@を使ったシンタックスシュガーを利用するのが一般的。

def double_func(func):
    def new_func(*args, **kwargs):
        result = func(*args, **kwargs) * 2
        print(result)
        return result
    return new_func


@double_func
def add_num(a, b):
    return a + b


add_num(1, 3)
実行結果
8

さらに、デコレータは複数つけることも可能である。
ここでは3倍にするtriple_funcを追加実装した。
複数の場合は下のシンタックスシュガーが先に実行される。

def double_func(func):
    def new_func(*args, **kwargs):
        result = func(*args, **kwargs) * 2
        print(result)
        return result
    return new_func


def triple_func(func):
    def new_func(*args, **kwargs):
        result = func(*args, **kwargs) * 3
        print(result)
        return result
    return new_func


@triple_func
@double_func
def add_num(a, b):
    return a + b


add_num(1, 3)
実行結果
8
24

シンタックスシュガーの位置を逆にすると、1+3の結果が先に3倍されていることがわかる。

@double_func
@triple_func
def add_num(a, b):
    return a + b
実行結果
12
24

メソッド

メソッドにはインスタンスメソッドとクラスメソッドとスタティックメソッドと抽象メソッドの4種類がある。

インスタンスメソッド

いわゆる通常のメソッド。
メソッド定義時にデコレータは不要。
第一引数にはクラスのインスタンス自身を表すselfを指定。文字列としてselfでなくても構わないが慣例的にselfを使う。
クラス変数にもインスタンス変数にもアクセス可。

クラスメソッド

クラスをインスタンス化しなくても呼び出すことができるメソッド。
メソッド宣言時に @classmethod デコレータを付ける。
第一引数にはクラス自身を表すclsを指定。文字列としてclsでなくても構わないが慣例的にclsを使う。
クラス変数にはアクセス可だが、インスタンス変数にはアクセス不可。

スタティックメソッド

クラスをインスタンス化しなくても呼び出すことができるメソッド。
メソッド宣言時に @staticmethod デコレータを付ける。
第一引数にはインスタンスメソッドやクラスメソッドのようにselfやclsなどの変数を必要としない。
クラス変数にもインスタンス変数にもアクセス不可。
一応メソッドの分類ではあるが、感覚的には関数とあまり変わらない。ただし、関数と違って、オーバーライドができる。

抽象メソッド

宣言時に処理を定義せず(pass)に、抽象クラスを継承したクラスでオーバーライドにより処理を定義するメソッド。
メソッド宣言時に @abstractmethod デコレータを付ける。
第一引数にはクラスのインスタンス自身を表すselfを指定。

関数とメソッド(インスタンスメソッド)の違い

defでクラス内で宣言されている時点では関数。
クラスをインスタンス化したオブジェクトが関数を呼び出そうとした時点で、クラス内に定義されていた関数はメソッドに変化する。
クラスの外で定義された関数は当然ながらメソッドにはなりえない。

クラス

定義

classでクラスを定義する。
Pythonの命名規則は基本的にスネーク型だが、クラス名はキャメル型で定義。

# クラス定義
class SampleClass:

    # 初期化メソッド定義
    def __init__(self, name, age):
        self.name = name
        self.age = age

    # メソッド定義
    def sample_print(self):
        print("{0} is {1}.".format(self.name, self.age))


# インスタンス生成
sample_class = SampleClass("james", 28)

# メソッド実行
sample_class.sample_print()
実行結果
james is 28.

__init__メソッド

__init__メソッドはインスタンスが作成されるときに自動で実行される特殊なメソッド。
いわゆるコンストラクタのようなもの。初期化処理などを定義しておく。

self

selfはインスタンス自身を表す。
インスタンスメソッドの第一仮引数に必ず記載する。文字列としてselfでなくても構わないが慣例的にselfを使う。

ゲッターとセッターとデリーター

Pythonでもゲッターとセッターとデリーターがある。
自分でそれっぽく定義する方法とデコレータを使う方法をまとめる。

自分でそれっぽく書く方法
class PropertyTest:

    def __init__(self, name):
        self._name = name

    def get_name(self):
        return self._name

    def set_name(self, name):
        self._name = name

    def del_name(self):
        del self._name

# インスタンス生成
pt = PropertyTest("james")

# getter実行
print(pt.get_name())

# setter実行
pt.set_name("tom")

# getter実行
print(pt.get_name())

# deleter実行
pt.del_name()
実行結果
james
tom
デコレータを使う方法
class PropertyTest:

    def __init__(self, name):
        self._name = name

    @property
    def name(self):
        return self._name

    @name.setter
    def name(self, name):
        self._name = name

    @name.deleter
    def name(self):
        del self._name


# initial
pt = PropertyTest("james")

# getter実行
print(pt.name)

# setter実行
pt.name = "tom"

# getter実行
print(pt.name)

# deleter実行
del pt.name
実行結果
james
tom

継承

サブクラス定義

サブクラス(スーパークラス)の形式でサブクラスを定義。
サブクラスはスーパークラスのメソッドを実行することが可能。さらに、サブクラス自身に独自のメソッドを定義して実行することも可能。

class Car:

    def __init__(self, number):
        self.number = number

    def print_number(self):
        print("Car number: " + self.number)


class SportsCar(Car):

    def print_sound(self):
        print("Sound: Boooooon")


sport = SportsCar("1234")
sport.print_number()
sport.print_sound()
実行結果
Car number: 1234
Sound: Boooooon

super関数

サブクラス内でスーパークラスのメソッドを呼び出す際はsuper関数を利用する。

class Car:

    def __init__(self, number):
        self.number = number

    def print_number(self):
        print("Car number: " + self.number)


class SportsCar(Car):

    def __init__(self, number, sound):
        # スーパークラスの__init__メソッドの呼び出し
        super().__init__(number)
        self.sound = sound

    def print_sound(self):
        print("Sound: " + self.sound)


sport = SportsCar("1234", "Boooooon")
sport.print_number()
sport.print_sound()
実行結果
Car number: 1234
Sound: Boooooon

objectクラス

objectクラスは全てのクラスのスーパークラスである。
Python2系ではクラスを宣言するときはobjectクラスを明示的に継承する必要があったが、3系では明示的に継承する必要がなくなった。

オーバーライド

オーバーライドとはスーパークラスのメソッドをサブクラスで上書き(オーバーライド)することである。

class Car:

    def __init__(self, number):
        self.number = number

    def print_number(self):
        print("Car number: " + self.number)

    def print_speed(self):
        print("Speed: 60km/h")


class SportsCar(Car):

    def print_speed(self):
        print("Speed: 100km/h")


sport = SportsCar("1234")
sport.print_number()
sport.print_speed()
実行結果
Car number: 1234
Speed: 100km/h

オーバーロード

オーバーロードとは同一メソッド名で引数の型や数が異なるものを定義することである。
Pythonにはオーバーロードはない。なぜならばまず、動的言語のため仮引数の型を明示的に宣言しないからである。
さらに、仮引数の数が違うものを定義した場合は、クラス内の下に定義されたものに上書きされてしまう。つまり、他言語であるような実引数を渡した際の引数の数の違いによる呼び出し先の変更を行ってくれない。下の例は仮引数2つのメソッドを実引数1つでメソッドを呼び出すかっこうとなりエラーとなる。

class SampleClass:

    def sample_method(self, var1):
        print(var1)

    def sample_method(self, var1, var2):
        print(var1)
        print(var2)


sample = SampleClass()
sample.sample_method(1)
実行結果
TypeError: sample_method() missing 1 required positional argument: 'var2'

実引数2つで呼び出せばエラーにならない。

class SampleClass:

    def sample_method(self, var1):
        print(var1)

    def sample_method(self, var1, var2):
        print(var1)
        print(var2)


sample = SampleClass()
sample.sample_method(1, 2)
実行結果
1
2

多重継承

Pythonではクラスの多重継承ができる。

class Add:
    """
    足し算をする
    """
    def __init__(self, val1, val2):
        self.val1 = val1
        self.val2 = val2

    def add(self):
        return self.val1 + self.val2


class Sub:
    """
    引き算をする
    """
    def __init__(self, val1, val2):
        self.val1 = val1
        self.val2 = val2

    def sub(self):
        return self.val1 - self.val2


class Calc(Add, Sub):
    def __init__(self, val1, val2):
        self.val1 = val1
        self.val2 = val2


calc = Calc(5, 3)
print(calc.add())
print(calc.sub())
実行結果
8
2

抽象クラス

abc(abstract base class)モジュールを利用する。
抽象クラスとしたいクラスのmetaclassキーワード引数にabcモジュールのABCMetaクラスを渡す。
抽象メソッドとしたいメソッドにはabstractmethodデコレーターをつける。
抽象クラスを継承したクラスに抽象メソッドのオーバーライドを強制できる。
もちろん、抽象クラスには通常のメソッドも記述できる。

from abc import ABCMeta, abstractmethod


class Animal(metaclass=ABCMeta):
    @abstractmethod
    def cry(self):
        pass

    def normal_method(self):
        print("This is not an abstract method.")


class Cat(Animal):
    def cry(self):
        print("Mya")


class Dog(Animal):
    def cry(self):
        print("Bow")


if __name__ == "__main__":
    c = Cat()
    c.cry()
    d = Dog()
    d.cry()
    d.normal_method()
実行結果
Mya
Bow
This is not an abstract method.

インターフェース

Pythonにはインターフェースは用意されていない。
ただし、abcモジュールとクラスを多重継承できる特徴を使えばそれっぽいことは実現できる。

オプジェクト指向プログラミング(OOP)三大要素

既にここまでに説明した要素でOOPの三大要素であるカプセル化と継承とポリモーフィズムを説明できる。

カプセル化

カプセル化はクラスの変数を保護して外部からのアクセスを禁止する機能である。
これは、クラス内で宣言する変数の先頭にアンダーバーを2つつけることで実現できる。
詳細

継承

継承はスーパークラスの機能をサブクラスに引き継ぐ機能である。
これは、"サブクラス(スーパークラス)"で実現できる。
詳細

ポリモーフィズム

ポリモーフィズムとは1つの名前でその状況に応じて別々のはたらきをもたせる機能である。
これはオーバーライドで実現できる。
詳細

モジュール

モジュールはPythonファイル(.py)のこと。
他モジュールを利用する場合は、importする。
fromでパッケージを指定して、importでモジュールを指定して、asで別名を付けることができる。

今回のサンプルのディレクトリ構成
sample
  - sample_mod1.py
  - sample_mod2.py
sample_mod1.py
def sample_print_mod1():
    print("mod1")
sample_mod2.py
from sample import sample_mod1 as mod1

mod1.sample_print_mod1()
実行結果(sample_mod2)
mod1

パッケージ

複数のモジュールで構成されたもの。
モジュールを1つのディレクトリに格納し、そのディレクトリをパッケージとして認識させるには以前は、_init_.pyファイル(空で可)が必要だったが、python3.3以降不要となった。

ファイル操作

open

ファイルは読み書きする前にopen関数で開く必要がある。

<file_object> = open(<file>, <mode>, encoding=<encoding>)

file_objectはopen関数が返すファイルオブジェクトを格納するための変数。
open関数の第一位置引数(file)には対象ファイルのパスを指定。
open関数の第二位置引数(mode)には操作種類を指定。

  • r: 読み込み。
  • w: 書き込み(上書き)。ファイルが存在しない場合は新規作成。
  • x: 書き込み(新規作成)。ファイルが存在する場合は書き込まない。
  • a: 末尾に追記。
  • t: テキストファイル操作。
  • b: バイナリファイル操作。

open関数のencodingキーワード引数にはエンコーディングタイプを指定。

close

開いたファイルは読み書きが完了したらcloseメソッドをで閉じる必要がある。

書き込み

ファイルオブジェクトのwriteメソッドでファイルに書き込む。

# sample.txtに書き込むために開く。日本語を扱う場合はUTF-8にエンコーディング。
f = open("sample.txt", "wt", encoding="utf-8")

# 書き込み
f.write("これはサンプルテキストです。\n")
f.write("メモリ16GBのMacbookPro欲しいなー。")

# 開けたら閉める
f.close()

読み込み

開いたファイルのreadメソッドでファイルの内容を読み込む。

sample.txt
これはサンプルテキストです。
メモリ16GBのMacbookPro欲しいなー。
import sys

# sample.txtを読み込むために開く。日本語を扱う場合はおなじみのUTF-8にエンコーディング。
f = open("sample.txt", "rt", encoding="utf-8")

# 一行ずつ読み込み
for row in f:
    # 改行なしで標準出力
    sys.stdout.write(row)

# 開けたら閉める
f.close()
実行結果
これはサンプルテキストです。
メモリ16GBのMacbookPro欲しいなー。

with構文

より安全で簡潔にその機能を使えるようにする構文。
具体的には、with構文から抜けると、close()の呼び出しを自動で実行してくれる。

with open("sample.txt", 'w') as f:
    f.write("Hello, world!")

コピー

shutilモジュールのcopy関数でファイルをコピー。

import shutil

shutil.copy("sample.txt", "sample_new.txt")

削除

osモジュールのremove関数でファイルを削除。

import os

os.remove("sample_renamed.txt")

名前変更

osモジュールのrename関数でファイル名を変更。

import os

os.rename("sample_new.txt", "sample_renamed.txt")

ディレクトリ操作

作成と削除

ディレクトリ(1つ)

osモジュールのmkdir関数でディレクトリを1つ作成。
osモジュールのrmdir関数でディレクトリを1つ削除。

sample_path = "C:\sample1"
os.mkdir(sample_path)
os.rmdir(sample_path)

ディレクトリ(再帰的)

osモジュールのmakedirs関数でディレクトリを再帰的に作成。
shutilモジュールのrmtree関数でディレクトリを再帰的に削除。

import shutil

sample_path = "C:\sample1\sample2"
os.makedirs(sample_path)
shutil.rmtree("C:\sample1")

ディレクトリ内一覧表示

osモジュールのlistdir関数で指定ディレクトリ内の一覧表示。

import os

sample_listdir = os.listdir("C:\sample")

ファイルあるいはディレクトリの存在確認

osモジュールのexists関数でファイルあるいはディレクトリが存在するかどうかを確認。

import os

sample_path = "C:\sample"
print(os.path.exists(sample_path))
実行結果
True

ファイルかディレクトリかの確認

osモジュールのpathオブジェクトのisfile関数でパスがファイルかどうかを確認。
osモジュールのpathオブジェクトのisdir関数でパスがディレクトリかどうかを確認。

import os

sample_path = "C:\sample"
print(os.path.isfile(sample_path))
print(os.path.isdir(sample_path))
実行結果
False
True

YAMLファイル

Pythonの標準ライブラリにはYAMLファイル処理が用意されていないため、PyYAML(サードパーティ)をインストールする必要がある。
yamlモジュールのload関数でYAMLファイルを読み込む。
YAMLファイルの開け閉めもopen関数とclose関数を使う。

サンプルのYAMLファイル
date:
  year: 2018
  month: 1
  day: 1
weather: rain
import yaml

sample_yaml_file = open("sample.yaml", "r")
sample_yaml_data = yaml.load(sample_yaml_file)
print(sample_yaml_data)
sample_yaml_file.close()
実行結果
{'date': {'year': 2018, 'month': 1, 'day': 1}, 'weather': 'rain'}

CSVファイル

CSVファイルの開け閉めもopen関数とclose関数を使う。

書き込み

csvモジュールのwriterクラスのwriterowsメソッドで書き込む。

import csv

csv_file_write = open("sample.csv", "w", newline="")
writer = csv.writer(csv_file_write)
data = [("name", "height", "weight"), ("james", 182, 76), ("tom", 176, 70)]
writer.writerows(data)
csv_file_write.close()
実行結果
name,height,weight
james,182,76
tom,176,70

読み込み

csvモジュールのreader関数で読み込む。

csv_file_read = open("sample.csv", "r", newline="")
reader = csv.reader(csv_file_read)
for data in reader:
    for cell in data:
        print(cell)
csv_file_read.close()
実行結果
name
height
weight
james
182
76
tom
176
70

JSONファイル

jsonモジュールのdumps関数で辞書データをJSONに変換。

import json

sample_dict = {"cat": "cute", "lion": "cool", "dog": "run"}
json_data = json.dumps(sample_dict)
print(json_data)
実行結果
{"cat": "cute", "lion": "cool", "dog": "run"}

ZIPファイル

zipfileモジュールのZipFileクラスのインスタンスを生成し、第三引数に"zipfile.ZIP_STORED"を与えてwriteメソッドを実行することで、複数ファイルを一つのzipファイルにまとめる。
同様にインスタンス生成し、第三引数に"zipfile.ZIP_DEFLATED"を与えてwriteメソッドを実行することで、圧縮する。

import zipfile

# 複数ファイルをまとめるだけ
sample_zip = zipfile.ZipFile("zip_result.zip", "w", zipfile.ZIP_STORED)
sample_zip.write("zip_sample_file_1")
sample_zip.write("zip_sample_file_2")
sample_zip.close()

# 圧縮も行う
sample_zip = zipfile.ZipFile("zip_result2.zip", "w", zipfile.ZIP_DEFLATED)
sample_zip.write("zip_sample_file_1")
sample_zip.write("zip_sample_file_2")
sample_zip.close()

コンフィグファイル

configparserモジュールのConfigParserクラスのreadメソッドでコンフィグファイルを読み込む。
getメソッドでセクションとキーを指定して、値を取得する。
コンフィグファイルに数値を設定したつもりでも、読み込んだ結果は文字列として扱われるので要注意。

import configparser

inifile = configparser.ConfigParser()
inifile.read("config.ini")

print(inifile.get("user", "name"))
print(inifile.get("user", "password"))
config.ini(ファイル名はなんでもOK)
[user]
name = james
password = 1234
実行結果
james
1234

ログファイル

ログレベル

Pythonでは、DEBUG、INFO、WARNING、ERROR、CRITICALの5つのログレベルが用意されている。
デフォルトではINFOログレベル以下は出力しない。

ログ出力

logging.ログレベル("ログメッセージ")でログを出力。
ログ設定はソースコード内に定義する方法とログコンフィグとして外に書き出す方法がある。

ログ設定をソースコード内に定義する

import logging

# ログ設定
logging.basicConfig(
    # ログファイルのパスを指定
    filename="C:\sample\python_logging/sample.log",

    # ログフォーマットを指定
    format='[%(asctime)s]%(filename)s(%(lineno)d): %(message)s',

    # ログフォーマット内の日付フォーマットを指定
    datefmt="%Y-%m-%d %H:%M:%S",

    # 出力ログレベルの閾値を指定。WARNING以上を出力する。
    level=logging.WARNING)

# debugログ出力
logging.debug("Debug log")

# infoログ出力
logging.info("Info log")

# warningログ出力
logging.warning("Warning log")

# errorログ出力
logging.error("Error log")

# criticalログ出力
logging.critical("Critical log")
実行結果
[2018-04-05 08:00:29]sample_logging.py(23): Warning log
[2018-04-05 08:00:29]sample_logging.py(25): Error log
[2018-04-05 08:00:29]sample_logging.py(27): Critical log

ログ設定をコンフィグで外に書き出す

基本的にはこの方法にすべき。

logging.conf(ファイル名はなんでもOK)
[loggers]
# ロガー名を設定
keys=root

[handlers]
# ログをファイルとコンソールに出力するように設定
keys=fileHandler,consoleHandler

[formatters]
# 独自ログフォーマットを使用するように設定
keys=myFormatter

[logger_root]
# ログレベルはWARNING
level=WARNING
# ログをファイルとコンソールに出力するように設定
handlers=fileHandler,consoleHandler

[handler_consoleHandler]
# コンソールログ出力の設定
class=StreamHandler
formatter=myFormatter
args=(sys.stdout,)

[handler_fileHandler]
# ログファイル出力の設定
class=FileHandler
formatter=myFormatter
args=('C:\sample\python_logging/sample.log',)

[formatter_myFormatter]
# 独自フォーマットの設定
format=[%(asctime)s]%(filename)s(%(lineno)d): %(message)s
datefmt=%Y-%m-%d %H:%M:%S
import logging
import logging.config

# 外部のログコンフィグを読み込む
logging.config.fileConfig("logging.conf")
logger = logging.getLogger("root")

# debugログ出力
logging.debug("Debug log")

# infoログ出力
logging.info("Info log")

# warningログ出力
logging.warning("Warning log")

# errorログ出力
logging.error("Error log")

# criticalログ出力
logging.critical("Critical log")
実行結果
[2018-04-05 08:00:29]sample_logging.py(23): Warning log
[2018-04-05 08:00:29]sample_logging.py(25): Error log
[2018-04-05 08:00:29]sample_logging.py(27): Critical log

ログローテーション

ログローテーションとは、1つのログファイルが肥大してディスクの圧迫などの問題が発生するのを防ぐために、一定期間ごとにログファイルを分割しておくことである。古くなったものは自動で圧縮するとか削除するとかすればよい。
ログローテーションさせる場合は、上記のloggong.confのhandler_fileHandlerセクションを下記のように書き換える。

logging.confのhandler_fileHandlerを修正
[handler_fileHandler]
# ログローテーションするクラスを設定
class=logging.handlers.TimedRotatingFileHandler
# 独自ログフォーマットを使用するように設定
formatter=myFormatter
# Dで1日ごとにログローテーションする
args=('C:\sample\python_logging/sample.log', 'D')

コメントアウト

一行コメントアウト

# aaaaaaaaa

複数行コメントアウト

ダブルクォーテーション(あるいはシングルクォーテーション)3つずつで囲むと複数行コメントアウト的なことを実現できる(厳密にはコメントアウトでなく文字列リテラル)。

"""
This 
is
a
comment
out
"""

ハッシュ値

hashlibモジュールで様々なハッシュ値を生成することができる。

import hashlib

# md5ハッシュ値生成
print(hashlib.md5(b"welcome to japan").hexdigest())
# sha1ハッシュ値生成
print(hashlib.sha1(b"welcome to japan").hexdigest())
実行結果
748a627101724e298eed2c2458fc39dd
8f1c94b91af00fc773c54391b91ffc39e7b232e9

乱数

randomモジュールで様々な形式の乱数値を生成できる。

import random

# 0.0~1.0の範囲でfloat値の乱数を生成
print(random.random())

# 第一引数~第二引数の範囲でfloat値の乱数を生成
print(random.uniform(1, 10))

# 第一引数~第二引数の範囲でint値の乱数を生成
print(random.randint(1, 10))
実行結果
0.6063238282929073
9.912161365863083
1

UUID

uuidモジュールのuuid4関数でUUIDを生成。

import uuid

# UUID生成
print(uuid.uuid4())
実行結果
f3594608-4907-4f03-9a63-5654b14daf51

メール送信

smtplibモジュールのSMTPクラスのloginメソッドで認証設定をし、sendmailメソッドでメール送信。

Yahooメールから送信する例
import smtplib

from email.mime.text import MIMEText
from email.header import Header
from email.utils import formatdate

from_address = "<mail_address>@yahoo.co.jp"
from_address_username = "<mail_address>@yahoo.co.jp"
from_address_password = "<mail_password>"
to_address = "<mail_address>"
charset = "ISO-2022-JP"
subject = "Sample Subject"
text = "This is a sample text."

msg = MIMEText(text, "plain", charset)
msg["Subject"] = Header(subject, charset)
msg["From"] = from_address
msg["To"] = to_address
msg["Date"] = formatdate(localtime=True)

# YahooのSMTPサーバと587番(SMTP AUTH)ポート指定
smtp = smtplib.SMTP("smtp.mail.yahoo.co.jp", 587)

# 認証
smtp.login(from_address_username, from_address_password)

# メール送信
smtp.sendmail(from_address, to_address, msg.as_string())

smtp.close()

日付

datetimeモジュールのdateオブジェクトで日付情報を取得。
datetimeモジュールのdatetimeオブジェクトで日付情報と時間情報を取得。

import datetime

# 現在の年月日情報取得
today = datetime.date.today()

# 現在の年月日時間情報取得
today_detail = datetime.datetime.today()

print(today)
print(today_detail)
print(today.year)
print(today.month)
print(today.day)
print(today_detail.hour)
print(today_detail.minute)
実行結果
2018-04-01
2018-04-01 11:12:05.158250
2018
4
1
11
12

マルチスレッド

threadingモジュールのThreadクラスを継承することでサブスレッドのクラスを宣言できる。
サブスレッドのクラス内で定義したrunメソッドは、メインスレッド内でサブスレッドクラスのインスタンスがstartメソッドを実行したときに自動で呼び出される。

import threading
import time

class SampleThread(threading.Thread):


    def run(self):
        for i in range(5):
            time.sleep(1)
            print("Sub Thread: " + str(i))


st = SampleThread()

# startメソッド実行により自動でThreadクラスを継承したクラスのrunメソッドが実行される
st.start()

for i in range(5):
    time.sleep(2)
    print("Main Thread: " + str(i))
実行結果
Sub Thread: 0
Main Thread: 0
Sub Thread: 1
Sub Thread: 2
Main Thread: 1
Sub Thread: 3
Sub Thread: 4
Main Thread: 2
Main Thread: 3
Main Thread: 4

データベース

SQLite

Pythonではsqliteが標準ライブラリにはいっているためドライバのダウンロードは不要。
connect関数でデータベースファイルに接続する。
cursor関数でカーソル作成する。
execute関数でSQL文実行する。
commit関数でコミットする。
close関数でデータベースファイルへの接続を断する。

import sqlite3

# データベースファイルに接続(なければファイル作成)
connector = sqlite3.connect("sample_sqlite.db")

# カーソル作成
cursor = connector.cursor()

# テーブル作成
cursor.execute("create table sample_table(id INTEGER, name TEXT, age INTEGER)")

# データ挿入(プレースホルダとか今回は省略)
cursor.execute("insert into sample_table values('1', 'james', '28')")

# データ検索
cursor.execute("select * from sample_table")
# タプルに検索結果を全て格納
result = cursor.fetchall()
for row in result:
    print(row[0])
    print(row[1])
    print(row[2])

# テーブル削除
cursor.execute("drop table sample_table")

# コミット
connector.commit()

# 接続断
connector.close()
実行結果
1
james
28

PostgreSQL

psycopg2モジュールのインストールが必要。
認証があること以外はsqlite3とほとんど同じ。

import psycopg2

# データベース接続
# 前提としてデータベース(sample-postgre-db)とユーザ((pguser/pgpwd))は作成済み。
connector = psycopg2.connect(
    host='localhost',
    database='sample-postgre-db',
    user='pguser',
    password='pgpwd',
)
cursor = connector.cursor()

# テーブル作成
cursor.execute("create table sample_table(id INTEGER, name TEXT, age INTEGER)")

# データ挿入(プレースホルダとか今回は省略)
cursor.execute("insert into sample_table values('1', 'james', '28')")

# データ検索
cursor.execute("select * from sample_table")
# タプルに検索結果を全て格納
result = cursor.fetchall()
for row in result:
    print(row[0])
    print(row[1])
    print(row[2])

# テーブル削除
cursor.execute("drop table sample_table")

# コミット
connector.commit()

# 接続断
cursor.close()
connector.close()
実行結果
1
james
28

MySQL

postgresqlとほぼ同じなので省略。

単体テスト(unittest)

単体テストのモジュールは複数あるが、ここでは標準で用意されているunittestモジュールをまとめる。
おおまかなルールはだいたい以下。

  • 単体テストモジュールにはunittestモジュールと被テストモジュールをインポートする。
  • unittestモジュールのTestCaseクラスを継承したサブクラスにテストメソッドを定義する。
  • テストメソッド名として"test_"を先頭につけることで、unittest.mainを実行すると自動で実行される。
  • assert系メソッドで被テストメソッドが期待通りのアウトプットをするかを判定する。
  • 一般的に自作テストモジュールはtestsパッケージにまとめておく。
sample_calc.py(被テストモジュール)
class SampleCalc:

    def __init__(self, num1, num2):
        self.num1 = num1
        self.num2 = num2

    def add(self):
        return self.num1 + self.num2
test_sample_calc.py(テストモジュール)
import unittest
import sample_calc


class TestSampleCalc(unittest.TestCase):

    @classmethod
    def setUpClass(cls):
        """
        setUpClassメソッドはテストクラス初期化時に一度だけ呼ばれる。
        テストクラス全体の初期化処理を定義しておく。
        今回はサンプルなので、pass。
        """
        pass

    @classmethod
    def tearDownClass(cls):
        """
        tearDownClassメソッドはテストクラス終了後に一度だけ呼ばれる。
        テストクラス全体の終了処理を定義しておく。
        今回はサンプルなので、pass。
        """
        pass

    def setUp(self):
        """
        setupメソッドは各テストメソッド実行前に呼ばれる。
        データベース接続などの前処理を定義しておく。
        今回はサンプルなので、pass。
        """
        pass

    def tearDown(self):
        """
        teardownメソッドは各テストメソッド終了後に呼ばれる。
        データベース接続断などの後処理を定義しておく。
        今回はサンプルなので、pass。
        """
        pass

    def test_add(self):
        # 被テストクラスのインスタンス化
        sc = sample_calc.SampleCalc(1, 1)

        # 被テストメソッドが期待通りの値を返すかを確認
        self.assertEqual(sc.add(), 2)


if __name__ == "__main__":
    unittest.main()

実行結果(単体テスト)
Ran 1 test in 0.001s

OK