はじめに
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
指数
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"の後に引数を指定し、":"(コロン)の後に処理を記述する。
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
def sample_print_mod1():
print("mod1")
from sample import sample_mod1 as mod1
mod1.sample_print_mod1()
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メソッドでファイルの内容を読み込む。
これはサンプルテキストです。
メモリ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関数を使う。
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"))
[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
ログ設定をコンフィグで外に書き出す
基本的にはこの方法にすべき。
[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セクションを下記のように書き換える。
[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メソッドでメール送信。
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パッケージにまとめておく。
class SampleCalc:
def __init__(self, num1, num2):
self.num1 = num1
self.num2 = num2
def add(self):
return self.num1 + self.num2
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