Python Tutorial の学習メモ
形式ばらないPythonの紹介
その他
コメントアウト
一行の場合、先頭に#
複数行の場合、三連引用符で囲む
除算
/は常にfloatを返却し、//は小数部を切捨て、intを返却
冪乗
**, 優先度が高い。-3**2 = -(3**2) = 9, (-3)**2 = 9
便利な_
対話モード時、_は前回の返却値のalias
その他
- 複数同時代入が可能、右辺の式をすべて左から右へ評価した後、左辺へ代入する
- ループのbody部は、インデント(字下げ)が必要、しかも同じ下げ方と幅じゃないと
-
whileやifの後ろの判定式を括弧で括る習慣はない
文字列(str)
文字列リテラルは、'...'か"..."で囲む。結果は同じで、エスケープした特殊文字は機能する。
エスケープ文字を機能させたくない場合、最初の引用符の前にrを付ける(raw string)。正規表現やバス指定のなどでバックスラッシュを多用する文字列を扱うとき、とりあえずこれだ。
複数行をまたがっている文字列リテラルは、三連引用符"""..."""か'''...'''を使用する。行末の改行が文字列に含まれる。改行を無視したい場合、その行の行末に\を付ける。これはPythonのhere documentで、format()と一緒に使用すれば、変数展開ができる。
+で連結できる。*で反復
並んでいる文字列___リテラル___は、自動的に連結する(変数や式不可)。長い文字列を改行したいとき便利。
インデックスで文字取得できる、長さ1の文字。マイナス指定できる。文字列は不変(immutable)なので、指定位置の文字を変更することはできない。スライスを使って、組み立て直すしかない。大量の文字列が生成されることを避けるため、StringIOとcStringIOモジュールがある。
word[2:5]は[2, 5)
s[:i] + s[i:]は常にsと等しい
インデックス指定の場合、範囲外は違法だが、スライスは大丈夫、取れるところまで取ってくれる。byte単位で文字の桁長を指定する場合、bytearray()関数やencode()メソッドでbyte変換してから、切ればいい
これ以降
- 文字列に対する操作方法を通じ、シーケンス型の共通する操作方法を学習。
- 基本の変換と検索
- f文字列 -- 最新??
-
str.format()-- 一般的に使用されている - %演算子 -- 古い??
リスト(list)
squares = [1, 4, 9, 16, 25]
可変なので、append()やインデックスを使用して、要素の追加や入れ替えができる。Pythonの習慣上、対象に対する変更があるメソッドを呼び出す場合、変更後の結果をリターンしない、逆に対象自身に対する副作用はない操作の場合、その結果をリターンする。例えば、append()のリターン値なNoneで、+演算子の場合に結果をリターンする。メソッドチェーンのコーディングスタイルに向いてない。
スライスも使用できる。しかも入れ替え可能。
その他の制御フローツール
if...elif...else
switch...caseはなし
for文
for w in words:
for user, status in users.copy().items():
foreachの形式しかない。
loop内でシーケンス型対象に対する削除や追加の操作がある時、動作を保証するため、対象のコピーに対し反複し、元の対象に対して操作を行う。要はループ対象と操作対象を被らないようにする。copy()やlst[:]で簡単にコピーを作ることができる。
索引(添え字)でループ制御をしたいなら、range()を使う。for i in range(len(lst)):
range(start, end, step)、[start, end)、stepはマイナスも可
索引と要素値を同時に欲しい場合、enumerate()関数を使う。for i, item in enumerate(items):
range()の結果は大体の場合、listのように扱えるが、listではないので、あくまでも短縮した表現、展開したい場合、list(range(4))のようにキャストすればいい
whileの例もそうだけれど、ifやwhileの後ろの判定式を括弧で括る習慣は無いようだ
ループのelse
forとwhileのループ文はelse節をつけることができる。ループ内breakされなく、完全に回った後、このelseが実行される。このelseを使えば、例え二個のリストを突合する場合、ヒット有無を判定するフラグを省略することができる。要は、二重ループの時使えそうだ。
pass
body部を保留する場合、このpassをプレースホルダとして使える。
関数
def foo(args):
関数が呼び出されると、関数のローカル変数の為のシンボルテーブル(symbol table)が作成される。渡されたパラメータもローカル変数としてこのシンボルテーブルに記憶される。
変数参照時の検索順:
-
local 関数のローカルなシンボルテーブルを検索
-
nonlocal 外側の関数のローカルなシンボルテーブルを検索(関数はネストできる)
-
global グローバルなシンボルテーブルを検索
-
__builtins__ 組み込みの名前テーブルを検索
実際、pythonのスコープも上記の4階層みたいで、ifやfor文などのブロックは独自のスコープは存在しないため、ブロック内で生成した変数は、外でも参照できる。初期化とか気を付けないと
Pythonのパラメータは値渡し(call by value)、値は常にオブジェクトへの参照になる
関数名もシンブルテーブルに格納されている。値はユーザ定義関数形と認識され、変数に代入することができる。この場合、この変数は関数の別名にもなる。
関数のパラメータ
名称
| 定義部分 | 呼び出し部分 |
|---|---|
| デフォルト値 | 位置引数 |
| *arguments | キーワード引数 |
| **keywords |
定義部分
def f(pos1, pos2, /, pos_or_kwd, *, kwd1, kwd2)
-
/がある場合、この前で指定したパラメータは位置引数しか受けとらない。 -
*がある場合、この後ろで指定したパラメータはキーワード引数(キーワード専用引数)しか受けとらない。 -
この種の定義方法は:
- ユーザー側にパラメータの位置を意識してもらいたい時とか、パラメータの名前に実意味がない場合、位置引数の制限をかければいい
- パラメータの位置を意識する必要はなく、パラメータの名前で物事を表してほしい場合、キーワード指定を強制すればいい
- 拡張性上、基本位置指定のほうがいい、キーワード引数の制約をかけると、パラメータの名称変更ができなくなる。
def f(*args, **ks)
-
*argsは可変長引数で、tupleとして受け取る -
**ksはキーワード引数で、dictとして受け取る -
argsとksは、Pythonの一般的な命名の慣例で、ほかには
**kwargsがあるが、特に理由がなければ、これらを使う。 -
*argsの後ろに、仮引数としてキーワード専用引数のみ指定可能 -
**ksは最後しか出てこない
Pythonは関数やメソッドの重複定義(オーバーロード)ができない、後ろで定義した同名関数が前のを上書きするから、パラメータのところにこのようないろんな設定方法でカーバーする。
呼び出し部分
引数リストのアンパック(展開)、tupleやlistの先頭に*を付ければ、アンパックされる。
foo(*(1, 2, 3))はfoo(1, 2, 3)と等しい、アンパック対象は普通は変数である。
dictの先頭に**を先頭に付けると、キーワード引数としてアンパックされる。
foo(**{key1: value1, key2: value2})はfoo(key1 = value1, key2 = value2)と等しい。
ラムダ式
lambda a, b: a + b
無名関数、単一の式に制限されている。入れ子構造の関数と同様に、取り囲むスコープの変数を参照することができる。パラメータとして直接定義して渡すこともできるので、便利かも
なんか、引数が複数ある時でも、括弧つける慣習は無いみたい。
引数にデフォルト値を指定することもできる。
ドキュメンテーション文字列
docstring, __doc__, help()
ルール
- 関数の最初の行から三連引用符で記述。大文字で始まり、ピリオドで終わらせ。関数の目的を短く簡潔に説明する。要は概要を記述
- さらに記述すべき行がある場合、二行目を空行にし、呼び出し規約や福作用などを記述
ドキュメント処理ツールは、最初の行(概要)の後ろにある空行の後ろの最初の行のインデントの量を基準にし、各行のインデントの剥ぎ取る量を算出する。
関数のアノテーション
完全オプションなメタデータ情報なので、間違っても動作上は何の影響はないが、IDEからは警告が出る可能性はある。
__annotations__
def f(ham: str, eggs: str = 'eggs') -> str:
コーディングスタイル
Google Python スタイルガイドを日本語に訳したサイト
データ構造
リスト形
list
append, extend, insert, remove, pop, clear, index, count, sort, reverse, copy
stack
append, pop
queue
stackと違い、listはc++のvector見たいなもので、listで実現すると効率上難がある。その代わりにfrom collections import dequeを使う。append, popleft
リストの内包表記
あるシーケンスやiterableをもとに新しいリストを生成したい場合使用。簡略化したmap()、filter()。強力すぎて、とりあえず、従来のmap()やfilter()、二重ループでリストを作成する部分に対し、この内包でリファクトをし、コード量を上げて慣れていかないと
タプル
tuple
constで修飾したリストみたいなもの。括弧が特徴なので、要素が1つしかない場合、最後に,を付けないと、ただのリテラルになる
集合形
set
差、和、対称差、積は-, |, ^, &のように算数演算符で使えるので、便利
集合の内包は、リスト内包の[]の代わりに{}で括る
辞書型
dict
内包はsetと同じ{}で括るが、生成ものをkey: value形式にすれば、辞書型になる
キーは単純文字列(スペースや変な記号はない)の場合、dict(sape=4139, guido=4127, jack=4098)でコンストラクトできる。どうやって実現したかわからないが、結構使えそう。
条件についてもう少し
a < b == cのように比較は連結できる
in, not in、含まれているかどうかを調べる
is, not is、両方のidは一致かどうかを調べる。is Noneは使うでしょう
条件に=は使用禁止で、代わりに:=を使う
モジュール
Pythonファイル(拡張子.pyのファイル)はモジュール
モジュール名はモジュール内で、グローバル変数__name__で参照できる。
import module_name
この分はモジュール名module_nameをシンボルテーブルに入れるだけで、module_name内で定義された関数や変数(グローバル、ローカル)はシンボルテーブルに登録されていないため、アクセス時、モジュール名経由で間接でアクセスする必要がある。
module_name.foo()
module_name.__name__
モジュールに関数定義のほか、実行文を入れることができる。基本モジュールを初期化するためのもの。
インポート文の中で最初にモジュール名を見つかった時にだけ実行される。
各モジュールは、自分のプライベートなシンボルテーブルを持っていて、モジュール内で定義された関数はこのテーブルをグローバルなシンボルテーブルとして使用する。
from fibo import fib, fibr
この場合、モジュール名は取り込まれていない
モジュール内で定義されている名前をすべてimportする。
from fibo import *
アンダースコア(_)で始まるものを除いてすべての名前をimportする。ただし、シンボルテーブルを汚れる恐れがあるため、推奨されていない。
import fibo as fib
from fibo import fib as fibonacci
importしたモジュールは1回だけimportされるため、モジュールを修正した場合、インタープリタを再起動させなければならない。ソースを弄れる場合、import importlib; importlib.reload(modulename)を使用できる。
モジュールをテストしたい場合、末尾に
if __name__ == "__main__":
import sys
fib(int(sys.argv[1]))
モジュール検索パス
- ビルトインモジュール
- スクリプトファイルを含むディレクトリ
- PYTHONPATH // ${PATH}
- 標準ライブラリ
1~3はsys.pathの初期値
sys.pathはプログラム内で修正できる。修正後の値は上記の2の部分に入れ替え、順序は変更しない
"コンパイル"されたPythonファイル
読み込みを高速化するためのもの、動作効率は影響しない
-
__pycache__(ディレクトリ)が生成され、そのなかにコンパイル済みモジュールが格納される。(__pycache__/module_name.cpython-33.pyc、cpython-33はPythonのバージョン番号、この命名法により、バージョン、リリース異なるものが共存できる) - プラットフォーム非依存で、異なるシステム間共有できる
- ソースの変更日時でコンパイル済みのものと比較して、更新要否を判断
モジュールがimportされる際に、生成される。
パッケージ
ディレクトリをパッケージにするには、直下に__init__.pyが必要、空ファイルでも構わない。
import item.subitem.subsubitem
「.」前のものは必ずパッケージでなければならない
- パッケージ配下にサブパッケージとモジュールが同名のものが存在する場合、パッケージが優先される。ただし、パッケージは
__init__.pyをロードするだけで、実用は無い、importした後、moduleとして存在するされ、package.moduleで使用できない
パッケージから*をimportする
__init__.pyに__all__が定義しなければ、なにもimportしない。
import sound.effects.echo
import sound.effects.surround
from sound.effects import *
import文は上から下へ実行され、__all__時はload済みのものは再loadしない
上記の場合、sound.effects.echo.foo()も使えるし、echo.foo()も使える
パッケージ内参照
兄弟関係にあるパッケージを参照する場合、
絶対import
相対import
from . import echo # 現在のパッケージ
from .. import formats # 親パッケージ
from ..filters import equalizer
さらに上(../..)はできない、絶対パスを使う。