Python組み込み型の勉強：リスト（list）

## メソッド一覧
```python
# In
[v for v in dir([]) if not v.startswith('_')]

# Out
['append',
 'clear',
 'copy',
 'count',
 'extend',
 'index',
 'insert',
 'pop',
 'remove',
 'reverse',
 'sort']
```

## 各メソッドの挙動
### append
末尾に要素を追加する。

```python
# In
l = []

l.append('foo')
print(l)

l.append('bar')
print(l)

# Out
['foo']
['foo', 'bar']
```

### clear
全ての要素を削除する。

```python
# In
l = ['foo'] * 10

print(l)

l.clear()

print(l)

# Out
['foo', 'foo', 'foo', 'foo', 'foo', 'foo', 'foo', 'foo', 'foo', 'foo']
[]
```

### copy
同じ要素を持つリストを作成する。

```python
#In
original = ['foo', 'bar']
clone = original.copy()

print(clone)
print(original)

# Out
['foo', 'bar']
['foo', 'bar']
```

### count
指定された値と等しい要素をいくつ含むかを返す。

```python
# In
l = ['foo', 'bar', 'baz', 'foo']

print(l.count('foo'))
print(l.count('bar'))

# Out
2
1
```

### extend
渡された`iterable`オブジェクト(リストに限らない)の全要素を末尾に追加する。

```python
# In
case = ['pencil']
items = ['eraser', 'ruler']

case.extend(items)
case

# Out
['pencil', 'eraser', 'ruler']
```

strも`iterable`なのでextendできる。

```python
# In
l = []
l.extend('hello, world!')
l

# Out
['h', 'e', 'l', 'l', 'o', ',', ' ', 'w', 'o', 'r', 'l', 'd', '!']
```

### index
渡された値と等しい要素が見つかった最初のインデックスを返す。

```python
# In
l = ['foo', 'bar', 'baz', 'foo']
l.index('bar')

# Out
1
```

検索の開始位置と終了位置(省略可)を指定できる。

```python
# In
l = ['foo', 'bar', 'baz', 'foo']
l.index('foo', 1)

# Out
3
```

要素が見つからなかった場合は`ValueError`例外が起きる。

```python
# In
l = ['foo', 'bar', 'baz', 'foo']
l.index('xyz')

# Out
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-39-82e2a7ec8599> in <module>
      1 l = ['foo', 'bar', 'baz', 'foo']
----> 2 l.index('xyz')

ValueError: 'xyz' is not in list
```

### insert
指定した位置に要素を挿入する。

```python
# In
l = ['foo', 'baz']
l.insert(1, 'bar')
l

# Out
['foo', 'bar', 'baz']
```

範囲外のインデックスを指定してもエラーにはならず、末尾に追加される。

```python
# In
l = ['foo', 'baz']
l.insert(99, 'bar')
l

# Out
['foo', 'baz', 'bar']
```

### pop
指定したインデックスの要素を取り出す。取り出した要素は大量のリストから削除される。

インデックスを指定しない場合は末尾の要素が取り出される。

```python
# In
l = ['foo', 'bar', 'baz']
print(l.pop(), l)
print(l.pop(), l)
print(l.pop(), l)

# Out
baz ['foo', 'bar']
bar ['foo']
foo []
```

先頭の要素を取り出したい場合はインデックスに`0`を指定する。

```python
# In
l = ['first', 'second']
print(l.pop(0), l)
print(l.pop(0), l)

# Out
first ['second']
second []
```

空リストまたは範囲外のインデックスに対してpopすると`IndexError`例外が起きる。

```python
# In
l = ['foo']
l.pop(1)

# Out
---------------------------------------------------------------------------
IndexError                                Traceback (most recent call last)
<ipython-input-56-2141ce645f3e> in <module>
      1 l = ['foo']
----> 2 l.pop(1)

IndexError: pop index out of range
```

### remove
渡された値と等しい要素をリストの先頭から探索し、最初に一致した要素を削除する。

```python
# In
l = ['foo', 'bar', 'baz', 'bar']
l.remove('bar')
l

# Out
['foo', 'baz', 'bar']
```

要素が見つからなかった場合は`ValueError`例外が起きる。

```python
# In
l = ['foo', 'bar', 'baz', 'bar']
l.remove('xyz')

# Out
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-62-ebb9d3e7094a> in <module>
      1 l = ['foo', 'bar', 'baz', 'bar']
----> 2 l.remove('xyz')

ValueError: list.remove(x): x not in list
```

### reverse
中身を逆順に入れ替える。新しいリストオブジェクトを作る訳ではないので、`reverse()`はNoneを返す。

```python
# In
l = ['foo', 'bar', 'baz']
print(l.reverse())
l

# Out
None
['baz', 'bar', 'foo']
```

### sort
要素をソートする。

新しいリストオブジェクトを作る訳ではない点に注意。

```python
# In
l = [3, 2, 1]
l.sort()
l

# Out
[1, 2, 3]
```

```python
# In
l = ['c', 'b', 'a']
l.sort()
l

# Out
['a', 'b', 'c']
```

デフォルトは`昇順`でソートされる。`reverse=True`を指定すると降順でソートされる。

```python
# In
l = [3, 2, 1]
l.sort(reverse=True)
l

# Out
[3, 2, 1]
```

`key`名前付き引数に`ソート関数`を渡す事で値に基づいてソート処理を制御できる様になる。

`ソート関数`は引数をひとつ取り、その値がどれだけ小さいか、あるいは大きいかを返す事でソート処理を制御できる。

例えば、以下の`sorter`ソート関数はどのような引数に対しても`1`を返すので大小の区別が無く、ソート処理の意味をなさない。

```python
# In
def sorter(i):
    return 1
    
ages = [1, 55, 31, 101, 21, 66]
ages.sort(key=sorter)
ages

# Out
[1, 55, 31, 101, 21, 66]
```

ここで、sorter関数を`50以上`の値を優遇(=小さい値を返す)するように書き換える。

```python
# In
def sorter(i):
    if i >= 50:
        return -1
    else:
        return 1
    
ages = [1, 55, 31, 101, 21, 66]
ages.sort(key=sorter)
ages

# Out
[55, 101, 66, 1, 31, 21]
```

このように`key`引数を使うことでソート処理を自在に制御できる。

## 特定の要素が含まれるかチェック
`in`で簡単にチェックできる。

```python
# In
'bar' in ['foo', 'bar', 'baz']

# Out
True
```

## リストの作成方法
### 同一要素の任意長のリストを作成
```python
# In
l = ['foo'] * 10
l

# Out
['foo', 'foo', 'foo', 'foo', 'foo', 'foo', 'foo', 'foo', 'foo', 'foo']
```

### iterableなオブジェクトを基に作成
```python
# In
print(list('foo, bar, baz'))  # 文字列
print(list(('foo', 'bar', 'baz')))  # タプル
print(list({'foo': 1, 'bar': 2, 'baz': 3}))  # 辞書

# Out
['f', 'o', 'o', ',', ' ', 'b', 'a', 'r', ',', ' ', 'b', 'a', 'z']
['foo', 'bar', 'baz']
['foo', 'bar', 'baz']
```

### コピー
```python
# In
original = [1, 2, 3]
print(original.copy())  # copyメソッド
print(original[:])  # スライス

# Out
[1, 2, 3]
[1, 2, 3]
```

## リストの比較
### 単純な比較
プリミティブ(intやstr)な要素のみで構成されるリストは単純に比較できる。

```python
# In
[1, True, 'foo'] == [1, True, 'foo']

# Out
True
```

### 非プリミティブな要素(オブジェクト)を含む場合の比較
含まれるオブジェクトの`__eq__`メソッドの返り値に依存する。

例えば以下のPersonクラスは`__eq__`メソッドを実装していないので、リストに含まれる要素はビジネスロジック的には同一人物にも関わらず比較結果はFalseである。

```python
# In
class Person:
    def __init__(self, name):
        self.name = name


[Person('Yamada'), Person('Sato')] == [Person('Yamada'), Person('Sato')]

# Out
False
```

`__eq__`メソッドに比較処理を実装する事で、リスト同時の比較処理も期待通りに働くようになる。

```python
class Person:
    def __init__(self, name):
        self.name = name
        
    def __eq__(self, other):
        return self.name == other


[Person('Yamada'), Person('Sato')] == [Person('Yamada'), Person('Sato')]
```