Python初心者が「Python文法詳解」を読んだメモ #6

# バックナンバー
- [Python初心者が「Python文法詳解」を読んだメモ #1](https://qiita.com/usayomo/items/7024c619c5f72462cb10) 
- [Python初心者が「Python文法詳解」を読んだメモ #2](https://qiita.com/usayomo/items/9c1537678bdde813a273)
- [Python初心者が「Python文法詳解」を読んだメモ #3](https://qiita.com/usayomo/items/57e6156c95e60b3223ca)
- [Python初心者が「Python文法詳解」を読んだメモ #4](https://qiita.com/usayomo/items/01fdb8bbe7704da236bb)
- [Python初心者が「Python文法詳解」を読んだメモ #5](https://qiita.com/usayomo/items/eb2c59b538f5c349bc52)

```py:Pythonのリストはオブジェクトを参照している
>>> L1 = [1, 2]
>>> L2 = ['A', L1]
>>> L2
['A', [1, 2]]
>>> L1.append('three')
>>> L2
['A', [1, 2, 'three']]

# 参照ではなく、別オブジェクトとして要素化するには
>>> L1 = [1, 2]
>>> L2 = ['A', L1[:]]
>>> L2
['A', [1, 2]]
>>> L1.append('three')
>>> L2
['A', [1, 2]]
```

```py:リスト内包#1
# こんな感じのリスト処理を
src = ['SPAM', 'HAM', 'EGG']
dest = []
for val in src:
    lowerer = val.lower()
    dest.append(lowerer)  # 全部小文字にして別のリストに突っ込む

# Pythonはこう書ける。かんたん。
src = ['SPAM', 'HAM', 'EGG']
dest = [val.lower() for val in src]
```
リスト内包表記の方が速いらしい。
[Pythonのリスト内包表記の速度](https://qiita.com/intermezzo-fr/items/43f90e07e4cebe63aeb6)


```py:リスト内包#2
# 条件式を指定してリスト生成することも可能
>>> L = [1, 2, 3, 4, 5]
>>> [i for i in L if i % 2]
[1, 3, 5]

# イテラブルオブジェクトの要素を分解して格納することもできる
>>> L = [('a', 1), ('b', 2), ('c', 3)]
>>> [c * i for c, i in L]
['a', 'bb', 'ccc']

# 変数名の先頭に*をつけると、複数要素をリストで受け取れる
>>> L = [(1, 2), (3, 4, 5), (6, 7, 8, 9)]
>>> [first * sum(others) for first, *others in L]
[2, 27, 144]

# 多重ループも可
>>> years = [2019, 2020]
>>> months = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
>>> [(y, m) for y in years
...     for m in months]
[(2019, 1), (2019, 2), (2019, 3), (2019, 4), (2019, 5), (2019, 6), (2019, 7), (2019, 8), (2019, 9), (2019, 10), (2019, 11), (2019, 12), (2020, 1), (2020, 2), (2020, 3), (2020, 4), (2020, 5), (2020, 6), (2020, 7), (2020, 8), (2020, 9), (2020, 10), (2020, 11), (2020, 12)]
```

リストはシーケンス型のオブジェクトなので、
+（連結）や *（繰返し）、比較演算子などの演算子がサポートされている

```py:リストの演算子
# 連結
>>> [1, 2] + [3]
[1, 2, 3]

# 繰り返し
# 固定長のリストを予め用意するような場合はこんな感じ
>>> L = [None] * 5
>>> L
[None, None, None, None, None]
```

```py:シーケンスの+演算子や*演算子は参照をコピーするだけ
# 複数のリストオブジェクトを要素としたリストオブジェクトを作成したい場合、
# こう書けそうな気がする
>>> L = [[]] * 3
>>> L
[[], [], []]

# が、ダメ
>>> L[0].append(1)
>>> L
[[1], [1], [1]]  # 要素は全部同じオブジェクトへの参照

# こんな時にリスト内包表記
>>> L = [[] for i in range(3)]
>>> L[0].append('spam')
>>> L
[['spam'], [], []]
```

```py:リストの累積代入文
# リストオブジェクトの場合、A = A + B と A += B は等価ではない！
>>> A = [0, 1, 2, 3]
>>> B = ['spam']
>>> id(A)
4429687688
>>> A = A + B
>>> A
[0, 1, 2, 3, 'spam']
>>> id(A)
4429687176  # A = A + B では、新しいリストオブジェクトがAに代入される


>>> A = [0, 1, 2, 3]
>>> B = ['spam']
>>> id(A)
4429687688
>>> A += B
>>> A
[0, 1, 2, 3, 'spam']
>>> id(A)
4429687688  # A += B では、変数Aが示しているリストオブジェクトにBの全要素がそのまま追加される
```

メソッドをまとめる必要はないけど、一回やってみる意味で書く。

```py:リストのメソッドあれこれ
# 要素追加
>>> A = [0, 1, 2, 3]
>>> A.append(4)
>>> A
[0, 1, 2, 3, 4]
# ただし要素数が予め分かっている場合は、代入や内包表記を用いたほうが速い
# 参考:https://www.kumilog.net/entry/python-speed-comp

# 要素全削除
>>> A = [0, 1, 2, 3]
>>> A.clear()  # del A[:] と同じ
>>> A
[]

# 同じ要素を持った別のリストオブジェクトを作成
>>> A = [0, 1, 2, 3]
>>> id(A)
4429688712
>>> B = A.copy()  # B = A[:] と同じ
>>> B
[0, 1, 2, 3]
>>> id(B)
4430184776

# 引数と等しい値の要素の数を返す
>>> A = [0, 1, 2, 1]
>>> A.count(1)
2

# イテラブルオブジェクトの全要素を分解してappendする
>>> A = [0, 1, 2]
>>> A.extend('spam')
>>> A
[0, 1, 2, 's', 'p', 'a', 'm']
>>> A[len(A):len(A)] = 'ham'  # これも同じ
>>> A
[0, 1, 2, 's', 'p', 'a', 'm', 'h', 'a', 'm']

# 引数と等しい値の要素を検索して最初に一致した要素のインデックスを返す
>>> A = [3, 4, 5, 3, 4, 5]
>>> A.index(4)
1
>>> A.index(4, 3)     # 探索範囲はスライスの要領で引数指定できる
4
>>> A.index(4, 3, 4)  # 無ければ例外
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: 4 is not in list

# 指定位置への要素追加
>>> L = ['ham']
>>> L.insert(0, 'spam')  # L[0:0] = ['spam'] と同じ
>>> L
['spam', 'ham']
>>> L.insert(-1, 'egg')  # 末尾からの相対位置に追加
>>> L
['spam', 'egg', 'ham']

# 末尾要素をpop（その値を返して要素を削除）
>>> L = [0, 1, 2, 3]
>>> L.pop()
3
>>> L
[0, 1, 2]
>>> L.pop(1)  # インデックス指定も可
1
>>> L
[0, 2]

# 引数と同じ値の要素を検索し、最初に一致した要素を削除する
>>> L = [0, 1, 2, 3, 2]
>>> L.remove(2)
>>> L
[0, 1, 3, 2]

# リストの要素を逆順に置換。新しいリストオブジェクトができるわけではない。
>>> L = [0, 1, 2, 3]
>>> L.reverse()
>>> L
[3, 2, 1, 0]

# リストの要素をソート。新しいリストオブジェクトはつくらない。引数はキーワード専用。
>>> L = ['9', '10', '100']
>>> L.sort()
>>> L
['10', '100', '9']

# keyに関数を指定すると、リストの要素を引数としてkeyを呼び出して戻り値をキーとしてソートする
>>> L.sort(key=int)
>>> L
['9', '10', '100']

# 降順ソート
>>> L.sort(reverse=True)
>>> L
['9', '100', '10']

# pythonのソートは「安定ソート」（ソートのキーが等しい要素同士はソート前後で前後関係が変わらない）
>>> def func(v):return v % 3
... 
>>> L = [1, 2, 3, 4, 5, 6, 7, 8, 9, 0]
>>> L.sort(key=func)
>>> L
[3, 6, 9, 0, 1, 4, 7, 2, 5, 8]  # 3,6,9 - 0,1,4,7 - 2,5,8 の前後関係は変化していない
```