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
さらに上(../..
)はできない、絶対パスを使う。