Pythonのお勉強～入門編2～

[前回](https://qiita.com/merito/items/e46a506ae9a020555203)の続きから

# データ構造
## リスト型
l = [1, 2, 3]

というように[]だとリスト型となる。
この後出てくるタプルは()、辞書は{}となるそうで、ここら辺Javaとかと全然違うので少し戸惑う。
でもそのうち慣れるでしょうきっとたぶん。。。
スライスについて勘違いしてたけど、範囲指定するときインデックスの指定じゃなくて配列内の間の位置?を指定している。
['a', 'b', 'c', 'd', 'e', 'f']というリストだとしたら、↓の「|」の位置を指定している。

|a|b|c|d|e|f|

つまり[0:4]は「a b c d e」じゃなくて「a b c d」になるみたい。
気づいてよかった。むしろなぜ気づかなかったのか。

```python:ターミナルで実行
>>> l = [1, 2, 40, 256, 111, 5656, 2, 1]
>>> l
[1, 2, 40, 256, 111, 5656, 2, 1]
>>> l[1]
2
>>> l[-3] # 末尾から数えていく
5656
>>> l[0:2]
[1, 2]
>>> l[:4]
[1, 2, 40, 256]
>>> l[5:]
[5656, 2, 1]
>>> type(l)
<class'list'>
>>> list('hogehagehige')
['h', 'o', 'g', 'e', 'h', 'a', 'g', 'e', 'h', 'i', 'g', 'e']
>>> n = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
>>> n
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
>>> n[::2] # 一個飛ばし
[1, 3, 5, 7, 9]
>>> n[::-1]
[10, 9, 8, 7, 6, 5, 4, 3, 2, 1]
>>> n[::-2] # 末尾から一個飛ばし
[10, 8, 6, 4, 2]
```

## リストの操作
リスト内の値の変更は個人的にはJavaとかよりわかりやすいかもしれない。そうじゃないかもしれない。

```python:ターミナルで実行
>>> s = ['a', 'b', 'c', 'd', 'e', 'f', 'g']
>>> s
['a', 'b', 'c', 'd', 'e', 'f', 'g']
>>> s[0] = 'X'
>>> s
['X', 'b', 'c', 'd', 'e', 'f', 'g']
>>> s[2:5]
['c', 'd', 'e']
>>> s[2:5] = [ 'C', 'D', 'E']
>>> s
['X', 'b', 'C', 'D', 'E', 'f', 'g']
>>> s[2:5] = []
>>> s
['X', 'b', 'f', 'g']
>>> s[:]
['X', 'b', 'f', 'g']
>>> s[:] = []
>>> s
[]
```

リストの末尾に値を追加する場合はappend、追加位置を指定する場合はinsertで追加する。
popは指定した位置の値を取り出し、配列内から削除する。
delは指定した位置の値を配列内から削除する。
removeは指定した値と同じ配列内の値を削除する。配列内に同じ値が複数存在する場合は、頭から探して最初に見つかった値が消される。

なお、delはかなり強力で、インデックスを指定しないで、「del l」というようにして実行すると、変数自体が削除されるので注意。怖い。

```python:ターミナルで実行
>>> n = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
>>> n
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
>>> n.append(100)
>>> n
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 100]
>>> n.insert(0,200)
>>> n
[200, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 100]
>>> n.pop()
100
>>> n
[200, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
>>> n.pop(0)
200
>>> n
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
>>> del n[0]
>>> n
[2, 3, 4, 5, 6, 7, 8, 9, 10]
>>> del n
>>> n
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'n' is not defined
>>> n = [1, 2, 2, 2, 3]
>>> n.remove(2)
>>> n
[1, 2, 2, 3]
>>> n.remove(2)
>>> n.remove(2)
>>> n
[1, 3]
>>> n.remove(2)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: list.remove(x): x not in list
```

配列の結合もできる。

```python:ターミナルで実行
>>> a = ['hoge', 'hage', 'hige']
>>> b = ['dojo', 'dormitory', 'training']
>>> a
['hoge', 'hage', 'hige']
>>> b
['dojo', 'dormitory', 'training']
>>> a += b
>>> a
['hoge', 'hage', 'hige', 'dojo', 'dormitory', 'training']
>>> a = ['hoge', 'hage', 'hige']
>>> a
['hoge', 'hage', 'hige']
>>> b
['dojo', 'dormitory', 'training']
>>> x = a + b
>>> x
['hoge', 'hage', 'hige', 'dojo', 'dormitory', 'training']
>>> a += b
>>> a
['hoge', 'hage', 'hige', 'dojo', 'dormitory', 'training']
>>> x = ['chanko', 'protein']
>>> x
['chanko', 'protein']
>>> x.extend(a)
>>> x
['chanko', 'protein', 'hoge', 'hage', 'hige', 'dojo', 'dormitory', 'training']
```

## リストのメソッド
indexは指定した値を最初に見つけたインデックスを返してくれる。検索する開始位置も指定できる。
countは指定した値が配列内にいくつ存在するかを返してくれる。
sortはソートしてくれる。
等々いろいろある。これも使っていけば嫌でも覚えるでしょうきっとたぶん。

```python:実行
r = [1, 2, 3, 100, 24, 35, 3, 5, 6, 10, 3]
print(r.index(3))
print(r.index(3, 3))

r.sort()
print(r)
r.reverse()
print(r)
````

````python:結果
2
6
[1, 2, 3, 3, 3, 5, 6, 10, 24, 35, 100]
[100, 35, 24, 10, 6, 5, 3, 3, 3, 2, 1]
```

## リストのコピー
copyまたは[:]で値渡しできる。

```python:実行
i = [1, 2, 3, 4, 5]
j = i
j[0] = 100
print('j =', j)
print('i =', i)

x = [1, 2, 3, 4, 5]
y = x.copy()
y[0] = 100
print('y =', y)
print('x =', x)
```

```python:結果
j = [100, 2, 3, 4, 5]
i = [100, 2, 3, 4, 5]
y = [100, 2, 3, 4, 5]
x = [1, 2, 3, 4, 5]
```

## タプル型
t = (1, 2, 3, 1, 2)

というように()だとタプル型になる。
リストと違ってタプルは配列内の値の変更はできない。参照専用みたいな感じかな？
インデックスの指定の仕方等は同じ感じ。

また、
t = 1,

というように変数に入れる値の後ろに「,」を付けるとタプル型となる。
実装するときにintとかにしたいのにうっかり「,」を付けないようにすること。逆もしかり。

```python:ターミナルで実行
>>> t = (1, 2, 3, 4, 1, 2)
>>> t
(1, 2, 3, 4, 1, 2)
>>> type(t)
<class'tuple'>
>>> t[0] = 100
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'tuple' object does not support item assignment
>>> t[0]
1
>>> t[3:5]
(4, 1)
>>> t.index(3)
2
>>> t.index(1, 3)
4
>>> t.count(1)
2
>>> t = 1, 2, 3
>>> type(t)
<class'tuple'>
>>> t = 1,
>>> type(t)
<class'tuple'>
```

タプル内にリストを持たせることができる。
タプル自体は変更追加はできないが、タプル内のリストの操作は可能。
文章にするとややこしいな…

```python:ターミナルで実行
>>> t = ([1, 2, 3], [4, 5, 6])
>>> t
([1, 2, 3], [4, 5, 6])
>>> t[0] = [1]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'tuple' object does not support item assignment
>>> t[0][0]
1
>>> t[0][0] = 100
>>> t[0][0]
100
```

## タプルのアンパッキング
タプルの中身をそのまま変数に展開できるような感じ。
変数の入れ替えとかに便利だそうな。
次項で使いどころの説明もあったが、結局使いどころのイメージがいまいちつかめず…

```python:実行
num_tuple = (10, 20)
print(num_tuple)

x, y = num_tuple
print(x, y)

x, y = (10, 20)
print(x, y)

a = 100
b = 200
a, b = b, a
print(a, b)
```

```python:結果
(10, 20)
10 20
10 20
200 100
```

## タプルの使いどころ
質問をユーザに投げかけて、選択肢の内2つを選択するようなアプリがあったとする。
例だから実装内容は突っ込まない。

質問の選択肢をタプル、回答者の選択結果はリストの変数にいれて出力。

```python:実行
# 質問の選択肢
chose_from_two = ('A', 'B', 'C')

# 回答者の選択結果
answer = []
answer.append('A')
answer.append('C')

print(chose_from_two)
print(answer)
```

```python:結果
('A', 'B', 'C')
['A', 'C']
```


この質問の選択肢をタプルではなくリストにする。
このときに間違って質問の選択肢に回答結果を入れて実装してしまったとする。

```python:実行
# 質問の選択肢をリストにしてみる
chose_from_two = ['A', 'B', 'C']

answer = []

# 回答者の選択結果を間違って選択肢のリストに入れてしまう…
chose_from_two.append('A')
chose_from_two.append('C')

# するとどうなる
print(chose_from_two)
print(answer)
```

```pthon:結果
['A', 'B', 'C', 'A', 'C']
[]
```


とても残念な結果になってしまう。
タプルはappendがないので、そういったことを未然に防ぐためにも使える。
人がやることは何があるかわからない。自分もそう。
本当に意味が分からないバグ満載の書き換えをしてくる人もいるし…(過去に被害にあいましたorz)
pythonの場合は型宣言をしないこともあるのでこういったことは割と重要かもしれない。


## 辞書型
d = {'x': 10, 'y': 20, 3: 20, 4: 'hoge'}

というように{}だと辞書型になる。
キーには文字列だけでなく数値も使える。

```python:ターミナルで実行
>>> d = {'x': 10, 'y': 20}
>>> d
{'x': 10, 'y': 20}
>>> type(d)
<class'dict'>
>>> d['x']
10
>>> d['y']
20
>>> d['x'] = 100
>>> d
{'x': 100, 'y': 20}
>>> d['x'] = 'XXXX'
>>> d
{'x': 'XXXX', 'y': 20}
>>> d['z'] = 200
>>> d
{'x': 'XXXX', 'y': 20, 'z': 200}
>>> d[1] = 10000
>>> d
{'x': 'XXXX', 'y': 20, 'z': 200, 1: 10000}
>>> dict(a=10, b=20)
{'a': 10, 'b': 20}
```

## 辞書型のメソッド
keys()でキーを配列で取り出す。
values()で値を配列で取り出す。
ここら辺はC#とかと同じ感じ。
update(辞書変数)は引数で渡した辞書内のキーの値へ更新、引数内の辞書のキーが存在しない場合は追加する。
sqlのmergeのようなイメージ。あればupdate、なければinsertみたいな。
get(引数)は指定したキーの値を取り出す。
存在しないキーを指定した場合、「None」が返却される。
辞書内から削除するpopやdelもある。

```python:ターミナルで実行
>>> d = {'x': 10, 'y': 20}
>>> d
{'x': 10, 'y': 20}
>>> d.keys()
dict_keys(['x', 'y'])
>>> d.values()
dict_values([10, 20])
>>> d2 = {'x':1000, 'j': 500}
>>> d2
{'x': 1000, 'j': 500}
>>> d.update(d2)
>>> d
{'x': 1000, 'y': 20, 'j': 500}
>>> r = d['hogehoge']
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
KeyError: 'hogehoge'
>>> r = d.get('hogehoge')
>>> r
>>> type(r)
<class'NoneType'>
```

## 辞書のコピー
リストと同様。

```python:実行
# 参照渡し
x = {'a': 1}
y = x
y['a'] = 1000
print(x)
print(y)

# 値渡し
x = {'a': 1}
y = x.copy()
y['a'] = 1000
print(x)
print(y)
```

```python:結果
{'a': 1000}
{'a': 1000}
{'a': 1}
{'a': 1000}
```

## 辞書の使いどころ
例えば果物の値段の辞書型リストがあるとする。
どの果物が値段が何か知りたいときに果物(キー)を指定することで値段(値)を出力できる。

```python:実行
# 果物ごとの値段のリスト(辞書)
fruits = {
    'apple': 100,
    'banana': 200,
    'orange': 300,
}

# バナナの値段を知りたい
print(fruits['banana'])
```

```python:結果
200
```

これをリストでやろうとすると…

```python:実行
# 果物ごとの値段のリスト
fruits = [
    ('apple', 100),
    ('banana', 200),
    ('orange', 300)
]

# バナナの値段を知りたいけどリストのどこにあるかわからないし…
print(fruits[0])
print(fruits[1])
print(fruits[2])
```

```python:結果
('apple', 100)
('banana', 200)
('orange', 300)
```

このようにすごく無駄なことしないといけなくなる。(forでやれよの突っ込みはノーセンキュー)
ものと値の紐付きがあるものは辞書型を、単純に値を格納する場合はリストみたいな感じ。(少し違うけど)
というかここら辺は他の言語やっていれば使いどころはわかるはず…だよね…？


## 集合型
a = {1, 2, 2, 3, 3, 3, 3, 4, 4, 5}

のように{}で辞書型のように「キー：値」の形にせず、リスト型と同じようにすると集合型になる。
実際に変数に作られるものは下記のように重複した値は入らないようになっている。

```python:ターミナルで実行
>>> a = {1, 2, 2, 3, 4, 4, 4, 5, 6}
>>> a
{1, 2, 3, 4, 5, 6}
>>> type(a)
<class'set'>
```

集合型は論理和や排他的論理和など数学の集合を表現できる感じ。
今こそ高校や大学で学んだ数学の知識を活かすとき(完璧に忘れてます)

```pyrhon:ターミナルで実行
>>> a = {'hage', 'hige', 'huge', 'hege', 'hoge'}
>>> b = {'hige', 'hege', 'noge'}
>>> a
{'hege', 'huge', 'hige', 'hage', 'hoge'}
>>> b
{'noge', 'hege', 'hige'}
>>> a - b # aからbにある値を除く
{'hage', 'huge', 'hoge'}
>>> b - a # bからaにある値を除くく
{'noge'}
>>> a & b # aとb両方に存在する値
{'hege', 'hige'}
>>> a | b # aとbどちらかに存在する値
{'hage', 'hoge', 'noge', 'hege', 'huge', 'hige'}
>>> a ^ b # aとbどちらかに存在する値で、aとbで同じ値は除く
{'hage', 'hoge', 'noge', 'huge'}
```


## 集合のメソッド
リストなどと同じようにadd、removeなどがある。
また、集合はリストと異なりインデックスがないので注意すること。
differenceなど前項の「a-b」と同じことができるメソッドなど色々あるようなので後日調べる。

```python:ターミナルで実行
>>> s = {1, 2, 3, 4, 5}
>>> s
{1, 2, 3, 4, 5}
>>> s[0] #集合はインデックスがないためエラーになる
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'set' object does not support indexing
>>> s.add(6)
>>> s
{1, 2, 3, 4, 5, 6}
>>> s.add(6)
>>> s
{1, 2, 3, 4, 5, 6}
>>> s.remove(6)
>>> s
{1, 2, 3, 4, 5}
```

## 集合の使いどころ
例えば自分とAさんの共通の友人を出してみる。

```python:実行
my_friends = {'A', 'B', 'C'}
A_friends = {'B', 'D', 'E', 'F'}
print(my_friends & A_friends)
```

```python:結果
{'B'}
```

例えば同じ種類含めた果物をいくつか買った時に、どの種類の果物があるか調べたいとする。

```python:実行
f = ['apple', 'banana', 'apple', 'banana']
kind = set(f)
print(kind)
```

```python:結果
{'banana', 'apple'}
```


基本的なところが終わったら一度pythonの公式ドキュメントにも目を通したいなと思いつつ、たぶん動画での学習優先しそう。
続きはまた後日。