Pythonのリストについて


# 概要
【内容】
　・Pythonにおけるリストで実施出来ることを一通り説明する。

【想定読者】
　・Pythonのリストに関する文法・出来ることが知りたい方

【参考サイト】
　・Python全般の文法について用途別に記載しているサイト

https://note.nkmk.me/python-post-summary/

# 1．Pythonの型宣言

Javaについては変数を定義する際に、型宣言が必要。


```java:sample.java
int x = 10;
String y = "abc";
List<String> list = new ArrayList<>();

```

Pythonでは型宣言は必要ない。
右辺に記載されている内容によって、数値（整数、小数）、文字列、リスト型などを区別する。

```python:sample.py
# 型を定義しなくても、コンパイルが通過する。
x = 10
y = "abc"
list = ["Mon","Tue","Wed","Thu","Fri","Sat","Sun"]

```

# 2．Pythonのリスト、タプル、辞書、セットについて
これらはすべて、括弧の付け方によって区別する。
リスト：**[** 値1, 値2, 値3...**]** 
タプル：**(** 値1, 値2, 値3...**)** 
辞書：**{** "キー1":値1, "キー2":値2, "キー3":値3...**}** 
セット：**{** 値1, 値2, 値3... **}**

## 2-1．リスト

基本的な定義や要素の取得の書き方は、Javaの配列に近い。

```python:list1.py
num_list = [2011, 2012, 2013, 2014, 2015]  # リストの作成
print(num_list [0])  # 先頭から1番目の要素　⇒　2011が出力
print(num_list [1])  # 先頭から2番目の要素　⇒　2012が出力
print(num_list [2])  # 先頭から3番目の要素　⇒　2013が出力

```

Pythonのリスト（タプル、辞書、セットも基本的に同様）の特徴的な点としては、
1つのリストの中にあらゆる変数、文字列、整数、小数すべて含めることが出来るところである。

```python:list2.py
num = 100
list_a = [num, 2017, 20.1, "Hello", "Hi"]
print(list_a)  # [100, 2017, 20.1, 'Hello', 'Hi']
```

リストの中にリストを格納することも出来る。
また、ネストされている要素にアクセスすることも可能。

```python:list3.py
list_b =[[2012, 2013, 2014], [2015, 2016, 2017]]
print(list_b[0]) #[2012, 2013, 2014]
print(list_b[1][1]) #2016
```

もちろん、リストとしての機能である、
要素の変更・追加・削除を実行することも可能。

```python:list4.py
list_c = ["Py", 543.21, 79, "thon", [2018, 2019, 2020]]
print(list_c)  #['Py', 543.21, 79, 'thon', [2018, 2019, 2020]]

# 要素の変更
list_c[2] = 99
print(list_c)  #['Py', 543.21, 99, 'thon', [2018, 2019, 2020]]

# 要素の追加
list_c.append(2021)
print(list_c)  #['Py', 543.21, 99, 'thon', [2018, 2019, 2020], 2021]

# 要素の削除（インデックス指定）
list_c.pop(1)
print(list_c)  #['Py', 99, 'thon', [2018, 2019, 2020], 2021]

# 要素の削除（値指定）
list_c.remove('thon')
print(list_c)  #['Py', 99, [2018, 2019, 2020], 2021]
```

## 2-2．タプル
タプルはリストと同じく複数の値をまとめて扱いたいときに利用するが、
作成後の要素の追加や削除、入れ替えなどが出来なくなっている。
要素を変更する予定が無い場合は、リストよりもタプルを使用する方がベターとされている。（リスト版の定数のような扱い）

```python:tuple.py
tuple = (2012, 2013, 2014)  # タプルの作成
print(tuple) #表示は可能　(2012, 2013, 2014)

# ただし変更も追加も不可（実行時エラー）
tuple[1] = 2016  # エラーが発生
print(tuple)
tuple.append(2015)  # エラーが発生
print(tuple)
```
実行時結果
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2352782/82b82e21-7210-0547-5000-82ded874dbfc.png)

## 2-3．辞書
Javaでいうマップに該当するもの。
辞書は、キーと値の組合せでデータを格納する。　
文字列をキーとして辞書を作成し、値の取得や入れ替え、要素の追加を実行することが可能。
他の扱いはリストに準拠する。
以下は、Pythonの辞書を扱う例。

```python:dict1.py
dict = {"Taro":1985, "Hanako":1986}  # 辞書の作成
print(dict["Taro"])   # "Taro"のキーを持つ値を取得　1985
dict["Hanako"] = 1987  # 要素の入れ替え
print(dict) # {'Taro': 1985, 'Hanako': 1987}
dict["Jiro"] = 1989  # 要素の追加　
print(dict) # {'Taro': 1985, 'Hanako': 1987, 'Jiro': 1989}
```

以下の通りネストを組んで定義、実行することも可能。（辞書として利用すべきかは別として）

```python:dict2.py
g = {'geo':"sgr",'hir':"mak"}
h = 'sample'
f = {'alpha':"Py", 'beta':543.21, 'gamma':79, 'delta':"thon",
         'epsilon':[2018, 2019, 2020],'forte':("shinta",12.21,20),'gol':g,h:g}
print(f['forte'])  # ('shinta', 12.21, 20)
print(f['gol']['hir']) # mak
print(f[h]['geo']) # sgr
print(f['sample']) # {'geo': 'sgr', 'hir': 'mak'}
```

## 2-4 ．セット
Javaのセット（Set）に該当する。
重複を排除して保有する。タプルのような複数値を持つ型でも保有できる。
この際、元要素の**順序は考慮されない**。

```python:set1.py
set = {1.23, 'a', (1, 2, 3), 1.4, 1.4, (1, 3, 2), 1.6, 'a', 1.6, 2.0, 'b', (1, 2, 3)} 
print(set) # {1.23, 1.4, 'a', 1.6, (1, 3, 2), 2.0, (1, 2, 3), 'b'}

```

注意点は2つ。
①上記の例の通り、セットについても複数の型を同時に含むことが出来るが、
リストのような動的に要素を変更できる項目は対象外。

```python:set2.py
set = {[0, 1, 2] , 2.5, 1} # list型を含むとエラーになる
```
実行結果
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2352782/2b09f8e8-107b-3904-8482-679be93589a8.png)

②intやfloatのように異なる型でも値が等価であれば重複していると見なされる。
文字列の「1」は等価とは判定されないが、数値型に変換していれば等価として扱われる。

```python:set3.py
set = {1.0, 1.01, 1.00, 1.02, 1, '1' , float('1.01')}
print(set) # {1.02, 1.0, '1', 1.01}
```

# 3. リスト要素へのアクセス

Pythonにおけるリストの優れている点として、要素へのアクセスがかなり柔軟にできる点が挙げられる。

## 3-1.複数要素の抜き出し（スライス）
list型項目の一部を抜き出す（スライスする）ような要素を生成出来る。
スライスでは選択範囲の開始位置startと終了位置stopを[start:stop]のように書く。
start <= x < stopの範囲が選択される。start番目の値は含まれるがstop番目の値は含まれないので注意。
なお、指定しない場合でもエラーにはならない。

```python:list5.py
list = ["Py", 543.21, 79, "thon", [2018, 2019, 2020]]
# 2番目から4番目までの要素をまとめて抜き取る
print(list[1:4]) # [543.21, 79, 'thon']

# 3番目以降の要素をまとめて抜き取る
print(list[2:]) # [79, 'thon', [2018, 2019, 2020]]

# 3番目までの要素をまとめて抜き取る
print(list[:3]) # ['Py', 543.21, 79]

# 始点も終点も指定しないと全部抜き取る
print(list[:]) # ['Py', 543.21, 79, 'thon', [2018, 2019, 2020]]

# 指定した終点が要素数を超えていたとしてもエラーにはならない
print(list[2:10]) # [79, 'thon', [2018, 2019, 2020]]

```
## 3-2.末尾からの抜き出し
負の値を指定することで、末尾からの指定が可能。

```python:list6.py
list = ["Py", 543.21, 79, "thon", [2018, 2019, 2020]]
# 末尾の要素を抜き取る
print(list[-1]) # [2018, 2019, 2020]

# 複数指定も可能
print(list[-3:]) # [79, 'thon', [2018, 2019, 2020]]

# 正の値と負の値を混在させることも可能
print(list[2:-1]) # [79, 'thon']

```

## 3-3.要素を歯抜けで取得する
スライスでは3番目の要素として増分値を指定することが出来る。
これにより、奇数（偶数）番目の要素だけを抜き出したりも出来る。

```python:list7.py
list = ["Py", 543.21, 79, "thon", [2018, 2019, 2020]]
# 奇数番目の要素だけを抜き出す
# 始点と終点を指定せず、増分を「2」とすることで、1、3、5……と取得していく。
print(list[::2]) # ['Py', 79, [2018, 2019, 2020]]

# 増分値を負の値で指定することで、逆順で取得することも可能
print(list[::-1]) #[[2018, 2019, 2020], 'thon', 79, 543.21, 'Py']
```
## 3-4.複数要素の追加・変更
スライスを利用することで複数の要素を一度に追加したり変更することが出来る。

```python:list8.py
l = [10,20,30] 
print(l) # [10, 20, 30]

# 末尾に追加
l.extend([40,"extend",[10,"sample"]]) 
print(l) # [10, 20, 30, 40, 'extend', [10, 'sample']]
# 途中に追加
l[1:1] = [50, "test"] 
print(l) # [10, 50, 'test', 20, 30, 40, 'extend', [10, 'sample']]
# 要素の上書き
l[1:3] = [60, "exam"]
print(l) # [10, 60, 'exam', 20, 30, 40, 'extend', [10, 'sample']]
# 要素の削除
l[1:3] = [] 
print(l) # [10, 20, 30, 40, 'extend', [10, 'sample']]
```

## 3-5.多次元配列の要素抜き出し
スライスは多次元配列についても同様に実行できる。
その場合、取得された各要素はリストとなる。

```python:list9.py
# 二次元配列を生成
list_2d = [[0, 1, 2], [3, 4, 5], [6, 7, 8], [9, 10, 11]]

#二次元配列についてもスライスの作成が可能
print(list_2d[1:3]) # [[3, 4, 5], [6, 7, 8]]

#拡張for文のような書き方を利用し、細かい要素の抜き出しが可能
print([list[::2] for list in list_2d[1::2]]) # [[3, 5], [9, 11]]
```
**注意点**
スライスされた要素の中にリストや辞書など、要素の追加、更新が出来るような要素があった場合、
スライス先の内容を修正した際に、スライス元のオブジェクトも変更されてしまう。

```python:list10.py
# リストが要素内に存在しているリストを作成
x = ["Py", 543.21, 79, "thon", [2018, 2019, 2020]]
a = x[4]
print(a) # [2018, 2019, 2020]
print(x) # ['Py', 543.21, 79, 'thon', [2018, 2019, 2020]]
a[1] = 2021 #コピーした変数の要素を置き換える
print(a) # [2018, 2021, 2020]]
print(x) # ['Py', 543.21, 79, 'thon', [2018, 2021, 2020]]

```

防止策としては、標準ライブラリのcopyモジュールのdeepcopy()を利用してコピーを生成する。

```python:list11.py
import copy

# リストが要素内に存在しているリストを作成
x = ["Py", 543.21, 79, "thon", [2018, 2019, 2020]]
a = copy.deepcopy(x[4])
print(a) # [2018, 2019, 2020]
print(x) # ['Py', 543.21, 79, 'thon', [2018, 2019, 2020]]
a[1] = 2021 #コピーした変数の要素を置き換える
print(a) # [2018, 2021, 2020]]
print(x) # ['Py', 543.21, 79, 'thon', [2018, 2019, 2020]]

```