Python Tutorial の学習メモ

# 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変換してから、切ればいい

#### これ以降

1. 文字列に対する操作方法を通じ、シーケンス型の共通する操作方法を学習。
2. 基本の変換と検索
3. f文字列 -- 最新??
4. `str.format()`-- 一般的に使用されている
5. %演算子 -- 古い??



### リスト(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)が作成される。渡されたパラメータもローカル変数としてこのシンボルテーブルに記憶される。

変数参照時の検索順：

1. **local**	関数のローカルなシンボルテーブルを検索

2. **nonlocal**	外側の関数のローカルなシンボルテーブルを検索（関数はネストできる)

3.  **global**	グローバルなシンボルテーブルを検索

4. **\_\_builtins\_\_** 	組み込みの名前テーブルを検索

実際、pythonのスコープも上記の4階層みたいで、`if`や`for`文などのブロックは独自のスコープは存在しないため、ブロック内で生成した変数は、外でも参照できる。初期化とか気を付けないと

Pythonのパラメータは値渡し(call by value)、値は常にオブジェクトへの参照になる

関数名もシンブルテーブルに格納されている。値はユーザ定義関数形と認識され、変数に代入することができる。この場合、この変数は関数の別名にもなる。

### 関数のパラメータ

#### 名称

| 定義部分     | 呼び出し部分   |
| ------------ | -------------- |
| デフォルト値 | 位置引数       |
| *arguments   | キーワード引数 |
| **keywords   |                |

#### 定義部分

`def f(pos1, pos2, /, pos_or_kwd, *, kwd1, kwd2)`

* `/`がある場合、この前で指定したパラメータは**位置引数**しか受けとらない。

* `*`がある場合、この後ろで指定したパラメータは**キーワード引数**(キーワード専用引数)しか受けとらない。
* この種の定義方法は：
  1. ユーザー側にパラメータの位置を意識してもらいたい時とか、パラメータの名前に実意味がない場合、位置引数の制限をかければいい
  2. パラメータの位置を意識する必要はなく、パラメータの名前で物事を表してほしい場合、キーワード指定を強制すればいい
  3. 拡張性上、基本位置指定のほうがいい、キーワード引数の制約をかけると、パラメータの名称変更ができなくなる。



`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()`

#### ルール

1. 関数の最初の行から三連引用符で記述。大文字で始まり、ピリオドで終わらせ。関数の目的を短く簡潔に説明する。要は概要を記述
2. さらに記述すべき行がある場合、二行目を空行にし、呼び出し規約や福作用などを記述



ドキュメント処理ツールは、最初の行(概要)の後ろにある空行の後ろの最初の行のインデントの量を基準にし、各行のインデントの剥ぎ取る量を算出する。



### 関数のアノテーション

完全オプションなメタデータ情報なので、間違っても動作上は何の影響はないが、IDEからは警告が出る可能性はある。

`__annotations__`

`def f(ham: str, eggs: str = 'eggs') -> str:`

### コーディングスタイル

[Google Python スタイルガイド](http://google.github.io/styleguide/pyguide.html)

[Google Python スタイルガイドを日本語に訳したサイト](http://works.surgo.jp/translation/pyguide.html)



## データ構造

### リスト形
`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したモジュールは１回だけimportされるため、モジュールを修正した場合、インタープリタを再起動させなければならない。ソースを弄れる場合、`import importlib; importlib.reload(modulename)`を使用できる。

モジュールをテストしたい場合、末尾に
```python
if __name__ == "__main__":
	import sys
	fib(int(sys.argv[1]))
```

### モジュール検索パス
0. ビルトインモジュール
1. スクリプトファイルを含むディレクトリ
2. PYTHONPATH　　// ${PATH}
3. 標準ライブラリ

1～3は`sys.path`の初期値
`sys.path`はプログラム内で修正できる。修正後の値は上記の2の部分に入れ替え、順序は変更しない

### "コンパイル"されたPythonファイル

読み込みを高速化するためのもの、動作効率は影響しない

1. `__pycache__`(ディレクトリ)が生成され、そのなかにコンパイル済みモジュールが格納される。(`__pycache__/module_name.cpython-33.pyc`、cpython-33はPythonのバージョン番号、この命名法により、バージョン、リリース異なるものが共存できる)
2. プラットフォーム非依存で、異なるシステム間共有できる
3. ソースの変更日時でコンパイル済みのものと比較して、更新要否を判断

モジュールがimportされる際に、生成される。

### パッケージ
ディレクトリをパッケージにするには、直下に`__init__.py`が必要、空ファイルでも構わない。

`import item.subitem.subsubitem`
「.」前のものは必ずパッケージでなければならない

1. パッケージ配下にサブパッケージとモジュールが同名のものが存在する場合、パッケージが優先される。ただし、パッケージは`__init__.py`をロードするだけで、実用は無い、importした後、moduleとして存在するされ、package.moduleで使用できない

#### パッケージから\*をimportする

`__init__.py`に`__all__`が定義しなければ、なにもimportしない。

```python
import sound.effects.echo
import sound.effects.surround
from sound.effects import *
```
import文は上から下へ実行され、`__all__`時はload済みのものは再loadしない
上記の場合、`sound.effects.echo.foo()`も使えるし、`echo.foo()`も使える

#### パッケージ内参照

兄弟関係にあるパッケージを参照する場合、
**絶対import**
**相対import**
```python
from .　import echo		# 現在のパッケージ
from .. import formats	# 親パッケージ
from ..filters import equalizer
```
さらに上(`../..`)はできない、絶対パスを使う。