post / 2023.06.27
Pythonの基礎
本記事は以前の4つのPythonノートを統合し、重複した内容や無関係な内容を削除して、日常的なクイックリファレンスとして使えるようにしたものです。サンプルはPython 3に基づいています。
変数とオブジェクト
- 変数は型を宣言する必要はありませんが、使用前に必ず値を代入する必要があります。そうでなければ
NameErrorが発生します。 - Pythonには
constはなく、変数の再代入を防ぐ仕組みもありません。いわゆる「定数」は全大文字で命名するという約束にすぎません。 =は代入、==は比較です。
>>> age
NameError: name 'age' is not defined
オブジェクト指向的な理解:オブジェクトはデータ実体であり、変数はオブジェクトに貼られたラベルです。
a = "Jack" # 文字列オブジェクト "Jack" を生成し、ラベル a を貼る
b = a # ラベル b を同じオブジェクトに貼る(コピーではない)
id(a) == id(b) # True、両者は同じオブジェクトを指す
a = "Ross" # 別のオブジェクトにラベル a を貼り直す。b は依然として "Jack" を指す
- 1つのオブジェクトには複数のラベルを貼れますが、1つの変数が同時に指せるオブジェクトは1つだけです。
- 変数自体に型はなく、型はオブジェクトに属します。したがって
a = 1の後にa = "1"としても完全に合法です。
代入とスライス
= は右結合であり、a = b = c = 1 は3つとも整数オブジェクト 1 を指すことを意味します。
a, b, c = 1, 2, 3 # それぞれに代入
age = 17 + 1 # 先に右辺を計算してから代入
スライスは文字列、タプル、リストに使え、構文は obj[a:b:c] です:
| 書き方 | 意味 |
|---|---|
obj[a:b] | インデックス $[a, b)$ を取得(b は含まない)。a を省略すると 0 から、b を省略すると末尾まで |
obj[a:b:c] | c 個ごとに1つ取得。c を省略した場合は 1 |
c が負の値 | 後ろから前に向かって逆順に取得 |
obj[::-1] | 反転(新しいオブジェクトを返し、元のオブジェクトは変わらない) |
>>> name = "polaris"
>>> name[1:3]
'ol'
>>> name[::-3]
'sap'
>>> name[::-1]
'siralop'
スライスはコピーにも使えます。y = x[:] で浅いコピーが得られ、その後 y をソートしても x には影響しません。
文字列
- シングルクォートとダブルクォートは等価で、単一文字と文字列を区別しません(char 型はありません)。
- 三重クォートで複数行の文字列を書けます。
- クォート内で同じ種類のクォートを使う場合は
\"でエスケープが必要です。
3つのフォーマット方法
name, age = "布洛妮娅", 18
print('My wife is %s, she is %d.' % (name, age)) # % 方式、最も古い
print('My wife is {}, she is {}.'.format(name, age)) # format メソッド
print(f'My wife is {name}, she is {age}.') # f-string、推奨
%方式は後ろに tuple または dict を続け、順番または名前で値を取得します。formatの{}内では添字{0}やキーワード{name}を使えます。- f-string には Python 3.6 以上が必要で、可読性が最も優れています。3つとも幅、配置、精度などの書式制御に対応しています。
数値とブール
| 型 | 説明 |
|---|---|
int | 整数。0x 16進数、0o 8進数、0b 2進数に対応 |
float | 浮動小数点数。科学表記 1.23e-6 に対応 |
complex | 複素数。a + bj または complex(a, b) と書く |
bool | ブール値。True は本質的に 1、False は本質的に 0 |
NoneType | 空値。None の先頭は大文字でなければならない |
- 整数と浮動小数点数の混合演算では、整数が浮動小数点数に昇格します。
1 + 2.0は3.0になります。 - 浮動小数点数には精度誤差があります。
0.1 + 0.2 == 0.3の結果はFalseです(実際には0.30000000000000004)。正確な計算が必要な場合はdecimalモジュールを使います。 round()は銀行家の丸め(最近接偶数への丸め)を採用しています。round(0.5)は0、round(1.5)は2、round(2.5)は2になります。
偽と判定される値:0、0.0、None、空文字列、空リスト、空タプル、空辞書、空集合、False。
真と判定される値:それ以外すべてです。空でない文字列も含みます(内容が "False" であっても真です)。
演算子
算術演算子
| 演算子 | 意味 | 例 |
|---|---|---|
+ - * | 加減乗 | |
/ | 除算。結果は必ず浮動小数点数 | 6 / 3 → 2.0 |
// | 整数除算。切り捨て | 7 // 2 → 3、-7 // 2 → -4 |
% | 剰余(モジュロ) | 7 % 2 → 1 |
** | べき乗。右結合 | 2 ** 2 ** 3 → 256 |
- | 単項マイナス。優先順位は ** より低い | -2 ** 2 → -4 |
その他の演算子
| 分類 | 演算子 |
|---|---|
| 比較 | == != < <= > >=。連結して書ける:a > b == c は a > b and b == c と等価 |
| 論理 | and or not |
| メンバー | in not in |
| 同一性 | is is not。同じオブジェクトを参照しているかを比較(値を比較するのではなく id を比較) |
| ビット演算 | & | ^ ~ << >> |
| 複合代入 | += -= *= //= **= &= |= ^= <<= >>= |
and / or が返すのは True / False ではなく、オペランドそのものです:
>>> True and 0 + 3
3
>>> False or 31
31
>>> 0 or 0
0
優先順位(高い順)
() → インデックス [] → 属性 . → ** → 単項 ~ + - → * / % // → + - → << >> → & → ^ → | → 比較 → 代入 → is / in → not → and → or
覚えられなければ括弧を付けるのが最も低コストです。
入出力と組み込み関数
print(self, *args, sep=' ', end='\n', file=None) # sep は区切り文字、end は終端文字
age = input("请输入年龄:") # 戻り値は常に文字列
int(age) # 数値が必要なときは明示的に変換
よく使う組み込み関数:
| 関数 | 役割 |
|---|---|
id(x) | オブジェクトのメモリアドレス(同一性の識別子) |
type(x) | オブジェクトの型。例:<class 'int'> |
len(x) | 要素数 |
abs() round() bool() | 絶対値、丸め、ブール変換 |
enumerate(seq, start=0) | ループ時にインデックスと値を同時に取得 |
range() zip() sum() max() min() sorted() | よく使うシーケンス操作 |
型変換関数:int(x, base)、float()、complex()、str()、repr()、chr()、ord()、hex()、oct()、eval()、list()、tuple()、set()、frozenset()、dict()。
変換は無条件にはできません。元のデータが対象の型を表現するのに十分な情報を含むかどうかによります。int("123") は可能ですが、int("Hello") はエラーになります。リストは集合に変換できますが、1つの数値に変換することはできません。
暗黙の型変換は精度の順序 bool < int < float < complex に従い、混合演算ではより精度の高い方に合わせられます。
galgames = ['咖啡馆', '千恋万花', '天使骚骚']
for index, galgame in enumerate(galgames, 1):
print(f'今天玩第{index}部作品{galgame}。')
条件分岐とループ
if condition1:
...
elif condition2:
...
else:
...
for は任意のイテラブル(リスト、辞書、文字列、タプル、ジェネレータ……)の走査に使います:
for x in 序列:
...
for i in range(5):
...
break:現在の階層のループから抜け出します。continue:今回の残りをスキップして次のループに進みます。for ... else/while ... else:ループが正常に終了したときelseを実行します。breakで中断された場合は実行されません。
while 判断条件:
...
while を書くときは、ループ本体が最終的に条件を変化させるか break を発生させることを必ず確認してください。そうでなければ無限ループになります。
4つのコンテナ
| コンテナ | リテラル | 順序あり | 可変 | 要素の重複 | 要素の条件 |
|---|---|---|---|---|---|
リスト list | [1, 2] | あり | あり | 可 | 任意 |
タプル tuple | (1, 2) | あり | なし | 可 | 任意 |
辞書 dict | {k: v} | 挿入順 | あり | キーは重複不可 | キーはハッシュ可能でなければならない |
集合 set | {1, 2} | なし | あり | 不可(自動で重複排除) | ハッシュ可能でなければならない |
list
a = [] # 空のリスト
a.append(x) # 末尾に追加
a.insert(i, x) # インデックス i に挿入し、元の要素は後ろにずれる
a.extend(b) # b の要素を1つずつ a の後ろに連結
a.pop() # 末尾の要素を取り出す(その要素を返す)
a.pop(i) # インデックス i の要素を取り出す
a.remove(x) # 値が x である最初の要素を削除。存在しない場合はエラー
a.index(x) # 値が x である最初の要素のインデックス
a.count(x) # 値が x である要素の個数
a.reverse() # その場で反転
a.sort(key=..., reverse=False) # その場でソート。戻り値なし
del a[i] # インデックス i を削除
del a[1:3] # スライス範囲を削除
ソートの例:
random = [(2, 2), (3, 4), (4, 1), (1, 3)]
random.sort(key=lambda e: e[1]) # 各タプルの2番目の要素でソート
# [(4, 1), (2, 2), (1, 3), (3, 4)]
注意:
sort()の引数はkeyとreverseの2つだけです。cmp引数は Python 3 で削除されました。
tuple
- タプルは生成後に変更できません。追加・削除・変更はできません。
- 要素が1つのタプルにはカンマが必要です。
(1,)はタプルですが、(1)は単なる整数です。 - 変換:
list(t)でリスト、tuple(l)でタプルが得られます。元のオブジェクトは変わらず、返されるのは新しいオブジェクトです。
dict
profile = {'name': '孤筝', 'age': 19}
profile2 = dict(name='孤筝', age=19) # 等号を使う場合、キーにクォートは付けない
profile3 = dict([('name', '孤筝'), ('age', 19)]) # キーと値のペアのシーケンスから生成
profile4 = {i: i ** 2 for i in range(2, 5)} # {2: 4, 3: 9, 4: 16}
値の取得と追加・変更:
profile['name'] # キーが存在しない場合は KeyError を送出
profile.get('gender') # 存在しない場合は None を返す
profile.get('gender', 'male') # 存在しない場合はデフォルト値を返す
profile.setdefault('gender', 'male') # 存在しない場合はそのキーと値のペアを書き込んで返す
profile['gender'] = 'male' # 追加または変更
del profile['age']
profile.pop('age')
重要な違い:
get()は読み取り専用で、辞書を変更しません。キーが存在しないときに書き込むのはsetdefault()だけです。
キーの存在確認には in / not in を使います。has_key() は Python 2 の書き方で、Python 3 では削除されました。
set
s = {1, 2, 2, 3} # 自動的に重複排除されて {1, 2, 3} になる
s = set() # 空の集合はこのようにしか書けない。{} は空の辞書
| 操作 | メソッド | 演算子 |
|---|---|---|
| 和集合 | a.union(b) | a | b |
| 積集合 | a.intersection(b) | a & b |
| 差集合(a にあって b にない) | a.difference(b) | a - b |
| 対称差(どちらか一方にのみ属する) | a.symmetric_difference(b) | a ^ b |
- 追加:
add(x)(不変型しか追加できない)、update(seq)(文字列、リスト、タプル、集合を渡せる。辞書を渡した場合はキーだけが取り込まれる)。 - 削除:
remove(x)は要素が存在しないとエラーになります。discard(x)はエラーになりません。pop()は任意の要素を削除して返しますが、引数は渡せません。clear()はすべて削除します。 - 判定:
x in s、a.isdisjoint(b)(共通要素がなければ True)、b.issubset(a)。 - 集合は順序もインデックスもありません。インデックスで値を取得することも、要素を変更することもできず、追加と削除だけが可能です。
よくある誤解:
pop()が削除するのは「任意の」要素であり、「ランダムな」要素ではありません。集合の格納順序はハッシュ値で決まるためランダムに見えますが、制御はできません。
イテレータとジェネレータ
イテラブルオブジェクト:for で走査できるオブジェクト。以下のいずれかを満たせば該当します:
__iter__()を実装し、イテレータのインスタンスを返す。__iter__()はないが__getitem__()を実装しており、インデックスによる要素の取得が可能。
イテレータオブジェクト:iter(イテラブルオブジェクト) の戻り値で、next() で1つずつ値を取得します。すべて取り出すと StopIteration が送出されます。
from collections.abc import Iterable, Iterator
isinstance([], Iterable) # True、リストはイテラブルオブジェクト
isinstance([], Iterator) # False、リスト自体はイテレータではない
isinstance(iter([]), Iterator) # True
つまり、リストはイテラブルオブジェクトであり、イテレータではありません。リストは何度でも走査できますが、イテレータは一度前に進むだけです。
ジェネレータは「関数で書いたイテレータ」です:
# リスト内包表記でリストを得る
alist = [i for i in range(5)] # [0, 1, 2, 3, 4]
# 丸括弧に変えるとジェネレータになる
agen = (i for i in range(5)) # <generator object ...>、遅延評価
注意:
(i for i in ...)はジェネレータ式であり、タプル内包表記ではありません。Python にタプル内包表記は存在しません。タプルを得たい場合はtuple(i for i in ...)と書きます。
yield と return の違い:関数の実行が yield に達すると一時停止して値を渡しますが、関数は終了しません。次に値を取得すると、一時停止した場所から関数が終了するまで再開します。yield に値が続かない場合は None を返します。
def generator():
i = 0
while i < 5:
i += 1
yield i
gen = generator()
next(gen) # 1
for v in generator():
print(v) # 1 2 3 4 5
ジェネレータの価値は遅延評価にあります。すべての結果を一度に計算してメモリに置くことをせず、大量のデータストリームを処理する際にリストよりメモリを節約できます。
その他のノート
コメント
# 単一行コメント
'''
三重クォートは厳密には「複数行文字列」であり、コメントではありません。
インタプリタによって文字列オブジェクトとして生成されるだけで、使われていない式にすぎません。
'''
本質がコメントではなく文字列であるため、コードの途中に三重クォートを気軽に挿入すると構文が壊れ、そのままエラーになることがあります。正しい書き方は # だけでコメントを書くことです。
エンコーディング
- Python 3 のソースファイルはデフォルトで UTF-8 なので、通常はエンコーディングの宣言は不要です。
- ファイル先頭での宣言が必要なのは Python 2 だけで、現在はほとんど使いません:
# -*- coding: UTF-8 -*-
識別子
- 英字、数字、アンダースコアで構成され、数字で始めることはできません。大文字と小文字は区別されます。
- Python 3 では中国語などの文字で命名することもできますが、推奨されません。
- アンダースコアの特殊な約束:
_name:アンダースコア1つで始まり、「内部使用」を意味します。from module import *ではインポートされません。__age:アンダースコア2つで始まり、名前マングリング(name mangling)が働きます。サブクラスとの衝突を避けるために使います。__init__:アンダースコア2つで始まり2つで終わるものは Python の組み込み特殊メソッドです。自分でこのような名前を作らないでください。
スポンサー / タクシーで家まで六十七十ドルかかります!


いつまた一杯の酒を飲み、細かい論文を議論するのか。