Pythonチュートリアルではえ＾〜って思ったことまとめ


# はじめに

現在[Python公式チュートリアル](https://docs.python.org/ja/3/tutorial/index.html)で、Pythonの勉強を再び一からやっています。

そこで、自分がはえ＾〜って思ったことを随時まとめていこうと思います。

# 1章　やる気を高めよう

Pythonは、C言語よりも高級な言語であり、C＋＋などと比べて

- 開発
- テスト
- 実行
- 開発

の流れのサイクルをすばやく繰り返すことができる。

# 2章 インタプリタ

Pythonはもともとインタプリタの概念が強いため
ファイルに書いて実行するのではなく
対話的に利用することができる。

# 3章 触ってみよう

## 文字列

```py
text = "hello, \"world"
print(text)
```

'' または ""で文字列を表すが、「\」この企業がついていると特殊記号をエスケープすることができる。

```py
text = '""""""""""'
print(text)
```
''や""は互いに独立しているため、お互いに影響しない。

```py
text = 'C:\name'
print(text)

text = r'C:\name'
print(text)

text = r'C:\namea\'ba'
print(text)

'''
C:
ame
C:\name
C:\namea\'ba
'''
```

特殊文字は、r''というRaw文字列を使うと自動エスケープされる。
ただし、このとき\が機能しなくなり\'のまま出力されるため注意。

```py
text = """
長い
ので
すごく
辛い
"""

'''

長い
ので
すごく
辛い



'''
print(text)
```

"""や'''で複数行に文字列を書くことができる。
各行末に「\」が無いと自動的に改行される。

```py
text = "HelloWorld"

for i in range(0, len(text), 2):
    print(text[i:i + 2])

'''
He
ll
oW
or
ld
'''
```

パイソンにはスライスという機能がある。
`[スタート位置:終了位置:step]`という記法で、省略すると自明な値が入る。
右開区間で設計されている。

```py
text = "Hello, World"
print(id(text))

# not permitted
# text[1] = 'j'
# print(text)

text = text[:1] + 'j' + text[2:]
print(text)
```

pythonの文字列は書き換えることができない。
これは、オブジェクトのメモリ管理を考えれば自明である。

## リスト

```py
list1 = list(range(0, 10))
list2 = list(range(3, 13))
print(list1 + list2)

'''
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
'''
```

リストは + 演算で結合することができる。
リストは可変mutableのため、書き換えて良い。

```py
words = ['hello', 'world', 'by', 'python']

for word in words[:]:
    words.append(word)

# ['hello', 'world', 'by', 'python', 'hello', 'world', 'by', 'python']
print(words)
```
Pythonのforで、リストなどのイテラブルオブジェクトを渡すとき、
そのオブジェクトのイテレータが渡される。
ここで、先に浅いコピーなどで渡しておかないと（メモリのコピー）をしないと
wordsオブジェクトに永遠にwordが追加されて処理がLoopする。

```py
list1 = ['hello']
list2 = list1

# 4432018400 4432018400
print(id(list1), id(list2))

list1 = ['hello']
list2 = list1[:]

# 4556365008 4432824912
print(id(list1), id(list2))
```

pythonでは、可変なオブジェクトに関しては「その先頭」のメモリを指しているイメージに近い。
そのため、上記の最初のprintでは、同じメモリlist1の先頭をlist2という`名前`が指しているため、同じメモリである。

一方、２つ目のprintに関しては、list1のオブジェクトを[:]シャローコピーして、渡している。
そのため、idがそれぞれ異なる。

また、リストごとにメモリが用意されるため、
一個目と二個目のlist1のメモリID自体が違うのも注意。

## 関数

```py

def pprint(text):
    print(text)


print(id(pprint('hello')))
print(id(pprint('hello2')))

'''
hello
4447101208
hello2
4447101208
'''
```

pythonでは、関数自体にメモリが与えられる（当たり前か）
関数も他の変数に入れることができ、これはただの参照である。

```py

def add(a, b=30):
    return a + b

# def add(a=10, b):
#     print(a + b)


print(add(2))
print(add(2, b=100))
```

pythonではデフォルト引数や名前付き引数が使用できる。
ただし、どちらも先頭に通常呼び出しで、後ろ側に名前付きやデフォルト引数を使用する、という原則が存在する。（そうしないと、インタプリタ的にも、コーディングを後で見返すときにもしんどい。）


```py
def sugoi(a, *other, **dicted):
    print(other)
    print(dicted)


sugoi(30, 30, 40, 40, 20, hello='world')
'''
(30, 40, 40, 20)
{'hello': 'world'}
'''
```
pythonでは、はみ出た引数を受け取ることができる。
まず、*という引数名でリストとして受け取れる。
また、はみ出た名前付き引数に関しては**dictedで辞書で受け取ることができる。

```py
def sugoi(a, *other):
    print(other)


sugoi(30, 30, 40, 40, 20, hello='world')
'''
    sugoi(30, 30, 40, 40, 20, hello='world')
TypeError: sugoi() got an unexpected keyword argument 'hello'
'''
```
また、引数がはみでたときにそれをキャッチする引数が用意されてないとエラーが出る。
これはリストも同様。

```py
a = [10, 20, 30]
b = {"x": 30, "y": 50}

print(a)
print(*a)

print(b)
print(*b)


def add(x, y):
    print(x + y)


add(**b)
```

pythonにはリスト・タプルといった組み込み型に**アンパック**という機能が存在する。
これは、リストから中身を取り出す記法である。

リストでは、そのまま中身を取り出し
辞書では、キーを取り出す。

ただし、名前付き引数として関数に渡したいときは**２つつけてアンパックする（なんで？

```py
f = lambda x, y: x + y

print(f(10, 20))
```

ラムダ式。
`lambda 引数: 返り値`の記法を取る。
基本的にsortなどの小さいスコープでしか使わないし、使うべきではない（見辛いため）

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

Pythonのコーディングスタイルガイドには`PEP8`がある。
これを守ることで、全員共通で読みやすいコーディングができる。

- 空白$4$つでインデントを表現する
- 幅は$78$文字以下となるように折り返す
- コメントは可能なら独立して１行とする
- DogStringを使う
- 必ず演算子とコンマのあとに改行を入れる。ただし、括弧類の内側に空白は入れない。
- クラスはUpperCamelCase、関数などはLowerCaseUnderScoreで表現する。


# 5章 データ構造

## リスト内包表記

```py
a = [(x, y) for x in range(10) for y in range(10) if x < y]
print(a)
```

上記のように
`[出力要素 for x in iterable for ...   if]`
の形になる。

```py
vector = [
    [1, 2, 3],
    [4, 5, 6],
    [7, 8, 9]
]

a = [x for vec in vector for x in vec]
print(a)
```
forに関しては左から右へ流れていく。


```py
def transpose(matrix):
    return [[row[i] for row in matrix] for i in range(len(matrix[0]))]


matrix = [
    [1, 2, 3, 4],
    [5, 6, 7, 8]
]

print(transpose(matrix))
```

リスト内包表記は入れ子構造にでき、メモリ的にも計算量的にも嬉しい


## タプルとシーケンス

list, tuple, rangeはシーケンス型と呼ばれ、イテラブルでありlen関数などの適用が行える型である。

タプルはListと異なり、immutableであり不可変である。

```py

a = 30, 20, 50
b = a, 40


'''
(30, 20, 50) ((30, 20, 50), 40)
'''
print(a, b)
```

tupleは括弧をつけなくても、自動的に括弧がつけて行われる。


```py
a = 20, 30
b, c = a

print(b, c)
```

パックとアンパックがある。
右辺から左辺に入れる時、変数の数が足りなければ自動的に後ろ側が「パック」される。
一方で、左辺で無辺から受け取る時、右辺のほうが足りなければ、自動的にアンパックされる。
このとき、お互いに変数の数が一致しない時エラーが発生する。

## setと辞書

setは、同様の値を持たないコレクションで
辞書はキーと値を対応付けるコレクションである。

```py
st = {x + y for x in range(10) for y in range(10)}

print(st)

mp = {x: x * x for x in range(10)}
print(mp)
```

setでは｛｝でリスト内包表記が使える。
同様に辞書でもリスト内包表記で同じ記号を使うが、キー・値の指定があればmapとなり
そうでないならばsetになる。

## ループテク

```py
mp = {'hello': 'world', '40': 311}


for key, value in mp.items():
    print(key, value)

for key in mp.keys():
    print(key, mp[key])

for value in mp.values():
    print(value)

```

辞書は、items, keys, valuesがそれぞれ用意されている。
itemsは両方である。

```py

a = [10, 20, 30]

for i, v in enumerate(a):
    print(i, v)
```

enumerateで、(i, value)となっているタプルを受け取ることができ
それはアンパックできるので、添字が取れて嬉しい。

```py
a = [10, 20, 30, 40]
b = [50, 60, 70, 80, 90]

'''
(10, 50)
(20, 60)
(30, 70)
(40, 80)
'''
for tup in zip(a, b):
    print(tup)
```

zipで複数のコレクションをセットのタプルにできる。
こｒを利用することでうまく扱える。


# 6章　モジュール

## sys.path

os.sys.pathでは、Pythonのライブラリを読み込むディレクトリが羅列されている。
よって、このパスにディレクトリ先を追加すると、追加で検索をしてくれる。

## dir関数
dir関数を使用するとそのパッケージ・モジュール下で定義されている
名前空間の名前の一覧を取り出すことができる。

これによって、名前の一致などを見れるかもしれない。


# 7章　入出力

## f文字列

```py
year = 2020
day = 10

print(f'hello, {year} {day}')
```

文字列リテラルの先頭にfをつけると変数の埋め込みができる。
ただし、変数をすべてそのまま書く必要があるため、文字列が長くなってしまい見辛いという欠点がある。
ドルマークはいらない。

書式指定もできる

```py

year = 2020
day = 10

print(f'hello, {year:.3f} {day}')
```

詳しい内容はhttps://docs.python.org/ja/3.7/library/string.html#formatspec
にある。

## フォーマット文字列

```py
year = 2000
day = 40

format1 = 'year is {0} day is {1}'.format(year, day)

print(format1)

format2 = 'year is {year} day is {day}'.format(year=year, day=day)
print(format2)
```

フォーマットを使うと、数字　もしくは　変数名の指定でも行ける。

## ファイル読み込み

ファイルの読み込みは組み込み関数のopenを利用する。

open(ファイル名, mode)と指定する。
modeは

- r 読み込み
- w 書き込み
- a 追記
- r+ 読み書き
- bもつける　バイナリ

のように指定する。

```python
with open(file_path, mode) as f:
  fの処理
```
で使用しよう。
こっちのほうが、Closeを明示しなくてよいため、安全マン。

```py
file_path = ''
mode = ''

with open(file_path, mode) as f:
    for line in f:
        print(line)
```

上記のようにファイルオブジェクトは各行ずつのイテレータを持っているため、簡潔・かつ高速に各行の取得ができる。

# 8章　例外

## 例外の発生

Pythonでは、構文エラー自体は実行時前にエラーが出てわかる（そりゃまあ）

ただ、C++などと違って、コンパイルがなく、型のミスや明らかに間違えている部分を検出することができず、実行時にしか分からない。


そのため、

```python 

a = 0

print(20 / a)
```

```
Traceback (most recent call last):
  File "/Users/students/Desktop/研究実装/Python_Tutorial/main.py", line 5, in <module>
    print(20 / a)
ZeroDivisionError: division by zero
```

上記のようなZeroDivisionErrorを発動し、その場で実行が終了してしまう。

このような例外にはtry-except文を使用する。


## 例外の補足

```python


while True:
    try:
        x = int(input())
        y = 30 / x

        print("here")

    except (ValueError, ZeroDivisionError, NameError) as e:
        print(e)

```

 例外のキャッチには、Pythonではtry-except構文を利用する。
tryの中でエラーが発生すると、まずexceptに例外が投げられる。
ここで、一致するエラーがあれば、except内を実行し、try-exceptのあとに移行する。
そのため、上記の例では、ZeroDivisionErrorが発生すると、print('here')は実行されない。

**また、もしexceptで補足されない場合さらに上の階層へエラーが投げられる。**
そこでも補足されない場合、プログラムが停止する。

```py
def f():
    try:
        x = int(input())
        y = 30 / x

        print("here")

    except (ValueError, NameError) as e:
        print(e)


while True:
    try:
        f()
    except ZeroDivisionError:
        print("ZeroDivision")
```

上記で0除算が発生すると、f関数内で補足されないのでよりインデントが上の上位で補足される。


## 例外の連続と継承

例外は、exceptを並べることで複数用意することができる。
ただし、上から見ていって一個でもエラーと種類があっていたらそこで終わる。

また、エラーを受け取る
つまりexcept SomeErrorとかける、SomeErrorcはExceptionというPythonの組み込みクラスを継承したクラスである。
そして、もし発生したエラーと基底クラスが同じならその時点でエラーが発生する。

例えば、以下の場合

```py
class B(Exception):
    pass

class C(B):
    pass

class D(C):
    pass

for cls in [B, C, D]:
    try:
        raise cls()
    except D:
        print("D")
    except C:
        print("C")
    except B:
        print("B")

for cls in [B, C, D]:
    try:
        raise cls()
    except B:
        print("B")
    except C:
        print("C")
    except D:
        print("D")
```

DCBBCDと出力される。
最後の３つがBCDとなるのは、Dの親の親がBであり、Dなどの子供のエラーが発生しても親であるBがそのエラーを補足するためである。

## 例外のワイルドカード

すべてを吸収できる例外構文がある。
ただ、これを使うとなんでもかんでも補足してしまうのでやめよう。
ただ、関数の最後の用意しておいて、親に補足してもらうために利用するのはありっぽい。


```py
def f():
    try:
        x = int(input())
        y = 30 / x

        print("here")

    except (ValueError, NameError) as e:
        print(e)

# 投げますねぇ！
    except:
        raise


while True:
    try:
        f()
    except ZeroDivisionError:
        print("ZeroDivision")

```

## 例外のクリーンナップ

try-exceptの最後にfinallyといういつもの構文を使える。
これによって、必ず最後に行いたい処理（ファイルのクローズなど）を行うことができて嬉しい

# 9章　クラス　

## 名前空間

![canvas.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/211482/e76550a1-cc7b-ac43-160e-fd52dd84b0f3.png)

Pythonでは、変数などの名前とオブジェクトのメモリを対応させるのを**名前空間**と読んでいる。

この名前空間は、それぞれ入れ子構造になっていて
例えば

- mathモジュール内で使用されているすべての関数とそのメモリの名前空間
    - math.gcd内で使用されている変数とその名前空間
- ユーザ定義モジュールの変数とその名前空間
    - モジュール内のクラスの名前空間

のように入れ子構造になっている。

そして、これらの名前空間は互いに参照しないようになっているため、名前衝突の危険性を減らすことができる。

## スコープ

スコープや変数などの名前が探しにいける範囲。
これは名前空間で閉じられる？
ただ、モジュールA・モジュールBがあったとき、これらはまたげないので、イメージ的には完全に分離されている。


名前空間の中にスコープがある、というイメージで認識している。


## globalとnonlocal

```py
def scope_test():

    def do_local():
        spam = "local spam"

    def do_nonlocal():
        nonlocal spam
        spam = "nonlocal spam"

    def do_global():
        global spam
        spam = "global spam"

    spam = "test"
    do_local()
    print(spam)

    do_nonlocal()
    print(spam)

    do_global()

scope_test()
print(spam)

'''
test
nonlocal spam
global spam
'''
```

pythonにはglobalとnonlocalキーワードがある。
関数などそれぞれの名前空間で通常は、一番小さい変数・スコープを使用する。
ただし、もしその変数名が無いなら親名前空間に同じ名前の変数がないか探しに行く。

ここで、もしグローバル宣言されている変数と同じ名前を関数で使用すると、一番小さいスコープがデフォルトのため、ローカル変数ができて、グローバル宣言されている変数には影響が出ない。
そこで、`global`キーワードをつけるとグローバル宣言されてる変数を直接参照できる。

また、同様にglobalじゃないけど一個上の階層の変数がほしいなど、ローカル変数の宣言じゃないんです〜ってときは、nonlocalキーワードを使用しよう。


## class

クラスは他言語と同様の概念。
ただ、Pythonの特有の特徴として以下がある。

- 変数・メソッドを後から足していい
    - 名前空間で管理しているため、変数名とオブジェクトのメモリの対応しかしていない。よって、付け替えれる
- 変数は基本的にローカル
    - クラス直下ならクラスの属性変数になる
    - 関数無いならselfを付けないならローカル変数になる
- コンストラクタは関数__init__(self)で宣言する


```python

class MyClass:
    """
    this is my class
    """

    var = 100

    def __init__(self, _var):

        self.var = _var
        var = 200

    def f(self):
        print(100)


a = MyClass(2434)

# 2434
print(a.var)
```

上記だと、200ではなく2434と出る。
これは、基本的にはローカル変数を参照し、selfを付けないと自インスタンスの属性とみなさないためである。

## クラスのメソッドオブジェクト

クラスのメソッドは、Pythonでは`メソッドオブジェクト`と呼ばれる。
このメソッドオブジェクトは、クラスのインスタンスに属するメソッドで、インスタンス生成時に具体化される。

メソッドオブジェクトはただのメモリであり、そこを参照すればメソッドを外部にコピーできる。
ここで、呼び出し時に何も引数が要らず、一方定義ではf(self)のように必ず第一引数を必要とする。

これは、メソッドオブジェクトは強制的に第一引数にそのクラスのオブジェクトが代入される。
よって、

```py

class MyClass:
    """
    this is my class
    """

    var = 100

    def f(self):
        print(100)
        self.f()

```

以上のようなコードを書くと、永遠にfを呼び続けてエラーがでる。

## クラス変数とインスタンス変数

PythonではC++と同様に

クラス変数
インスタンス変数

の概念がある。
クラス変数は一度のみ初期化される変数でクラスからしか参照できない。
インスタンス変数は各インスタンスごとに存在する変数で、インスタンスからしか参照できず、共有されない。

ここで、クラス変数にはインスタンス変数から触ることができないことに注意する。


```py
class Dog:

    # クラス変数領域
    name = 'ganariya'

    def __init__(self, name):

        # インスタンス変数
        self.name = name

        # ローカル
        hello = "hello"


'''
ganariya
other
'''
print(Dog.name)
print(Dog('other').name)
```

以上のように、クラス変数とインスタンス変数、そしてただのローカル変数に注意する。
クラス変数はクラス名から参照でき、書き換えられる。

## クラス変数とリスト

リストなどのmutableオブジェクトに関しては別の挙動を示す。

mutableオブジェクトは、オブジェクト自体のあるメモリの位置を参照している。

ここで、どうやらクラス変数がmutableなものかそうでないかで行える操作が違う。

まず、mutableでないならインスタンス変数から参照できない。

```py

class Dog:
    arr = 's'

    def __init__(self, name):
        self.name = name

    def print(self):
        self.arr = 'hello'


Dog('other').print()
print(Dog.arr)

```


一方、mutableならインスタンス変数から参照できる。

```py
class Dog:
    arr = []

    def __init__(self, name):
        self.name = name

    def print(self):
        self.arr.append('hello')


Dog('other').print()
print(Dog.arr)
# ['hello']
```

今のところ原因不明。
できる限り可変なものはインスタンス変数として
定数系をクラス変数として扱おう。

---

#### 追記

このようになる原因は`代入`にある。

まず、クラスのメソッドオブジェクトは通常の関数とほとんど同じ動きをする。

つまり

- 参照なら現スコープ・外・さらに外・グローバルのように探す。
- 代入のとき
    - 先にグローバル変数を参照した時、そのスコープ内でグローバル変数を書き換えると、呼び出されて同じスコープに持って越せられたグローバル変数が書き換わるためエラーになる。
    - 先にグローバル変数を参照してない時、ただのローカル変数を新しく作ることになる


```py
global_var = "Global Varibale"


def get_global():

    # globalの情報がここで来る
    local_var = global_var
    global_var = 'fate'
    print(local_var)

'''エラー
'''
print(get_global())
```


よって、以上のルールがクラスにも適用されるため

```py

class MyClass:
    name = 'myclass'

    def f(self):
        print(self.name)
        self.name = 'instance'
        print(self.name)

'''
myclass
instance
'''
MyClass().f()
```

以上の場合は、
print(self.name)で、ローカルにもインスタンスにもないため、クラスから取ってくる。
その後instanceという名前の変数で生成されるため、次はインスタンスの属性のnameを取ってくる。

ここで、インスタンス変数からはクラス変数をimmutableなら変更できない。
参照した時点でインスタンス変数を差しているとみなされるためである。

しかし、mutableの変数で代入でない場合は、もしインスタンス変数にその名前の変数がないなら、クラス変数を見に行く。
そのため、上記のようにリストが共有されてしまっていた。
ここで、インスタンス生成時にクラスのmutableと同じ名前の変数を作れば指す参照は変わる。


```py
class MyClass:
    arr = []

    def __init__(self):
        self.arr = []

    def set(self, x):
        self.arr.append(x)


MyClass.arr.append("hello")

mc1 = MyClass()
mc2 = MyClass()

mc1.set("fate")
mc2.set("grand")

'''
['fate']
['grand']
['hello']
'''
print(mc1.arr)
print(mc2.arr)
print(MyClass.arr)
```


## プライベート

Pythonにはプライベートな情報を持つことはできない。
「そうならないように注意してください」というスタンスをユーザーに取るのみである。

そこで、アンダースコアを使って、プライベート変数かどうかを見分ける（隠せるわけではない。）

アンダースコア２つをつけて、そのような関数を使用する際は書き換えないように気をつける。


## イテレータ

Pythonのオブジェクトにはイテレータが定義されていることがある。
このイテレータ機構があることで、forで簡単に配列やオブジェクトを舐めることができる。

オブジェクトのイテレータを取り出すには`iter`関数を使う。
これを使うことで、オブジェクトを舐めるイテレータが返される。
for文などでオブジェクトを指定するときは、暗にiterが裏側で指定されている。

```py
arr = ['1', '2', '3']
for x in iter(arr):
    print(x)
```

このイテレータはオブジェクトのコピーのようなもので、舐めることしか機能がない。
そして、next関数をイテレータに利用することで要素を取り出すことができる。（for内部で勝手に使われている）

イテレータを自作するには

- __iter__
- __next__

メソッドを内部で定義する（プロトコル）。

__iter__はiter関数で呼び出され、この返り値が__next__を持つオブジェクトになる。
実装では、selfを返すだけで良い。
なぜなら、self=そのクラスオブジェクトであり、クラスオブジェクトに__next__を実装しているはずだからである。

また、__next__は、実際にnext関数で呼び出されて一つずつ値を取り出すような処理を書く。

```py
class Iterable:

    def __init__(self, data):
        self.data = data
        self.length = len(data)
        self.index = -1

    def __iter__(self):
        return self

    def __next__(self):
        self.index += 1
        if self.index == self.length:
            raise StopIteration
        return self.data[self.index]


iter = Iterable("fate")
for c in iter:
    print(c)
```

## ジェネレータ

ジェネレータは名前の通りなにかを作り出すシュガータックス構文機構。
なにかというのは、先程のイテレータを自動生成する。

内部でnext, iterを自動でいい感じにしてくれる。

行うときは
関数内部に

```py
    for i in range(0, len(arr))[::-1]:
        yield i
```

のように、yieldで値を返そう。
すると、ここで一度処理が中断される。

yieldとreturnは併用ができる。ただ、returnした時点ですべてのジェネレータとしての機能を終了する。

```py
def reverse(arr):
    for i in range(0, len(arr))[::-1]:
        yield i
    print("here")
    yield 30



'''

'''
arr = [1, 2, 3, 4, 5]
for x in reverse(arr):
    print(x)
```

# 10.標準ライブラリミニツアー

## os

os はオペレーティング・システムを利用するモジュール。
os.dirなどを普段使う気がする。

## glob

ファイルのパスを取得するときに、globを使うことで
ワイルドカード検索ができる。

ファイルパスへのリストを取得できる。

## re

正規表現ができるモジュール。
正規表現が分からないので、分からない（おい）

## math

いつもの

## urllib

urllibはインターネットとの通信で使う標準ライブラリである。
ただ、requestsのようなサードパーティのライブラリのほうが使いやすいことが多いので直接扱うこともあるが、なかったりする

## datetime

日時・操作のモジュール。
分かり辛いし、暗記する必要は一切ない。

## データ圧縮

`zlib, gzip, bz2`のようなアーカイブが直接的にモジュールでサポートされているらしい。


# 11.標準ライブラリツアー２

## 文字列テンプレート

stringモジュールに、文字列をより扱いやすくするTemplateクラスがある。

Templateクラスは、Vue.jsのようなテンプレートと同様に

1. 先にテンプレートクラスから生成したテンプレートテキストを用意する。
2. テンプレートテキストにパラメータを指定して、文字列を埋め込む。

の手順を踏むことで分かりやすく文字列を生成できる。

```py
from string import Template

money = 1200

template = Template('I have ${money} yen')
txt = template.substitute(money=money)
print(txt)
```

以上では、`template`変数ではまだ何も変数が入っていない。
しかし、substituteでパラメータを指定して文字列を代入することで、文字列を生成できる。

ただ、もしパラメータを指定していないものがあるとエラーが発生する。

```py
from string import Template

money = 1200

template = Template('I have ${money} yen')
txt = template.substitute()
print(txt)
```

こういうのは、サーバー側でユーザのリクエストを受けて返したり、サーバーのデータベースからデータを持ってきて埋め込むときにエラーが発生してしまう。
そのため、パラメータし忘れてもいいようなsafe_sabstituteメソッドも存在するため、使い分ける。


```py
from string import Template

money = 1200

template = Template('I have ${money} yen')
txt = template.safe_substitute()

# I have ${money} yen
print(txt)
```

## マルチスレッド処理

threadingモジュールを使うことで、並行処理を行うことができる。
ただ、メモリ管理などが難しくなるため、Queueオブジェクトを使うといいらしい。
使うときにまた調べる。


## logging

ログを記録するためのモジュール。

ファイルに出力することもできる。
機械学習やサーバーで利用すると簡単に情報を取れそう。

出力は5段階で

1. debug(最も丁寧)
2. info(通常情報)
3. warning(エラーが出るほどではないが、危険)
4. error(エラー)
5. critical(もうやばいエラー)

のように分けて出力できる。
そして、出力をどのレベルから行うかは
logging.basicConfig(level=)で指定できる。
level以上の出力が、標準出力やファイル名を指定すればファイルに出力される。

```py

import logging

logging.basicConfig(level=logging.INFO)
logging.debug("Debug")
logging.info("Info")
logging.warning("Warning")
logging.error('Error')
logging.critical('Critical error')

'''
INFO:root:Info
WARNING:root:Warning
ERROR:root:Error
CRITICAL:root:Critical error
'''
```


## 競プロコレクション

競プロで使えるようなデータ構造・モジュールがある。

collectionsにはdequeがあり、左からも右からも$O(1)$で追加・削除が行える。
また、heapqはヒープを作り最大値を$O(logN)$で取得・追加できる。

bisectモジュールは二分探索などを行うソート済みのリストに使えるモジュールである。

## decimal

Pythonの浮動小数点は自動計算のため、精度が安定しない。
そこで、decimalモジュールを使うことで、指定した桁数精度の出る計算を行うことができる。
ただ、その分計算量・メモリは食う


# 12.仮想環境

Pythonは多くのモジュールを使用するため、例えば複数のアプリで同じパッケージを使うとしても異なるバージョンを使用したい場合がある。

そこで、Python公式自体でvenvというモジュールが開発されている。

## venvとpyenvとvirtualenvの違い

pyenvとpyenv-vertualenvはサードパーティ製の環境構築ライブラリ。
一方、venvはPython3.x.xに含まれているモジュールである。

pyenvは、各PCごとに一個だけ入れる`ミドルウェア`であり、そのPC内で複数のPythonのバージョンPythonx.x.x, Python.y.y.yをインストールし切り替えることができる。
よって、**バージョンの仮想環境**である。

pyenv-virtualenvは、pyenv以下で動くものである。
つまり、pyenvでインストールしたPython.x.x.x以下で動く切り分け環境である。
例えば、Python3.7.3を使いたいがアプリAとアプリBで使用したいKerasのバージョンが異なるとする。
こういうときに、pyenvのみでは当然Python3.7.3を複数インストールできない。
各PCに対するミドルウェアだからである。

そこで、pyenv-virtualenvを利用する。
pyenv-virtualenvを利用することで、Python3.7.3の環境内でパッケージを別々にインストールできる環境を構築できる。
つまりPython3.7.3-a, Python3.7.3-bのようなコピー環境をつくれると考えてもいいかもしれない。

よって、pyenv + pyenv-virtualenvによって、

- 任意のPythonバージョンを指定して
- さらに、そのバージョン下で任意のパッケージ利用仮想環境を作成できる

機能を実装できる。

一方、venvはインストールされたpythonの公式モジュールであり、mathやloggingと同じ立ち位置である。
よって、イメージ的には

- Macに元々入っているpython3.7.3のモジュールの一つのvenv
- pyenvでインストールされたPython3.4.5のモジュールの一つのvenv

のようになっている。
そのため、venvのみでは、Pythonのバージョンは当然変えられない。
行えるのは、Pythonのバージョンではなく、その中身のパッケージのインストールである。