はじめに、 本講座のコードを読むために必要な Python の基礎知識と、 有用な tips をいくつか紹介する。 Python は動的な型付けをもつスクリプト言語であり、 JavaScript や Ruby に似ている部分が多いが、いくつか重要な違いがある。
このページの使い方: 各セクションはすぐに読める程度の長さになっているので、 特定の箇所だけをつまみ読みすることを想定している。 すべて読む必要はない。
以下に Python をインストール・実行する代表的な 3つの方法を挙げる:
Mac の場合はインストーラを開き、普通にインストールを完了させる。
Windows の場合は、インストール時のウィンドウに表示される Add Python 3.9 to PATH の部分をチェックすること:
本講座では、おもに Windows で上記 a. のケースを想定して説明しているが、 b. および c. の環境を使っていても支障はない。
上記 a. の方法で Python のみをインストールした場合、 簡単なテキストエディタをインストールしておくことが望ましい。 Visual Studio Code (以下 VS Code) は高機能なテキストエディタで、 Python のプログラムを書いたり実行したりするのにおすすめである:
VS Code が正しくインストールされると、
コマンド プロンプト上で code
コマンドを
使ってエディタを起動することができる:
C:\Users\euske> code hello.py
インストールが終わったら、左下の歯車アイコンをクリックして Extensions (拡張機能) を選ぶ。
ここでさらに Python を選択し、インストールする。
C:\Users\euske> python hello.py
python
" コマンドではなく、
"python3
" コマンドとして実行される:
(×) euske@macbook ~ % python hello.py (○) euske@macbook ~ % python3 hello.py
いずれの場合も、スクリプト名を省略して python
のみを実行すると、
対話モードで使うことができる。
これはちょっとした計算や実験をしたいときに便利である:
C:\Users\euske> python Python 3.9.9 (tags/v3.9.9:ccb0e6a, Nov 15 2021, 18:08:50) [MSC v.1929 64 bit (AMD64)] on win32 Type "help", "copyright", "credits" or "license" for more information. >>> print("hello, world!") hello, world! >>> 2+3 5
KeyboardInterrupt
例外が発生し、
Python は終了する。
Python には簡単なオンラインヘルプがあり、
help()
関数を実行すると、特定の関数やモジュールの仕様を確認できる。
>>> help(print) Help on built-in function print in module builtins: print(...) print(value, ..., sep=' ', end='\n', file=sys.stdout, flush=False) Prints the values to a stream, or to sys.stdout by default. Optional keyword arguments: ...
ただし help()
で表示される内容はやや上級者向けであり、
通常は Web上のドキュメントを参照するほうがよい:
Python には非常に多くの拡張機能が用意されている。
これらは Python Package Index というサイトで
一元的に集約されており、pip
コマンドで自動的にダウンロード・インストールできる:
C:\Users\euske> pip install numpy Collecting numpy Downloading numpy-1.22.1-cp39-cp39-win_amd64.whl (14.7 MB) |████████████████████████████████| 14.7 MB 364 kB/s Installing collected packages: numpy Successfully installed numpy-1.22.1
pip コマンドの代表的な用法は以下のとおり:
書式 | 説明 | 例 |
---|---|---|
pip install name |
拡張機能 name (およびその依存パッケージ) をダウンロードし、インストールする。 | pip install numpy |
pip uninstall name |
インストールされた拡張機能 name を削除する。 | pip uninstall torch |
pip list |
インストールされた拡張機能の一覧を表示する。 | pip list |
pip コマンドでインストールされたファイルは、
Windows の場合は
C:\Users\ユーザ名\AppData\Local\Programs\Python3.9\Lib\site-packages
以下に、macOS/Linux の場合は (通常)
/usr/lib/python3.9/site-packages
以下に格納される。
Anaconda をインストールしている場合、
conda
コマンドで
Anaconda Repository
以下のパッケージもインストールできる。
一部の環境 (Linux や macOS) では、あらかじめ Python がシステムの一部として
組み込まれており、バージョンを選ぶことができない。
このような場合は pyenv というパッケージを使うと、
バージョンが違う Python 実行環境をユーザのホームディレクトリ下に構築できる。
なお、pyenv
自体は
pip
ではインストールできず、
ディストリビューション特有のパッケージ管理ツール
(apt-get
, pacman
, rpm
など) を使う必要がある。
pyenv
をインストール後、たとえば
Python 3.9.10 をインストールしたい場合は以下のように実行する:
$ pyenv install 3.9.10
Downloading Python-3.9.10.tar.xz...
-> https://www.python.org/ftp/python/3.9.10/Python-3.9.10.tar.xz
Installing Python-3.9.10...
Installed Python-3.9.10 to /home/euske/.pyenv/versions/3.9.10
$ pyenv versions # インストールされているバージョンの確認
* system (set by /home/euske/.pyenv/version)
3.9.10
別バージョンの
python
(および pip
) を実行するには、
まず pyenv global
コマンドで使いたいバージョンを設定する:
これ以降、$ pyenv global 3.9.10
python
および pip
を実行するかわりに
つねに「pyenv exec python
」「pyenv exec pip
」のように
記述する:
$ python ... → $ pyenv exec python ... $ pip ... → $ pyenv exec pip ...
なお
pyenv
上でインストールされた python
および
pip
が使うディレクトリは、システムとは完全に独立しており、
最初は何も拡張機能がインストールされていない状態になっている。
そのため、必要な拡張機能は pyenv exec pip
を使って
適宜インストールする必要がある。
Python は言語自体の機能は比較的小さく、 多くの機能はライブラリによって提供されている。
以下は基本的な Python のプログラムである。 プログラム中の各行を、それぞれ 文 (statement) とよぶ。 Python はスクリプト言語であり、 各文はインタプリタによって逐次実行される:
# 名前を尋く。 print("What is your name?") x = input("name:") # 入力された文字列を変数 x に格納。 # 挨拶を表示。 print(f"Hello, {x}!")
#
以降は、コメントとみなされ、無視される。
print(...)
、input(...)
のような部分を
関数 (function) とよぶ。
print(…)
関数は、文字列を標準出力に表示する。
input(…)
関数は、プロンプトを表示して標準入力から文字列を入力させる。
x
のような部分を変数 (variable) とよぶ。
x = input("name:")
は代入文 (assignment) である。
f"〜"
のような文字列は f-string と呼ばれ、
文字列中の {…}
で囲まれた部分が式として評価され埋め込まれる。
この例では、変数 x
の値が展開される。
hello.py
を入力し、実行せよ。
name1:
および name2:
) を
それぞれ変数 x
と y
に入力させ、
以下のように表示させるよう変更せよ。
C:\Users\euske> python hello.py What is your name? name1: john name2: mary Hello, john and mary!
Python の文字列は、すべて Unicode 文字列である。
デフォルトでは、Python のソースコードは UTF-8 テキストとみなされる。
改行コードは DOS / Unix どちらでもよい。
それ以外の文字コードを明示的に指定したい場合、
ソースコード先頭に coding:
で始まるコメントを入れる。
たとえばソースコードが Shift_JIS で書かれている場合は、
ファイル先頭に以下のように書く:
# -*- coding: shift_jis -*-
print("こんにちわ。")
Python はいわゆる動的型付け言語であり、 各変数にはあらゆる型の値を格納することができる。
int
型
0x
表記をつけると16進数として解釈される。
1729 -3 0x270f
/
) を除いて、int
どうしの演算は
つねに int
になる。
書式 | 説明 | 例 |
---|---|---|
a + b |
a と b を足した値。 | 2 + 3 |
a - b |
a から b を引いた値。 | y - 1 |
a * b |
a と b を掛けた値。 | x * (n+1) |
a // b |
a を b で割った商。 | h // 2 |
a % b |
a を b で割った余り。 | (n+1) % 10 |
a / b |
a を b で割った値 (結果はfloat )。 |
total / n |
a ** b |
a の b 乗 (ab)。 | 2 ** n |
a & b |
a と b のビットごとAND。 | flag & 63 |
a | b |
a と b のビットごとOR。 | p | 4096 |
a ^ b |
a と b のビットごとXOR。 | q ^ 1 |
a << n |
a を n ビット左シフト。 | 1 << k |
a >> n |
a を n ビット右シフト。 | (x|m) >> 3 |
Python は、代入演算子もサポートしている:
+=
, -=
, *=
, /=
,
%=
, //=
, **=
,
&=
, |=
, ^=
,
<<=
, >>=
a = (b += 1)
のようには書けない
(ただし a = b = 0
は特別に可能である)。
++
および --
演算子は存在しない。
float
型
.
」が含まれるかどうかで int型と区別する。
-3.14 1.4551915228366852e-11 5.使用可能な演算子は int型と同じだが、
float
とint
の演算はつねに float
になる
(いわゆる「型の昇華」)。
>>> 3.14 * 2
6.28
>>> 0.5 + 0.5
1.0
>>> 3 / 4 # 除算はつねに float型。
0.75
str
型
"〜"
あるいは '〜'
で表す。
Unicode文字は \uXXXX
のように表すことも可能。
Python3 では、ひとつの文字は 1つの Unicodeコードポイントに相当する。
改行を入れたい場合は \n
を使う。
str
型を使う。
"foo bar" 'foo "bar" baz' # ダブルクォートが入った文字列 'これは\'パイプ\'ではない' # \ でエスケープしたもの "I\u2665NY" # Unicode(♥)が入った文字列
Python における文字列は変更不能 (immutable) で、
基本的にはひとつの値のように扱われる。
また、Python の文字列は f-string と呼ばれる方法で作成することもできる。
f"…{式}…"
あるいは f'…{式}…'
のように書くと、式の部分が評価され文字列として埋め込まれる
(式はいくつあってもよい)。
x = 2
y = 3
s = f"{x} plus {y} is {x+y}" # "2 plus 3 is 5"
文字列に対しては以下の演算が可能である:
書式 | 説明 | 例 |
---|---|---|
s[i] |
文字列s の i番目の文字を返す。 | name[i] |
s[i:j] |
文字列s の i〜j番目の部分文字列 (スライス) を返す。 | path[8:11] |
s + t |
文字列s と文字列t を連結。 | "Good " + "Morning" |
s * n |
文字列s の n回繰り返し (nは整数)。 | "choo" * 5 |
len(s) |
文字列s の長さを返す。 | len(name) |
s.find(t) |
文字列s に含まれる文字列 t の位置を返す
(含まれない場合は -1 )。
| path.find('/') |
s.strip() |
文字列s の先頭および末尾の空白・改行を切り取る。
(JavaScript では s.trim() ) |
line.strip() |
s.split([delim]) |
文字列s を指定した区切り文字で分割したリストを返す。 | buf.split(',') |
文字列の部分列はスライスと呼ぶ。 Python では、負のインデックスは末尾から数えた位置を表すため、 たとえば以下のような文字列があるとして、
s = "HEADACHE"この場合の
s
の各部分は、以下のようになる:
bool
型
True
と False
の2つしかない。
Bool型の具体的な使い方は、条件式と条件式 の節で述べる。
True False
None
型
None
だけである。
None
int
→ float
への昇華を除いて)
自動的には型変換されない。
たとえば 123 + "abc"
という演算は
TypeError
を発生する。
データ型の変換は、以下の関数を使って明示的におこなう:
書式 | 説明 | 例 |
---|---|---|
int(x) |
文字列を整数として解釈。 あるいは、 float を整数に変換。 |
int('123') int(3.14) |
int(x, n) |
文字列をn進数の整数として解釈。 | int('6c1', 16) |
float(x) |
文字列を小数として解釈。 あるいは、 int を小数に変換。 |
float('3.14') float(123) |
str(x) |
数値 (int またはfloat ) を文字列として表現。 |
str(-2.3) |
ord(x) |
1文字の Unicodeコードポイントを返す。 | ord('あ') |
chr(x) |
Unicodeコードポイントを長さ1の文字列に変換。 | chr(65) |
repr(x) |
任意のPythonオブジェクト(値)を、正式な文字表現に変換する。 たとえば文字列 abc の文字表現は、
クォートを含む 'abc' である。 |
repr('abc') |
以下のプログラムが何をするか、実行する前に推測し、 実際に実行して確認せよ。
w = int(input("w:")) h = int(input("h:")) area = w*h / 2 print(f"w={w}, h={h}, area={area}")
c = input("character:") c1 = chr(ord(c)+1) c2 = chr(ord(c)+2) print(f"{c} {c1} {c2}")
s = input("path:") i = s.find("/") dirname = s[:i] basename = s[i+1:]
Python の制御文は、基本的には 3つしかない。
if
文、while
文 および for
文である。
(Python 3.10 では match
文が導入されたが、本講座では扱わない。)
if
文と while
文では、条件式を使う。
Python の比較演算子には、次のようなものがある
(比較の結果はつねに True
か False
のどちらかとなる):
書式 | 説明 | 例 |
---|---|---|
a < b |
a が b より小さい。 | y < height |
a > b |
a が b より大きい。 | (s+t) > m |
a == b |
a と b が等しい。 | key == 32 |
a != b |
a と b が等しくない。 | status != 0 |
a <= b |
a が b 以下。 | t <= threshold |
a >= b |
a が b 以上。 | (i+1) >= n |
リストおよびタプルに対しては、以下の比較演算子が利用可能である:
x in a |
要素 x が a 中に含まれている。 | z in [1,3,5,7] |
x not in a |
要素 x が a 中に含まれていない。 | z not in [1,3,5,7] |
int
と float
を除いて)
異なる型どうしの大小比較はできない。
たとえば "abc" < 123
は TypeError
を発生する。
また、(JavaScript とは異なり) 型が異なる値は自動的、変換されないため
"123" == 123
は False
となる。
Python における等号 ==
は、
内容の比較 (Java でいう .equals()
) である。
例えば、異なるリストどうしを比較した場合、
すべての要素が一致するかどうかチェックされる。
いっぽうで、参照が等しいかどうかをチェックするには
is
演算子を使う。
これは JavaScript における ===
演算子に似ている:
a is b |
参照 a と b が等しい。 | p[0] is q |
a is not b |
参照 a と b が等しくない。 | x is not None |
論理演算子には、次のようなものがある:
書式 | 説明 | 例 |
---|---|---|
a and b |
a および b の両方が真。 | 2 <= x and x <= 5 |
a or b |
a または b のどちらか1つ以上は真。 | x == 0 or y == 0 |
not a |
a が真でない。 | not (x != 3) |
&&
や ||
は使えない。
いわゆる三項演算子 (? :
) は、Python では次のように書く:
(a if c else b) # c が真ならば a、さもなくば b を返す。
多くの言語で {…}
のように表現されるブロックは、
Python では一定幅の インデント (indentation、字下げ) により表現される。
正しく字下げがないソースコードは SyntaxError
が発生し、
(部分的にすら) 実行されない。
動かない例 (インデントがない):s = input("password:") if s == "sesame": print("Correct.") else: print("Wrong.")
インデントの幅は (一貫しているかぎり) 何文字でもよいが、
一般的な慣例では 半角スペース4個
(
) を使う。
半角スペース以外の文字を使うと動かない。
たとえば以下の例は正しく見えるが、実際にはエラーとなる:
動かない例 (全角スペースが入っている):s = input("password:") if s == "sesame": print("Correct.") else: print("Wrong.")
if
文
いくつかの構文パターンが存在する。
if
節の内部では、
必ずインデントが必要である。
password = input("password:")
if password == "sasami":
# 条件式が真であるときに実行される。
print("Correct.")
print("Good.")
x = int(input("x:")) if 0 < x: # 条件式が真であるときに実行される。 print("positive") else: # 条件式が真でないときに実行される。 print("zero or negative")
x = int(input("x:")) if 0 < x: # 条件式1 # 条件式1が真であるときに実行される。 print("positive") elif 0 > x: # 条件式2 # 条件式1が真でなく、条件式2が真であるときに実行される。 print("negative") else: # どの条件式も真でないときに実行される。 print("zero")
if
やelse
のなかに、さらにif文が入ることもありうる。
この場合、「内側のif文」は2重にインデントされる。
a = input("a:") b = input("b:") if a == "2": if b == "3": print("both correct") else: print("a is correct") else: print("a is not correct")
while
文i = 0 while i < 10: # 条件式が真の間、繰り返し実行される print(f"i={i}") i += 1 # 条件式が真でなくなったとき実行される print("end")
while
ループ中では、
break
文および continue
文が使える。
i = 0 while True: # つねに真 print(f"i={i}") i += 1 if 10 <= i: # 条件式が真ならばループを抜ける break print("end")
while
文の後に else:
節を利用できる。
これは while
ループが途中で break
されず、
ループが最後まで (条件式が真でなくなるまで) 実行されたときにのみ
実行される。
a = [5,9,4,0] target = int(input("target:")) while i < len(a): # 条件式が真の間、実行される if a[i] == target: # targetが発見されたら抜ける。 print("found") break else: # 条件式が真でなくなったら実行される print("not found")
for
文
Python の for
文は C言語などとは異なり
「リストの各要素を1つずつたどる」動作をする。
これはシェルスクリプトにおける for
や、
C# における foreach
文に近い。
a = [5,9,4,0]
for x in a:
# aの要素をひとつずつ表示する
print(f"x={x}")
range()
関数 (後述) を使う:
# i を 0〜9 まで変化させつつループ。
for i in range(10):
print(i)
a = [5,9,4,0]
for x in a:
print(f"x={x}")
if x % 2 == 0:
# 偶数であれば抜ける
break
while
文と同様、for
文における
else:
節も、途中で break
されず、
最後まで (すべての要素をたどるまで) ループが実行されたときに
実行される。
a = [5,9,4,0] target = int(input("target:")) for x in a: if x == target: # targetが発見されたら抜ける。 print("found") break else: # すべての要素をたどったら実行される。 print("not found")
pass
文
何もしない文。while
文などで
中に最低ひとつの文が必要なときに、プレースホルダとして使われる。
C や Java における「;
」に近い。
# is_busy()が真のあいだ待つ。
while is_busy():
pass
try…except
文発生した例外を補足する。詳細は エラーと例外 を参照。
switch
…case
文や、
do
…while
文は存在しない。
assert
文
デバッグ時の検査をおこなう文。
条件式が成立していれば何もせず、
成立していなければ指定されたメッセージとともに
AssertionError
を発生する。
assert 0 < x, "x must be positive!"Python に-O
オプションをつけて起動した場合、assert
文は無視される。
n = int(input("n:")) a = 1 b = 1 ...
for i in range(100): ...
Python には可変長の配列として、リスト (list
型) と
タプル (tuple
型) が存在する。
この2つは機能的によく似ており、Python におけるリストとタプルの違いは
よく取り沙汰される議論であるが、まずはリストから先に説明する。
a = [5, 9, 4, 0] b = ["foo", "bar", 3.7] empty = [] # 空のリスト table = [[1,2], [3,4], [5,6,7]] # リストのリスト
a = [5, 9, 4, 0] b = a # 参照はコピーされるが、リストは複製されない。 b[0] = 1 # a[0]も変更される。
「リストのリスト」では、外側のリストの各要素には内側のリストの参照が入っている。
table = [[1,2], [3,4], [5,6,7]]
[…]
を使ってアクセスできる。
インデックスは 0 から開始する。
x = a[i] # 要素の参照 a[0] = 3 # 要素の更新 a[n+1] = a[n] # 要素のコピー
インデックスはつねに int型である。 インデックスが負の数だった場合、それはリストの末尾から数えた要素になる。
print(a[-1]) # 最後の要素 print(a[-2]) # 最後から2番目の要素
IndexError
が発生する。
a = [5,9,4,0,7,3,1,8]
このとき a
の部分列は、以下のようにして取り出せる。
なお、リストのスライスはつねに新しいリストが作成される。
a[i:] |
i >=0 の場合:
位置 i 以降の要素
|
i < 0 の場合:
末尾から i 番目以降の要素 |
|
a[:i] |
i >=0 の場合:
先頭から位置 i までの要素
|
i < 0 の場合:
(i が負の場合は末尾からの位置) |
|
a[i:j] |
位置 i から位置 j の間にある要素
(このときの長さは j - i 要素) |
以下はスライスの例である:
copy()
メソッドを使う方法である。
どちらを使ってもよい。
a = [5,9,4,0] b = a[:] # 全要素のスライスを作成。 c = a.copy() # メソッドを使う方法。
list
型のメソッドを使っておこなう。
(歴史的な理由により、削除だけはメソッドではなく del
文である。)
書式 | 説明 | 例 |
---|---|---|
a.append(x) |
リスト a の末尾に要素 x を追加。 | r.append(2) |
a.insert(i, x) |
リスト a の i番目の要素として x を挿入。 | args.insert(1, "foo") |
del a[i] |
リスト a の i番目の要素を削除。 | del users[0] |
書式 | 説明 | 例 |
---|---|---|
a + b |
リストa とリストb を連結 (新しいリストが作成される)。 | [1,2,3] + [4,5,6] |
a * n |
リストa の n回繰り返し (新しいリストが作成される)。 | [0] * 5 |
len(a) |
リスト a の長さを返す。 | len(args) |
sorted(a) |
リスト a をソートした新しいリストを返す。 | sorted([5,9,4,0]) |
+
演算では新しいリストが作成されるが、
代入演算子 +=
を用いた場合、新しいリストは作成されず、
破壊的に要素が追加される。
a = [5,9]
b = a
a += [4,0] # a,b ともに [5,9,4,0] となる。
[ 式 for 変数名 in リスト ]
a = [1,2,3,4,5]
b = [ x*2+1 for x in a ] # [3,5,7,9,11]
内包表記には、if
節も追加できる。
こうすると特定の条件を満たす要素だけを抽出できる。
a = [1,2,3,4,5]
e = [ x for x in a if x % 2 == 0 ] # [2,4]
タプルはリストと似た機能をもつ型としてよく挙げられる。
最初に見た目の違いとして、リストでは各要素を [1,2,3]
のように
表現するのに対して、タプルは (1,2,3)
のように表現する。
機能的にもっとも大きな違いは、リストは変更可能 (mutable) なのに対して
タプルは変更不能 (immutable) であるということである。
したがってリストでは許される要素の変更や削除などは、
タプルに対しては使えない:
t = (5,9,4) # 3要素のタプルを作成。 print(t[0]) t[1] = 3 # TypeErrorが発生 del t[2] # TypeErrorが発生
ではタプルは一体何のために存在するのかというと、 おもに複数の値をまとめる匿名構造体としてである。 歴史的に Python には C の構造体に相当するものがなく、 バージョン 1.5 でクラスが導入されるまで、 タプルが構造体の代わりに使われていた。 タプルを使うと、通常ひとつの値しか入らない場所 (変数、リストの各要素、関数の返り値など) に複数の値を 「つめこむ」ことができる。(リストでもこれは可能だが、 タプルは変更不能なため、複数の値をまとめたタプルも 依然として「ひとつの値」として扱われる。) 代入文の左辺にタプルを使うと、まとめられた値を複数の変数に分解できる:
user = ('john', 30, 6.5) # タプルの作成 (pack) (name, age, height) = user # タプルの分解 (unpack)
タプルは任意の長さのものを作成できるが、一度作成されたタプルの 長さが変わることはないため、 この意味で、タプルは数学における伝統的な「ベクトル」に近い (数学におけるベクトルは、要素数が変化したりしない)。 また、リストの基本的な役割が配列であり、その各要素は 「均質」として扱われるのに対して、タプルは構造体であるため、 タプル中の各要素はそれぞれ別の型・役割をもっていることが多い。
現在の Python プログラミングにおけるタプルのおもな用途は、 次のとおりである:
users = [('john', 30, 6.5), ('bob', 24, 5.9)]
def split(name): return (name[:8], name[8:])
d = {(2,3):6, (-1,0):7}
タプルは内部的には参照型であるが、 変更不能であるため、リストのように明示的に複製する必要がない。 タプル (の参照) のコピーはつねに同じ値であることが保証される:
a = (5,9,4,0) b = a # 参照がコピーされる。 a[0] = 1 # 変更不能。
Python における辞書 (dict
型) は、
他言語では「ハッシュテーブル」「map型」「連想配列」などと
呼ばれている。辞書を使うと、任意の変更不能なデータに、
別の値を関連づけることができる。
これらの値をそれぞれ辞書のキー (key) および バリュー (value) と呼ぶ。
辞書の表面上の使い方は、リストとよく似ている。
リストのインデックスとして int
型のかわりに
任意の値が入ると考えればよい:
d = {'abc':123, 'def':456} # 辞書を作成する。 print(d['abc']) # キー'abc'に対応するバリューを取得する。 d['xyz'] = 999 # キー'xyz'に対応するバリューとして9999を設定する。Python の辞書は、見た目が JavaScript の「オブジェクト」に似ているが、 別の概念である。JavaScript とは異なり、Python ではd['abc']
とd.abc
は異なる意味をもつ。 Python にもいわゆる 「オブジェクト (インスタンス型)」は存在するが、 辞書型とは異なる。
辞書のキーは、必ず変更不能な値でなければならない (辞書のキーが途中で変化すると混乱するためである)。 つまり、リストは辞書のキーになれない。 ただし、タプルは変更不能であることが保証されているため、 辞書のキーになることができる。タプルのおもな存在理由はこれである:
d = {} k1 = [1,2,3] # TypeError d[k1] = 4 k2 = (1,2,3) # Ok d[k2] = 4
d = {'abc': 123, 'def': 456} w = {'c':['cat','car'], 'd':['dog','duck']} empty = {} # 空の辞書 files = { 'usr': {'bin': 42, 'lib': 65} } # 辞書の辞書
リストと同様に、辞書も内包表記を使って作成することができる:
d = {'abc': 123, 'def': 456}
dd = { k: v*2 for (k,v) in d.items() } # {'abc': 246, 'def': 912}
[…]
記法を使ってアクセスできる。
v = d['abc'] # キーabcに対するバリューを取得 d['xyz'] = v # キーxyzに対するバリューを設定 del d['xyz'] # キーxyzに対するバリューを削除
KeyError
が発生する。
.get()
メソッドを使うか、以下のように
try…except
文を使って KeyError
を
補足しなければならない:
try: v = d[k] # キー k に対するバリューを取得。 except KeyError: v = None # キーが存在しない場合は None とする。
書式 | 説明 | 例 |
---|---|---|
d.clear() |
辞書内のキー・バリューを全削除する。 | d.clear() |
d.copy() |
辞書をコピー (shallow copy) する。 | e = d.copy() |
d.get(k, v=None) |
指定されたキー k に対するバリューを返す。 キーが存在しないときにはデフォルト値として v が返される。 |
v = d.get(name) |
d.keys() |
辞書 d のキー一覧 (※) を返す。for文で巡回可能。 (実際には for 文で使う場合、.keys() を省略できる)返されるキーの順序は未定義であり、予測不能であるため注意。 |
for k in d.keys(): print(k)または for k in d: print(k) |
d.values() |
辞書 d のバリュー一覧 (※) を返す。for文で巡回可能。 返されるバリューの順序は未定義であり、予測不能であるため注意。 |
for v in d.values(): print(v) |
d.items() |
(キー, バリュー) からなる
2要素タプルの一覧 (※) を返す。for文で巡回可能。 返されるタプルの順序は未定義であり、予測不能であるため注意。 |
for (k,v) in d.items(): print(f"{k} -> {v}") |
len(d) |
辞書 d 中のキー・バリューの個数を返す。 | n = len(d) |
(※) ここで返される「一覧」とはリストではなく、 イテレータ である。
Python の集合 (set
型) は、
文字どおり重複を許さない値の集合である。
これは「辞書のキーのみを取り出した型」とも言える。
集合の各要素は、辞書と同様に変更不能な値でなければならない。
s = {'abc', 'def'} w = set([1, 2, 3]) # リストを集合に変換 empty = set() # 空の集合 t = {[1,2,3], [4,5,6]} # TypeErrorが発生
内包表記を使った作成方法は以下のようになる:
s = {'abc', 'def'}
ss = { x[0] for x in s } # {'a', 'd'}
in
演算子または
not in
演算子を使って要素が含まれているか否かを判定できる。
s = { 'abc', 'def' } print('abc' in s) # True print('xyz' not in s) # True
これ以外の集合型に関する演算としては、以下のものがある:
書式 | 説明 | 例 |
---|---|---|
s.clear() |
集合の要素をすべて削除する。 | s.clear() |
s.copy() |
集合をコピー (shallow copy) する。 | tmp = s.copy() |
s.add(x) |
集合に要素をひとつ追加する。 | files.add(path) |
s.remove(x) |
集合から要素をひとつ削除する。 (要素がない場合は KeyError が発生する)
|
users.remove(uid) |
s.union(t) |
集合 s と t の和集合を返す。 | uid = uid.union(added) |
s.intersection(t) |
集合 s と t の積集合を返す。 | r = kwd1.intersection(kwd2) |
s.difference(t) |
集合 s から集合 t を引いたものを返す。 | objs = objs.difference(removed) |
s.issubset(t) |
集合 s が集合 t の部分集合 (subset) であれば真。 | if {1,4}.issubset(flags): process() |
s.issuperset(t) |
集合 s が集合 t の上位集合 (superset) であれば真。 | if not perms.issuperset(a): print("denied") |
len(s) |
集合 s の要素数を返す。 | print(len(s)) |
文字列からなるリスト x
を与えると、
もっとも頻出する文字列を選ぶ処理を書け:
x = ['a','b','a','c','b','a']
...
# 'a' を表示する。
(ちなみに、
同様の処理は collections.Counter
クラスを
使えば可能である)
Python の関数は、以下のようにして定義する。 関数の内部は、インデントする。
def 名前(引数1, 引数2, ...): 処理 ... return [返り値]
return
文の返り値、または
return文全体は省略可能である
(その場合は None
が返り値となる)。
あるいは、関数中に複数の return
があってもよい。
# 関数avgを定義する。 def avg(x, y): a = (x+y)/2 return a # 関数avgを利用する。 print(avg(3, 5)) # 引数をキーワード指定する。 print(avg(x=3, y=5))
Python の関数は、def
文が実行されたときに定義される。
まだ def
が実行されていない時点で利用しようとすると
NameError
が発生する。
# def文の実行前に関数を呼び出す。 print(avg2(3, 5)) # NameErrorが発生 def avg2(x, y): a = (x+y)/2 return a
def avg(x=3, y=5): a = (x+y)/2 return a print(avg()) # 引数はどちらもデフォルト値。 print(avg(1)) # 第2引数のみデフォルト値。 print(avg(x=1)) # 第1引数のみキーワード指定、第2引数はデフォルト値。 print(avg(y=3)) # 第2引数のみキーワード指定、第1引数はデフォルト値。 print(avg(1, 3)) # デフォルト引数を使わない。
*
をつけると
任意の個数の引数からなるタプルとして受け取れる。
引数なしで呼び出した場合、空のタプルが渡される。
def avg(*args): a = 0 # タプルの要素をひとつずつ巡回。 for x in args: a += x return a/len(args) print(avg()) # argsは空のタプル。 print(avg(1,3)) # argsは2要素のタプル。 print(avg(1,3,5)) # argsは3要素のタプル。
Python では、関数自体がひとつの値 (オブジェクト) であり、 関数名は実はそのオブジェクトを保持している変数名である。
たとえば print()
関数は、実際には
「画面に文字列を表示する関数 (の参照)」がたまたま print
という
変数に保持されているにすぎない。その証拠に、以下のようにすると
print()
および p()
は
まったく同じ関数として扱える:
つまり Python におけるp = print # print関数の参照を p にコピー。 p("hello") # 関数pを呼び出す。
def
文は、
関数オブジェクトを変数に代入する文とみなすことができる:
def foo(x): # "foo = <関数オブジェクト>" と同値
...
この特徴を利用して、以下のように関数オブジェクトの一覧をリストに入れ、
動的に切り替えることが可能になる:
# 3種類の異なる関数を定義。 def p1(x): return x+1 def p2(x): return x+2 def p3(x): return x+3 # これらをリストに入れる。 funcs = [p1, p2, p3] # ひとつずつ呼び出す。 for f in funcs: print(f(123))
以上のように、Python では関数名と変数名は同一の名前空間を使っている。 そのため、同一の名前 (変数) に関数とそれ以外のデータを入れることはできない:
def a(x): ... a = 1 # 関数a()は使えなくなる。 a() # TypeErrorが発生
Python では、いわゆるクロージャが使える。 クロージャとは、関数内で新たに関数を定義したとき、 その関数の外側にある変数が記録され、 アクセスできるようになっているものである:
def makeplus(n): # 関数内でさらに関数 plusを定義する。 # (この関数は変数 n にアクセスできるクロージャである) def plus(x): return x+n # 作成した関数オブジェクト plus を返す。 return plus f = makeplus(2) # fはクロージャ。 print(f(3)) # 2+3 が返される。
lambda
式
ある種の関数は、(def
文を使わずとも) 値として直接表現することが可能である。
これが lambda
式である:
# 通常の関数定義。 def avg(x, y): return (x+y)/2 # lambda式を関数として代入した場合。 avg = (lambda x, y: (x+y)/2)lambda
中で表せるのは 1つの値を返す式のみである。 Python の一般的な構文 (代入文や制御文など) は使うことができない。 Python には、JavaScript におけるfunction() { ... }
のような、 関数全体をひとつの値として直接表現する方法は存在しない。 その場合は一度def
文を使って変数に代入する必要がある。
global
文Python では、関数の外で (スクリプト中にじかに) 定義されている変数は すべてグローバル変数として扱われる。 いっぽう関数内で定義 (代入) されている変数は ローカル変数とみなされ、外側の変数とは区別される:
flag = False # グローバル変数 def set_flag(): flag = True # 関数内のローカル変数 return
上のケースでは、関数 set_flag()
内で
変更している変数 flag
は
グローバル変数 flag
とは別物なため、
set_flag()
を呼んでも
グローバル変数 flag
の値は変わらない。
このような事態に対処するため Python では
global
文が用意されている。
関数の先頭で、以下のように global
文を使うことにより
指定されたグローバル変数にアクセスできるようになる:
flag = False # グローバル変数 def set_flag(): global flag # flag をグローバル変数として認識する flag = True return
0
で埋められた二次元リスト (の参照) に対して、
指定された位置 (x,y) および大きさ (w,h) の矩形を
1
で描く関数 drawrect()
を定義せよ:
# リストa 内の位置 (x,y) に大きさ (w,h) の矩形を描く。 def drawrect(a, x, y, w, h): ... return canvas = [ [0]*10 for i in range(10) ] drawrect(canvas, 0, 0, 10, 10) drawrect(canvas, 2, 3, 6, 4) for line in canvas: print(line) # 以下が表示される: # [1, 1, 1, 1, 1, 1, 1, 1, 1, 1] # [1, 0, 0, 0, 0, 0, 0, 0, 0, 1] # [1, 0, 0, 0, 0, 0, 0, 0, 0, 1] # [1, 0, 1, 1, 1, 1, 1, 1, 0, 1] # [1, 0, 1, 0, 0, 0, 0, 1, 0, 1] # [1, 0, 1, 0, 0, 0, 0, 1, 0, 1] # [1, 0, 1, 1, 1, 1, 1, 1, 0, 1] # [1, 0, 0, 0, 0, 0, 0, 0, 0, 1] # [1, 0, 0, 0, 0, 0, 0, 0, 0, 1] # [1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
Python では、エラー発生時に必ずトレースバック (Traceback、いわゆるスタックフレーム) が表示される。 たとえば、以下のプログラムを実行すると:
def avg(x, y): a = (x+y)/2 return a def main(): print(avg("a", "b")) main()
以下のトレースバックが表示され、Python は終了する。 トレースバックには各関数の呼び出し位置が、 外側 → 内側の関数の順で表示される:
Traceback (most recent call last): File "/home/euske/.../foo.py", line 8, in <module> (最外部の発生位置) main() File "/home/euske/.../foo.py", line 6, in main (main中の発生位置) print(avg("a", "b")) File "/home/euske/.../foo.py", line 2, in avg (avg中の発生位置) a = (x+y)/2 TypeError: unsupported operand type(s) for /: 'str' and 'int' (例外の種類および内容)
各呼び出し位置に対して ファイル名, line 行数, in 関数名
および、その行のコードが表示されている
(もっとも外側のスクリプトレベルの関数は <module>
と表示される)。
最後に、例外の内容が表示される。デバッグするには、
これらの情報をもとにコードを修正すればよい。
Python は (JavaScript などと比べると) 比較的頻繁に
例外 (エラー) を発生させる言語である。
Python における例外は、SyntaxError
を除けば、
すべて実行時の例外である。
例外一覧については
Built-in Exceptions
を参照。
よく見かけるエラーの種類を以下に挙げる:
例外 | 原因 | 例 |
---|---|---|
KeyboardInterruption |
Control + C を押したときに発生する例外。 | |
SyntaxError IndentationError
|
構文エラー。 |
print("hello"]
|
NameError |
グローバル変数・関数が定義されていない。 | a = nonexistent_var undefined_func(1) |
UnboundLocalError |
NameError とほぼ同じだが、
関数内で定義された変数に値がまだ代入されていない場合に起こる。
|
def foo():
print(x)
x = 1
|
AttributeError |
オブジェクトの 属性 が定義されていない。 |
s = "abc"
print(s.foo)
|
TypeError |
型の不一致、または関数の引数の不一致。 | "abc" / 2 input("abc", "xyz") |
ValueError |
さまざまな関数で、想定外の引数を与えた場合に発生する。 |
int("よう")
|
ZeroDivisionError |
ゼロ除算エラー。 |
2 / 0
|
IndexError |
リストのインデックスが範囲を超えている。 |
a = [5,9,4,0]
print(a[4])
|
KeyError |
辞書 のキーが存在しない。 |
d = {'abc':123, 'def':456}
print(d['pqrs'])
|
UnicodeDecodeError |
文字列をファイル等から読み込む際に正しくデコードできなかった。 |
b'\x80'.decode('utf-8')
|
UnicodeEncodeError |
文字列をファイル等に書き込む際に正しくエンコードできなかった。 |
'あ'.encode('ascii')
|
OSError |
OSレベルで発生する入出力関連のエラー。 |
open('nonexistent_file')
|
AssertionError |
assert 文の条件が成立しなかったときに発生する。 |
assert 2+2 == 5
|
try…except
文による例外の補足
Python の例外は、Java や C++ の例外と同様に
例外オブジェクトを使っている。すべての例外は
try…except
文によって補足できる。
try…except
文は、
ひとつのtry
節と複数のexcept
節からなる:
try: なんらかの処理 except 例外クラス1 as 変数名1: 例外処理1 except 例外クラス2 as 変数名2: 例外処理2 ...
例外クラスには上に挙げた例外のどれかを指定する:
try: # int が例外を発生する可能性。 n = int(input("number:")) print(f"n={n}") except ValueError as e: # ValueErrorは変換できない文字列が与えられたことを意味する。 print("not a number")
上の例で、補足された例外オブジェクトは変数 e
に格納される。
ここには普通は例外の内容が e.args
に含まれており、
トレースバック情報も含まれている (が、詳細は省略する)。
実際には (Javaなどと同様に) Python の例外クラスは階層化されており、
except
節には当該する例外クラスか、その基底クラスを
指定してもよい。すべての例外の基底クラスは
Exception
であるので:
のようにすればあらゆる例外を補足できるが、 例外クラスはなるべく細かく指定すべきであって、 これは推奨される方法ではない。try: なんらかの処理 except Exception as e: 例外処理
Python のプログラムでは イテレータ (iterator) がよく使われる。
簡単にいえば、イテレータとは
「for
文で巡回可能な値の列」のことである。
Python のイテレータは C++ や Java におけるイテレータと機能的にほぼ同じだが、
C++ や Java ではイテレータは単なるクラスのひとつであるのに対し、
Python のイテレータは言語とより密接に関連している。
Python では、ファイルからの入力や DB テーブルの行など、
可変長の値を返す処理の多くがメモリ効率のために
イテレータを返すようになっている。また
辞書の d.keys()
などのメソッドが返す結果も
イテレータである。
iter()
関数でリストの各要素をたどるイテレータを作成し、
next()
関数でそれを1つずつ巡回する。
それ以上の要素がなくなると StopIteration
例外が発生する。
a = [1,2,3] it = iter(a) # イテレータを作成。 print(next(it)) # 1 print(next(it)) # 2 print(next(it)) # 3 print(next(it)) # StopIteration例外
実際には、上のような方法でイテレータを使うことはまずない。
イテレータに対して for
文を使えば、
以上の処理が簡単に書けるためである:
a = [1,2,3] it = iter(a) # イテレータを作成。 for x in it: print(x) # イテレータが終われば for文も終わる。
さらに、for
文は与えられたオブジェクトのイテレータを自動的に生成するため、
実は iter()
関数すら必要ない。
一般的に、イテレータ型を受けつける部分には
リスト・タプル・文字列・集合などの型も受けつける。
a = [1,2,3]
for x in a: # 自動的にイテレータが作成される。
print(x)
range()
関数
range()
関数である。
これはおもに for
文で伝統的な
(カウンタを使った) ループを表すのに使われる:
# range(n): 0…(n-1) までの整数を返す。 for i in range(10): print(i) # i: 0〜9 # range(i, j): i…(j-1) までの整数を返す。 for i in range(10, 20): print(i) # i: 10〜19 # range(i, j, k): i…(j-1) までの k きざみで増える整数を返す。 for i in range(0, 10, 2): print(i) # i: 0,2,4,6,8
書式 | 説明 | 例 |
---|---|---|
enumerate(it) |
イテレータ it の各要素にインデックスを付加する。a,b,c,… の列は
(0,a),(1,b),(2,c),… という
2要素タプルの列に変換される。 |
a = ['abc','def','xyz']
# iter(a) は省略可。
for (i,x) in enumerate(a):
print(i,x)
|
map(f, it) |
イテレータ it の各要素に関数 f を適用する。a,b,c,… は
f(a),f(b),f(c),…
に変換される。 |
a = [1,2,3] for x in map(str, a): print(x) |
zip(it1, it2, …) |
イテレータ it1, it2, ...の各要素を
ひとつのタプルにまとめる。a,b,c,… および
x,y,z,… というイテレータは
(a,x),(b,y),(c,z),… に変換される。 |
a = ['abc','def','xyz'] b = [1,2,3] for (x,y) in zip(a, b): print(x, y) |
書式 | 説明 | 例 |
---|---|---|
list(it) |
イテレータ it をリストに変換する。 | a = list(it) |
tuple(it) |
イテレータ it をタプルに変換する。 | t = tuple(it) |
sum(it) |
イテレータ it の数値をすべて合計する。 | sum(range(1,5)) |
min(it) |
イテレータ it の最小値を返す。 | min([5,9,4]) |
max(it) |
イテレータ it の最大値を返す。 | max(scores) |
s.join(it) |
イテレータ it の文字列を区切り文字 s で連結する。 | '\n'.join(lines) |
len()
関数を使うことはできない。
[…]
のかわりに (…)
を使うと、
リストのかわりにイテレータが返される。これをジェネレータ式とよぶ:
a = [1,2,3,4,5]
b = ( x*2+1 for x in a )
for x in b:
print(x) # 3,5,7,9,11
yield
文を使うと、
イテレータを通常のプログラムのように簡単に記述できる。
このような関数をジェネレータと呼ぶ。
関数定義の中で yield
文が使われていると、
その関数はジェネレータとみなされる。通常の関数とは異なり、
ジェネレータは段階的に実行される。
# g: すべての奇数からなるイテレータ (無限に続く) を返す。 def g(): i = 1 while True: yield i i += 2 # イテレータを消費する。 for n in g(): print(n) # 1,3,5,7,…
上の例にあるように、ジェネレータ関数は終了しなくてもかまわない。 具体的には、ジェネレータ関数は以下のように実行される:
yield
文に到達するまで実行される。
return
文による返り値は無視される。
Python では、オブジェクト指向はどちらかといえば付け足し的な扱いであり、 (Javaのように) なんでもクラス化することが推奨されているわけではない。 クラスを使わない Python プログラムも多く存在する。 Python におけるクラスの用途は、おもに次の2つである:
Python でクラスを扱う場合は、インスタンス型を使う。 これは一連の手続き (メソッド) にデータが付随したものである。 すべてのインスタンスは、そのテンプレートである クラスから実体化される。
以下は Python の典型的なクラス定義と使用の例である (Python では、クラス名は大文字から始めることが多い) :
# Counterクラスの定義 class Counter: # コンストラクタ def __init__(self, count): self.count = count # inc()メソッド: カウンタをnだけ増やす。 def inc(self, n): self.count += n return self.count # インスタンスの作成 c = Counter(0) print(c.inc(1)) # 1 print(c.inc(1)) # 2 print(c.inc(1)) # 3
class
文で始める。
Java などと違って、
Python ではインスタンス変数をあらかじめ宣言する必要はなく、
ひとつのインスタンス中にあとからいくらでも変数を追加できる。
次に、インデントされた各メソッドを記述する。
上の例では、メソッド __init__
および inc
が
定義されている。__init__
メソッドはコンストラクタであり、
そのインスタンスが作成されるときに最初に呼ばれる
(なお、コンストラクタは省略可能である)。
インスタンスに所属する変数は、
ふつうコンストラクタ (__init__
メソッド) で初期化される。
上の例では count
というインスタンス変数が初期化されている。
Python では、インスタンスに所属する変数を、オブジェクトの
属性 (attribute) と呼ぶ。属性は、c.count
のように
インスタンスに .
をつけることで参照・変更できる。
self.
をつける必要がある。
同様に、同一クラス内のメソッド呼び出しも単に foo()
とはできず、
必ず self.foo()
のように書く必要がある。
なお self
はただの変数名であり、
this
のような特殊なキーワードではない
(self
という名前は慣例にすぎない)。
関数と同様、Python のクラスは class文が実行されたときに定義される。
また、関数オブジェクトと同様に、Python ではクラスもひとつの値である。
つまり class
文は、実際には
クラスオブジェクトを変数に代入する文である:
class Counter: # "Counter = <クラスオブジェクト>" と同値
...
実際、Counter
クラスを別の変数に代入すれば、
その名前はクラスと同様に扱うことができる:
T = Counter
c = T(0) # Counterインスタンスが作成される
self
でなければならない。
(実際には他の名前もつけられるが、self
が慣例である。)
Python では自分自身のインスタンスを表す this
キーワードは
存在せず、インスタンスへの参照はつねにメソッドの第1引数 self
に渡される。
ただし、引数として self
が現れるのは定義だけであって、
メソッド呼び出し時には self を除いた引数を渡す。
上に挙げた Counter
クラスを再掲すると、以下のようになる:
class Counter: def __init__(self, count): # 実際に渡す引数: countのみ self.count = count def inc(self, n): # 実際に渡す引数: nのみ self.count += n return self.count c = Counter(0) # self=(新しいインスタンス), count=0 print(c.inc(1)) # self=c, n=1
new
演算子は存在しない。
あるクラスのインスタンスを作成するには、
そのクラス名 (クラスオブジェクト) を関数のようにして呼ぶだけでよい。
ここで渡した引数はコンストラクタ (__init__
メソッド) の
引数として渡される:
# インスタンスの作成 c = Counter(0) # Counter.__init__ が呼ばれる。
メソッド呼び出しは Java などの言語と似ているが、 Python ではメソッドとは、単にクラスに所属し、 あらかじめ第1引数が束縛されている関数オブジェクトにすぎない。 以下の3つの関数 (メソッド) 呼び出しはどれも同じ効果をもつ:
print(c.inc(1)) # 通常のメソッド呼び出し func = c.inc # 関数オブジェクトを取り出す print(func(1)) print(Counter.inc(c, 1)) # 直接呼び出し (非推奨)
self
が束縛されたクロージャである。
class
文で
基底クラスを (…)
で囲んで表示する。
ここでは上の Counter
クラスを継承し、__init__
と
inc
メソッドを上書きした DoubleCounter
クラスを定義している:
# Counterを継承したDoubleCounterクラスの定義 class DoubleCounter(Counter): def __init__(self, count): # 基底クラスのメソッドを呼び出す。 super().__init__(count*2) # 基底クラスの inc() を上書きする。 def inc(self, n): self.count += n*2 return self.count
class
文は、
必ず派生クラスの定義よりも前に実行されている必要がある。
さもないと NameError
が発生する。
基底クラスのメソッドを呼ぶには、
super()
関数を使う。
こうするとクラスが多重継承である場合に、
基底クラスのメソッドを正しい呼び出し順序 (MRO) で実行できる。
書式 | 説明 | 例 |
---|---|---|
type(x) |
インスタンス x が所属するクラスを返す。 | if type(c) == Counter: c.inc(1) |
isinstance(x, t) |
インスタンス x がクラス t (あるいはその派生クラス) に 所属していれば真。 | if isinstance(c, Counter): c.inc(1) |
issubclass(s, t) |
クラス s がクラス t の派生クラスであれば真。 | assert issubclass(DoubleCounter, Counter) |
実は Python では「型 (type)」と「クラス (class)」はほぼ同義である。
int
型や str
型などは内部的に
クラスとして扱われており、実際、int
や str
といった
名前はクラスオブジェクト (type
オブジェクト) が
入っている変数にすぎない。(int()
や str()
関数は、
実際にはコンストラクタである。)
したがって、上の3つの関数は実際にはクラスだけでなく、
あらゆる値の型検査に使える:
if isinstance(x, int):
# x が int型のときに実行。
print(x+1)
書式 | 説明 | 例 |
---|---|---|
__repr__(self) |
インスタンスの「正式な」文字表現を返す。
このインスタンスに対して repr() 関数が
実行されたとき、このメソッドの返り値が使われる。 |
def __repr__(self): return f'<count={self.count}>' |
__str__(self) |
インスタンスの「表示用の」文字表現を返す。
このインスタンスに対して str() 関数が
実行されたとき、このメソッドの返り値が使われる。 |
def __str__(self): return f'<{self.count}>' |
__len__(self) |
インスタンスの「サイズ」を返す。
このインスタンスに対して len() 関数が
実行されたとき、このメソッドの返り値が使われる。
リストや文字列のようなふるまいをインスタンスに持たせるときに使う。 |
def __len__(self): return 1 |
__getitem__(self, k) |
インスタンス x に対して、添え字による参照
x[k] がおこなわれたときに、
このメソッドの返り値が使われる。
辞書のようなふるまいをインスタンスに持たせるときに使う。 |
def __getitem__(self, k): return k * 2 |
__setitem__(self, k, v) |
インスタンス x に対して、添え字による代入
x[k] = v がおこなわれたときに、
このメソッドが呼ばれる。 |
def __setitem__(self, k, v): print(f'set {k} to {v}') |
__call__(self, arg1, …) |
インスタンス x に対して関数呼び出し
x(arg1, …) がおこなわれたときに、
このメソッドが呼ばれる。
インスタンスに関数オブジェクトのようなふるまいを持たせるときに使う。 |
def __call__(self, x): return x*2 |
__iter__(self) |
インスタンス x に対して (for 文などで)
イテレータが要求されたときに、このメソッドの返り値が使われる。
ジェネレータ関数であってもよい。
|
def __iter__(self): yield 1 yield 2 yield 3 |
__eq__(self, x) |
このインスタンスが == 演算子で
他のオブジェクト (同じクラスとは限らない) と比較されたときに
このメソッドが返り値が使われる。
|
def __eq__(self, c): return (isinstance(c, Counter) and self.count == c.count) |
以下のクラス Parser
のメソッドを完成させよ。
これはカンマ (,
) で区切られた要素列を文字列のリストに
変換するパーザで、"〜"内に囲まれた部分ではカンマは
区切り文字として認識されない (エスケープされる)。
.feed()
メソッドは、文字を 1文字ずつ入力し
内部状態を変化させ、
.get()
メソッドで最終的な結果を取得するものとする。
# カンマで区切られた要素列をリストに変換する。 class Parser: def __init__(self): self.result = [] # 最終的なリスト。 self.inquote = '' # "〜"内部ならばTrue。 self.buf = '' # 構築中の部分文字列。 # 1文字を入力、内部状態を変化させる。 def feed(self, c): ... return # 解析結果を受けとる。 def get(self): ... return self.result s = 'abc,"de"f,"p,qr"' # パーザを作成。 p = Parser() # 1文字ずつ解析させる。 for c in s: p.feed(c) # 結果を取得する。 print(p.get()) # ['abc', 'def', 'p,qr']
Python では基本的な (ストリームによる) ファイル操作が標準でサポートされている。
# テキストファイルを開く。 fp = open('path/to/file') # 1行読み込む。 line = fp.readline() # ファイルを閉じる (非推奨)。 fp.close()
Python では open()
関数によりファイルを開く。
このときファイルオブジェクトが返されるので、それに対して
読み書きの操作をおこなう。ファイルを閉じるには、
close()
メソッドを実行する。
open()
はファイルを開くモード (後述) や
バッファの有無によって異なった型のオブジェクトを返すが、
この差をユーザが意識する必要はほとんどない。
ここではひとまとめにファイルオブジェクトと呼ぶことにする。
実は、上に示したやり方は現在は推奨されない。
なんらかの原因で fp.close()
が実行されない場合
OS上のファイル記述子がリークしてしまうので、
現在では以下のような with
文を使った方法が推奨されている:
# テキストファイルを開く。 with open('path/to/file') as fp: # 1行読み込む。 line = fp.readline() # withから抜けた時点で、自動的にファイルが閉じられる。
(with文の動作の詳細については Pythonリファレンスマニュアルの 3.3.9. With Statement Context Managers を参照)
open()
関数の引数にはさまざまなオプションがある。
代表的なものを以下に紹介する:
書式 | 説明 | 例 |
---|---|---|
open(path) |
テキストファイル path (UTF-8エンコーディング) を読み込みモードで開く。 | fp = open('/etc/motd') |
open(path, encoding=e) |
テキストファイル path (エンコーディング e) を 読み込みモードで開く。 | fp = open('readme.txt', encoding='cp932') |
open(path, 'w') |
テキストファイル path を書き込みモードで開く (ファイルの内容はクリアされる)。 | fp = open('/tmp/foo', 'w') |
open(path, 'a') |
テキストファイル path を追記モードで開く (ファイルがない場合は作成される)。 | fp = open('out.log', 'a') |
open(path, 'rb') |
バイナリファイル path を読み込みモードで開く。 | fp = open('foo.jpg', 'rb') |
open(path, 'wb') |
バイナリファイル path を書き込みモードで開く (ファイルの内容はクリアされる)。 | fp = open('dump.dat', 'wb') |
open(path, 'rb+') |
バイナリファイル path を読み書き込みモードで開く (ファイルがない場合は作成される)。 | fp = open('update.db', 'wb+') |
\r
), LF (\n
), CR+LF (\r\n
)
のどれであっても 1行として認識され、改行文字は
自動的に str型の '\n'
に変換される。
テキストファイルからデータを読み込む・書き込むには、 以下の方法が用意されている:
書式 | 説明 | 例 |
---|---|---|
fp.read([n]) |
ファイル fp から n文字を読み込む。 nが省略された場合はファイルの終わりまで読み込む。 | print(fp.read()) |
fp.readline() |
ファイル fp から1行分 (改行まで) の文字列を読み込む。 | line = fp.readline() |
iter(fp) |
ファイル fp の各行を文字列のイテレータとして返す。 |
# iter() は省略可能
for line in fp:
print(line)
|
fp.write(s) |
ファイル fp に文字列 s を書き込む。 | fp.write('howdy!\n') |
bytes
型を使う。
bytes
型は見た目・機能ともstr
型に似ているが、
各要素は Unicode文字ではなく、0〜255の範囲をもつ整数である。
Bytes型の定数は b'〜'
のように
先頭に b をつけて表現する:
b'abc' b'\x0d\x0a'
文字列とは異なり、bytes型の各要素は「長さ1のbytes
型」ではなく
int型である。
ただし、bytes
型のスライスはbytes型である:
また、文字列と同じくbytes
列は読み込み専用である。
つまり各要素を取り出すことはできるが、変更することはできない。
b = b'ABC' print(b[1]) # 66 print(b[1:2]) # b'B' b[1] = 100 # TypeError が発生
str
型をバイナリファイルとして読み込み・書き込みをする場合、
明示的にbytes
型と型変換しなければならないので注意が必要である。
bytes型→str型への変換 (デコード) には decode()
メソッドを、
str型→bytes型への変換 (エンコード) には encode()
メソッドを使う:
b = fp.read(3) # b'ABC' s = b.decode('utf-8') # bytes型→str型に変換 (UTF-8) print(s) # 'ABC' s = 'あ' b = s.encode('cp932') # str型→bytes型に変換 (Shift-JIS) fp.write(b) # b'\x82\xa0'
バイナリファイルからデータを読み込む・書き込むには、 以下の方法が用意されている:
書式 | 説明 | 例 |
---|---|---|
fp.read([n]) |
ファイル fp から nバイトを読み込む。 nが省略された場合はファイルの終わりまで読み込む。 | b = fp.read(4) |
fp.seek(offset[, whence=0]) |
ファイル fp の特定の位置 offset から読み込みを開始する。
whence が 0 の場合、offset は絶対位置をあらわし、
1 の場合は相対位置、2 の場合はファイル末尾からの相対位置をあわわす。 |
fp.seek(4096) |
fp.tell() |
ファイル fp の現在位置を返す。 | pos = fp.tell() |
fp.write(b) |
ファイル fp にバイト列 b を書き込む。 | fp.write('よう'.encode('utf-8')) |
Python におけるモジュールとは、簡単にいえば 別のプログラムから利用できる Python スクリプト (ファイル) のことである。 ただし Python においてはモジュールは単なるファイルではなく、 実際に値を参照したり代入したりできるオブジェクトとして扱われる。 なお、Python では「モジュール」と「ライブラリ」はほぼ同じ意味である。
本節ではまずユーザがモジュールを定義・利用する方法を説明する。 つぎに Python でデータ処理をする際に よく使われるモジュールの使用例を紹介する。
PYTHONPATH
+ システム標準」が使われる。
システム標準は Python のディストリビューションによって異なっているが、
pip
あるいは conda
などでモジュールを
インストールした際には必ずシステム標準のパスで発見できるように
なっている。
以下は Python によるモジュールの例である:
num = 1729 def avg(x, y): a = (x+y)/2 return a class Counter: def __init__(self, count): self.count = count def inc(self, n): self.count += n return self.count
このモジュールでは、変数num
、
関数avg
、およびクラスCounter
が定義されている。
Python では、関数定義・クラス定義は
どちらも変数へのオブジェクトの代入であるので、
実際には上の foo
モジュールは
3つの属性 (foo.num
、foo.avg
および
foo.Counter
) をもつオブジェクトとみなせる。
foo.num
は foo
というインスタンスの属性かもしれないし、
foo
というモジュールで定義された変数かもしれない。
Python においては、どちらも特定の名前空間の属性なのである。
モジュールを利用するには、import
文を使う。
Pythonインタプリタは、実行するスクリプトと同じディレクトリ
(あるいは環境変数 PYTHONPATH
で指定されているディレクトリ)
上にある foo.py
を読み込み、def
文や class
文などを
実際に実行して関数・クラスを定義する:
import foo # def, class などが実行される
print(foo.num)
print(foo.avg(3, 5))
c = foo.Counter(1)
実際には、import
文もまた
変数への「モジュールオブジェクト」の代入とみなせる。
import
文には以下の4種類の表記があり、
モジュール内のオブジェクトをどのように参照するかによって使いわける:
import foo # foo = <モジュールfoo> import foo as bar # bar = <モジュールfoo> from foo import num # num = <モジュールfoo>.num from foo import num as spam # spam = <モジュールfoo>.num
モジュール内のすべてのオブジェクトを現在の名前空間に取り込むには、
以下のように *
表記を使う (この方法は推奨されていない):
from foo import * # num, avg, Counter が定義される
さらに、モジュールはディレクトリ (フォルダ) によって階層化できる。
複数のモジュールをまとめたモジュールをパッケージ (package) と呼ぶ。
たとえば、あるディレクトリ mypkg
をパッケージとして利用するには、
以下のようにファイルを配置する:
|
ディレクトリを Python パッケージ (モジュール) として認識させるためには、
必ず __init__.py
ファイルが必要である
(内容は空でもよい)。__init__.py
ファイル内で
変数・関数・クラスを定義した場合、それらは
mypkg
モジュールの属性として利用できる。
パッケージもモジュールであるので、普通に import
文で利用できる:
# mypkg.foo として参照する場合 import mypkg.foo print(mypkg.foo.num) # fooを直接参照したい場合 from mypkg import foo print(foo.num)
通常、Python のモジュールに含まれる文は基本的に代入文
(変数への代入や、def
文および class
文) だけである。
なぜならモジュールの中身は import
時にスクリプトとして
実際に評価 (実行) されるため、たとえば print()
などを書いておくと
import
時に余計な副作用が発生してしまうためである。
ただし、(テストなどの目的で) ときにモジュール用の Python スクリプトを単体でも実行したいときがある。 このような場合は、以下のようなトリックを使う:
num = ...
def avg(x, y): ...
class Counter: ...
if __name__ == '__main__':
# foo.py を直接実行した場合。
print('Hello')
変数 __name__
は現在 import
している
モジュール名をあらわす特殊変数で、スクリプトが単体で実行された場合、
この値はつねに '__main__'
となるため
if
文の中が実行される:
C:\Users\euske> python foo.py Hello # printが実行される。 C:\Users\euske> python >>> import foo >>> # 何も表示されない。
Python では「Battery included (電池つき)」の考え方により、 多くのモジュールが標準搭載されている。その分野は ファイル処理からネットワーク処理、HTMLや電子メールの解析から 端末制御や共有ライブラリ中の C APIの呼び出しまでさまざまである:
これらの標準ライブラリのコードは、Windows の場合
C:\Users\ユーザ名\AppData\Local\Programs\Python3.9\Lib
以下に、macOS/Linux の場合は (通常)
/usr/lib/python3.9
以下に格納されている。
以下によく使われるモジュールを挙げる:
sys
(コマンドライン引数、標準入力・標準出力)
os
(ファイル操作)
os.path
(パス名操作)
math
(sqrt, sin, exp などの関数)
re
(正規表現)
datetime
(日付と時刻の操作)
socket
(低レベルネットワーク処理)
urllib
(HTTP/HTTPS通信)
subprocess
(外部プロセスの起動)
random
(乱数の発生)
Python でコマンドラインからの引数を解析するには、
sys
モジュールを
使うのが一般的である。sys.argv
にはプロセス起動時の
各引数 (C でいう char** argv
) が
str
型のリストとして格納されており、
sys.argv[0]
はコマンド名に相当する:
import sys
if __name__ == '__main__':
args = sys.argv[1:]
# ...argsを使った処理...
さらに、
argparse.ArgumentParser
クラス を使うと、
引数およびオプションの解析を自動化できる:
if __name__ == '__main__': from argparse import ArgumentParser parser = ArgumentParser() # 有効なオプションを追加。 parser.add_argument('-f', '--file', dest='filename', help='file name to process', metavar='FILE') parser.add_argument('-v', '--verbose', action='store_true', dest='verbose', default=False, help='print debug messages') # 解析する。 args = parser.parse_args() # 例. オプションの値を使用する。 print(args.filename) # ファイル名 (デフォルトは None) print(args.verbose) # verboseフラグ (デフォルトは False)
Python で CSVファイルを読み書きするには、
csv
モジュールを使う。
ここでは Excel が出力する形式の CSVファイルをサポートしている。
読み込みには csv.reader()
を使い、
CSV の各行を返すイテレータを取得する:
import csv # Shift-JIS (cp932) の CSVファイルを開く。 with open('input.csv', encoding='cp932') as fp: # CSVの各行は strのリストとして表される。 for row in csv.reader(fp): print(row)
書き込みには csv.writer()
を使う。
ここで返される writer
オブジェクトに対して
writerow()
メソッドを呼び、
1行ずつ出力する:
import csv # UTF-8 の CSVファイルを開く。 with open('output.csv', 'w') as fp: writer = csv.writer(fp) # 1行ずつ出力する。 for row in rows: writer.writerow(row)
Python で JSONファイルを読み書きするには、
json
モジュールを使う。
JSON の型は Python に近いため、
JSON オブジェクトは容易に Python の辞書やリストとして扱うことができる。
import json # 文字列から読み込む場合。 text = '{"foo": "bar", "baz": [1, 2, 3]}' obj = json.loads(text) print(obj) # {'foo': 'bar', 'baz': [1, 2, 3]} # ファイルから読み込む場合。 with open('input.json') as fp: obj = json.load(fp)
以下は JSON 形式を出力する例である:
import json # 文字列として出力する場合。 obj = {'foo': 'bar', 'baz': [1, 2, 3]} print(json.dumps(obj)) # ファイルに出力する場合。 with open('output.json') as fp: json.dump(obj, fp)
Zipファイルの読み書きには、
zipfile
モジュールを使う。
最初に ZipFile
インスタンスを作成すると、
各ファイルに対するメタデータである ZipInfo
のリストが取得できる。
次にこれを .open()
メソッドに渡し、
各ファイルの内容を取得する。
なお zip に格納されたファイルはすべてバイナリファイルとして扱われる。
import zipfile # zipファイルを開く。 with zipfile.ZipFile('input.zip') as zfp: # ファイル一覧を取得。 for info in zfp.infolist(): # 各ファイルを開く。 print(info.filename) with zfp.open(info) as fp: # ファイルの中身 (バイナリ) を読む。 data = fp.read()
Python で XML を処理するにはいくつかの方法があるが、
もっとも手軽なのは xml.etree.ElementTree
モジュールを使う方法である。
たとえば、以下のような XML ファイルを読み込むことを考える:
<?xml version="1.0"?> <group> <person id="1">john</person> <person id="2">mary</person> </group>
このような XML を処理するプログラムは以下のようになる:
from xml.etree.ElementTree import XML with open('input.xml') as fp: # ファイルからXML文書を構築。 root = XML(fp.read()) # トップレベルのタグは <group> のはず。 assert root.tag == 'group' # 子要素の一覧を取得する。 for elem in root: # <person>タグであれば、属性idおよびテキスト部分を取得。 if elem.tag == 'person': print(elem.get('id')) print(elem.text)
Python で画像ファイルを扱うモジュールは多数存在するが、
ここでは広く使われている
Pillow という
モジュールを使う。
Pillow はデフォルトでは含まれていないため、
pip
を使ってインストールする:
C:\Users\euske> pip install pillow Collecting pillow Downloading Pillow-9.0.0-cp39-cp39-win_amd64.whl (3.2 MB) |████████████████████████████████| 3.2 MB 2.2 MB/s Installing collected packages: pillow Successfully installed pillow-9.0.0
Pillow は標準で PNG, JPEG, TIFF などの画像ファイル形式をサポートしている。 以下のようにすると、現在サポートするファイル形式一覧が表示される:
C:\Users\euske> python -m pip
Pillow を使った典型的な画像の読み込み、情報の取得、切り取り、
リサイズ、保存をする一連のプロセスは以下のようになる。
(なお、Pillow はもともと PIL (Python Imaging Library) の
互換モジュールとして開発されたため、モジュール名は PIL
となっている。)
from PIL import Image # JPEG画像ファイルを読み込む。 img = Image.open('input.jpg') # 幅・高さ・形式を取得する。 print(img.width, img.height, img.mode) # 画像の切り取り。 img = img.crop((100, 200, 100, 100)) # 画像のリサイズ。 img = img.resize((50, 50)) # 画像を保存する。 img.save('output.jpg')
Pillow の画像オブジェクトは PIL.Image.Image
インスタンスとして
表現されるが、ユーザがこのインスタンスを直接作成することはなく、
通常は Image.open
などの関数を使って作成する。
また、切り取りやリサイズ等の操作ではもとの画像は変化せず、
つねに新しい画像が返される。
さらに Pillow の画像オブジェクトは、後で紹介する NumPy の
ndarray
型と
簡単に画像データを相互変換できる。
(注意: あとで説明するように、
Pillow の画像は (高さ × 幅 × チャンネル数)
という並びで表現されているため、
PyTorch等で利用するには (チャンネル数 × 高さ × 幅)
という並びに変える必要がある。)
from PIL import Image import numpy as np # ndarray配列 (10×10×3) から 10×10 のRGB画像を作成する。 a = np.array.zeros((10, 10, 3)) img = Image.fromarray(a) # RGB画像を (高さ×幅×3) の ndarray配列に変換する。 img = Image.open('input.jpg') a = np.array(img)
画像オブジェクトに対するおもな操作を以下に示す:
書式 | 説明 | 例 |
---|---|---|
Image.open(path) |
画像ファイル path を読み込み、画像オブジェクトを返す。 | img = Image.open('input.jpg') |
img.save(path) |
画像 img をファイル path に保存する。 ファイル形式は拡張子に応じて決められる。 | out.save('output.png') |
img.copy() |
複製した画像を返す。 | img2 = img.copy() |
img.resize((w, h)) |
画像 img を w×h ピクセルにリサイズした画像を返す。 | img = img.resize((img.width//2, img.height//2)) |
img.crop((x0, y0, x1, y1)) |
画像 img の一部を (x0, y0, x1, y1) の矩形で切り取った画像を返す。 | img = img.crop((0, 0, 100, 100)) |
dst.paste(src, (x0, y0, x1, y1)) |
画像 src を画像 dst 内の位置 (x0,y0,x1,y1) に描画する。 | out.paste(img, (x,y,x+img.width,y+img.height)) |
img.thumbnail((w, h)) |
画像 img を w×h ピクセルに入るように変更する。このとき、画像の縦横比は保存される。 | img.thumbnail((100, 100)) |
img.width |
画像の幅を返す。 | |
img.height |
画像の高さを返す。 | |
img.mode |
画像のピクセル形式を文字列
('RGB' , 'RGBA' など) で返す。
(一覧)
|