Pythonのデータ型についてまとめてみた

# はじめに
Pythonのデータ型がいろいろあってPythonをやっていくうちにまとめないと最初は分からなくなりそうと感じまとめた記事になっています。

想定の倍以上の量になってしまったため、間違いがあるかもしれません。
見つけ次第やっつけていく予定です。
ごめんなさい。

2018/04/04 23時頃
@shiracamus さんよりいただいた[コメント](https://qiita.com/tasogarei/items/2b986248318878c26475#comment-8b44f6e2a077792b98db)の修正を行いました。

# 環境
Python 3.6.4
Visual Stdio Code

# データ型の基本
* 宣言と同時に型が分かるように初期化が必要です。
 
``` python
# これは実行時にエラーとなる
value
# こうする必要がある
value = 0
```

* 変数には型という概念がないようで、いつ何時どんな型でも入れ替え自由です(ただの入れ物って感じみたいです)

``` python
# これはエラーになりません
value = 0
value = "string"
print(value)
```

# boolean型(真偽値)
0 or Flaseが偽
1 or Trueが真
です。

`bool`関数で調べてみると0が`False`でそれ以外が`True`になっています。
マイナスでもTrueなのはちょっと驚きました。

コメントで条件分岐では`bool`で直して真偽判断していると教えていただきました。
真偽判断をわざわざ色々記載いただいていたので、こちらで再掲します。

``` python
>>> bool(0)
False
>>> bool(0.)
False
>>> bool(0j)
False
>>> bool(.1)
True
>>> bool(1)
True
>>> bool(1j)
True)
>>> bool(2)
True
>>> bool('')
False
>>> bool('a')
True
>>> bool([])
False
>>> bool([0])
True
>>> bool(())
False
>>> bool((0,))
True
>>> bool({})
False
>>> bool({0})
True
```
# number型(数値)
数字を扱うデータ型です。
小数点と整数を扱うデータ型で計算する際中に2つ混ざっても問題ありません。

``` python
value = 1.2 + 1 - (-1)
print(value)
>>> 3.2
```

## n進数
扱える進数には2進数、8進数、10進数、16進数が存在しています。

``` python
print(0b11) # 2進数は0bを先頭に付ける
print(0o11) # 8進数は0oを先頭に付ける
print(0x11) # 16進数は0xを先頭に付ける
>>>3
>>>9
>>>17
```

## 計算
計算の優先順位については他の言語と変わりません。
書き方もほとんど同じですが、調べたので記載します。

|  | 記述方法 | 備考 |
|----- | --------|---- |
| 加算 | 1 + 1   | |
| 減算 | 1 - 1   | |
| 乗算 | 1 * 1   | |
| 除算 | 1 / 1   | 浮動小数点ありの計算結果になる。整数の場合は1//1と記述する。 |
| 除算(余り) | 1 % 1 | |
| べき乗 | 1 ** 1 | |

他にもビット演算、シフト演算も可能となっていますが、あんまり使用しないと思ったので記載しません。

あと、Decimal型、Fraction型(有理数)、複素数なんかも対応していますが、書き始めると大変なことになる気がするので今回は書きません。(そのうち書くかもしれません)

## 数値型に変換
変換はint関数、float関数で行います。

``` python
print(int("1") + int("2"))
print(float("1.1") + int("2"))
>>> 3
>>> 3.1
```

整数をfloat関数で変換しても問題ありませんが

``` python
print(float("1"))
>>> 1.0
```

小数点付きの文字列をint関数に指定するとエラーになってしまうので注意が必要です。

``` python
print(int("1.1"))
>>> ValueError: invalid literal for int() with base 10: '1.1'
```

その場合は小数点付きの数値に直してからint関数でさらに変換させます。

``` python
print(int(float("1.1")))
>>> 1
```

ちなみに負の数は素直に変換が可能となっているようです。

``` python
print(int("-1"))
>>> -1
```

絶対値が必要な場合はabs関数で変換できます。
ただし、数値型じゃないとエラーになるので数値型への変換は必要です。

``` python
print(abs(int("-1")))
>>> 1
```

# String型(文字列)
文字列を扱うデータ型です。
文字列をシングルクォート、ダブルクォートで囲むことが出来、クォートは1つもしくは3つで構成されます。

``` python
print("aaa") # 1つは改行なし
print('aaa') # 1つは改行なし
print('''aaa
aaa
bbb''') # 3つは改行を含める
print("""aaa
aaa
bbb
""")# 3つは改行を含める
>>> aaa
>>> aaa
>>> aaa
aaa
bbb
>>> aaa
aaa
bbb
```

1つしか記述していないのに改行すると、エラーです。
シングルとダブルの使い分けは結構好みぐらいな感じに見えます。
シングルクォートの時はダブルクォータのエスケープがいらないといったことは可能ですが、エスケープしてあげれば普通に表現可能なので、わざわざ使い分ける必要かなというのが感想です。

``` python
string = "bbb\""
print(string)
>>> bbb"
```

改行やタブなんかのエスケープ文字も埋め込んでprint可能です。
なので、3つでやるのも見づらくなるだけかなとか思ってます。

``` python
string = "bbb\n"
print(string + string)
>>> bbb
bbb
string = "bbb\tbbb"
print(string)
>>> bbb     bbb
```

## Charcter
Pythonには明示的なCharacterがないので、1文字もStringです。
文字列から特定のIndexにある文字を取り出すことは可能となっています。

``` python
string = "bbb"
print(string[1]) # indexを指定すると1文字
print(string[1:3] # indexの開始と終わりを指定するとその分取得してくれる
>>> b
>>> bb
```

ただし、1文字の文字列が文字として扱われることもあるとのことです。
コメントで`ord`関数の例をいただいたので、確かにこれは1文字をUnicodeコードポイントを表す整数に変換する関数で2文字以上入れた場合はエラーとなってしまいました。
(下のソースはコメントでいただいた例をそのまま載せてます)

``` python
>>> ord('a')
97
>>> ord('aa')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: ord() expected a character, but string of length 2 found
```


## 文字列型に変換
変換はstr関数を使用して可能です。

``` python
print(str(1).replace("1","2"))
>>> 2
```

デフォルトのエンコードはUTF-8となっています。他も指定可能ですが、指定した場合はbytesもしくはbytearrayでなくてはいけません。

``` python
print(str(1, "Shitf-Jis"))
> TypeError: decoding to str: need a bytes-like object, int found
```

# Byte型(バイト)
Byteを扱うデータ型です。
見ている限りでは、自分は余り使う機会がないかなという感じな型です。
作るにはbytearrayかbytesで作り出します。
違いは前者がmutableで後者がimmutableです。
使える組み込み関数が違うのと性質が違うので、きちんと使い分けましょう。

``` python
abd = bytearray(b'aaa')
print(list(abd))
>>> [97, 97, 97]

abd = bytes(b'aaa')
print(list(abd))
>>> [97, 97, 97]
```

関数呼び出しの外にある`b`は接頭辞というらしいです。
byte型であることを明示することになっています。
ない場合はエラーになってしまいます。

```
abd = bytes('aaa')
print(list(abd))
>>> TypeError: string argument without an encoding
```

エラーになる原因としては、デフォルト(bという接頭辞を付与した場合)のエンコードがASCIIですが、上記の例ではUTF-8文字`aaa`が渡されたためにエラーとなっています。
Python2系では暗黙的に変換されるそうですが、3系から変換されないためにByte型とString型の比較は偽です。

``` python
if(b'aaa' == 'aaa'):
    print("真")
else:
    print("偽")
>>> 偽
```

それでは文字列を変換したい場合ですが、引数としてエンコードを渡せるので渡してやるとエラーにならずに変換されます。

``` python
abd = bytes('aaa', 'UTF-8')
print(list(abd))
>>> [97, 97, 97]
```

エンコード付きで変換してやると、ASCIIではエラーになる日本語も変換してあげることが可能です。

``` python
abd = bytearray('あいえうお', 'UTF-8')
print(list(abd))
>>> [227, 129, 130, 227, 129, 132, 227, 129, 136, 227, 129, 134, 227, 129, 138]
```
``` python
abd = bytes(b 'あいえうお')
print(list(abd))
>>> SyntaxError: invalid syntax
```

逆にエンコード無しで変換しようとした場合、文字列から直接変換しようとするとASCIIじゃないので怒られるので注意が必要です。

``` python
abd = bytes('aaa')
print(list(abd))
>>> TypeError: string argument without an encoding
```

## Hex(16進数)
Byte配列の場合は16進数とのやり取りが行われることが多いですが、文字列とのやり取りのみのようです。

``` python
abd = bytearray.fromhex('f0 f1f2')
print(list(abd))
>>> [240, 241, 242]
```

``` python
abd = bytearray(b'\xf0\xf1\xf2').hex()
print(abd)
>>> f0f1f2
```

## Byte型に変換
bytesとかbytearrayが文字列からの変換みたいなものになっていますが、bytes(immutable)であれば`encode`と`decode`で変換可能でした。
デフォルトエンコードがUTF-8になっているので、こっちの方が明示的で良さそうです。

``` python
value = 'abc'.encode()
print(value)
print(value.decode())
>>> b'abc'
>>> abc
```

数字型はやり方が別でbytesやbytearrayみたいな関数ではなくto_bytesを使います。

``` python
value = 1
print(value.to_bytes(2, 'big'))
>>> b'\x00\x01'
```

ただしint(整数)である必要があります。
float(小数)には関数が定義されていません。

``` python
value = 1.1
print(value.to_bytes(2, 'big'))
>>> AttributeError: 'float' object has no attribute 'to_bytes'
```

byte配列からintも戻せます。
こちらも整数のみとなっています(intのclassmethod)。

``` python
value = 1
print(int.from_bytes(value.to_bytes(2, 'big'), 'big'))
>>> 1
```

# List型 (リスト)
なんとなくリストをばしばしいじるとプログラムしている感じが出てくるのは自分だけでしょうか。
リストは複数の要素をまとめることが出来る型となっていてmutableなオブジェクトとなっています。

``` python
list = [1,2,3]
print(list)
>>> [1, 2, 3]
```

少し問題となりそうなのが、このリストは複数の型が混在していても問題なく動作してしまいます。
問題となる場面が出てきてもおかしくないので、自分はそんなに好きじゃない挙動です。

``` python
list = [1,"2",3]
for data in list:
    print(type(data))
>>> <class 'int'>
<class 'str'>
<class 'int'>
```

リスト内部の特定要素を取得したい場合はインデックス表記やスライス表記を使用して取り出すことが可能となっています。

``` python
list = [1,2,3]
print(list[0])
print(list[1])
print(list[2])
>>> 1
>>> 2
>>> 3
```

```
list = [1,2,3]
print(list[0:1]) 
print(list[1:2])
print(list[2:3])
>>> [1]
>>> [2]
>>> [3]
```

## List in List ...
リスト内のオブジェクトは特に問題なく全ての要素を扱えるようなので、List in List in Listみたいなことも可能です。

``` python
list = [[[1,2,3],2,3],2,3]
print(list)
>>> [[[1, 2, 3], 2, 3], 2, 3]
```

## Listの中身を入れ替える
中身を入れ替える場合は要素を取り出す時と同じようにインデックス表記やスライス表記で入れ替えを行うことが出来ます。

``` python
list = [[[1,2,3],2,3],2,3]
list[0] = 1
print(list)
>>> [1, 2, 3]
```

``` python
list = [[[1,2,3],2,3],2,3]
list[0:2] = [1,1]
print(list)
```

スライスで入れ替える場合は要素を入れ替える分Listを渡す必要があります。

``` python
list = [1,2,3]
list[0:2] = [4,5,6,7]
print(list)
>>> [4, 5, 6, 7, 3]
```

多い分にはうまくやってくれているので、単なる入れ替えだけではなく挿入も一緒にやってくれるようです。

``` python
list = [1,2,3]
list[0:2] = 1
print(list)
>>> TypeError: can only assign an iterable
```

ただしスライス表記の場合はListを渡す必要があるので注意してください。

``` python
list = [1,2,3]
list[0:0] = [4]
print(list)
>>> [4, 1, 2, 3]
```

ちなみにスライス表記で同じ箇所を指定した場合は挿入動作だけしてくれます。


## Listに要素を追加する
List要素を1つ追加するか複数追加するかでやり方が変わります。

``` python
list = [1,2,3]
list.append(4)
print(list)
>>> [1, 2, 3, 4]
```

1つの場合は`append`を使用して追加します。

``` python
list = [1,2,3]
list.append([4,5])
print(list)
>>> [1, 2, 3, [4, 5]]
```

Listを入れても1つの要素として扱われます。

``` python
list = [1,2,3]
list.extend([4,5])
print(list)
>>> [1, 2, 3, 4, 5]
```

``` python
list = [1,2,3]
list += [4,5]
print(list)
```

複数個追加したい場合は`extend`するか`+=`でListを渡してあげると出来ます。

``` python
list = [1,2,3]
list.insert(5,6)
print(list)
>>> [1, 2, 3, 6]
```

挿入であれば`List`型関数に`insert(index, value)`が用意されているので、こちらを使用する手もあります。

## Listから削除
削除する方法はいくつかあります。

``` python
list = [1,2,3]
list[0:1]= []
print(list)
>>> [2, 3]
```

まずはスライス表記を使用して空を入れて入れ替えをしてあげる方法です。

``` python
list = [1,2,3]
del list[0:1]
print(list)
>>> [2, 3]
```

もう1つは`del`文を使用して行う方法があります。

``` python
list = [1,2,3]
list.remove(1)
print(list)
```

ピンポイントで消したい場合は`List`型の関数として`remove(index)`が用意されているので、こちらを使用する手もあります。
ただし、なぜか`remove`は1オリジンとなっているので注意なのと削除できないインデックスを指定した場合は(index == 0 or size < index)エラーです。

``` python
list = [1,2,3]
list.clear()
print(list)
>>> []
```

全部消したい場合は`List`型の関数として`clear`が用意されているので、こちらを使用する手もあります。

## 文字列からListへ変換
数字型からは直接変換出来ないようですが、文字列型からは変換が可能となっています。
やり方は`list`関数を呼ぶだけです。

``` python
value = list('123')
print(value)
>>> ['1', '2', '3']
```

## List内包処理
詳しくは他の記事を参照してください！(まだ良くわかってない)

## その他
List型は他にも`map` `filter` `sort` `reverse`など一般的にList操作として行えることは出来ます。
多すぎるのでここでは省略します。

# Tuple型(タプル)
TupleはListと同じく要素を複数持つことが出来る型です。
Listとの違いは大きく2つあり

1. immutableである
2. ~~要素内に複数の型を混ぜることが出来ない~~

コメントで指摘いただきましたが、Tupleで型は混ぜれます。

といった違いがあります。
他は操作を含めて基本的にはListと同じ感じで使うことが出来ます。
もちろんimmutableなので副作用のある操作を行うことは出来ません。

## Tupleへ変換
文字列やListから変換が可能となっています。
逆にTupleからListへの変換も可能です。
やり方は`tuple`関数を呼ぶだけです。

``` python
value = [1,2,3]
print(tuple(value))
print(list(tuple(value)))
>>> (1, 2, 3)
[1, 2, 3]
```

``` python
value = 'abc'
print(tuple(value))
>>> ('a', 'b', 'c')
```

# Range型(数値シーケンス)
rangeは数値のシーケンスを簡単に扱うことが出来る型です。
このシーケンスはimmutableです。
やり方は`range`関数を呼ぶだけです。

``` python
value = range(10)
for i in value:
    print(i)
print(value)
>>> 0
1
2
3
4
5
6
7
8
9
range(0, 10)
```

``` python
class range(stop)
class range(start, stop[, step])
```

range関数はこの2つがあり、細かいシーケンスを作成することが可能となっています。

# set型(集合体)
同一の値が持てないListみたいなものです。
`set`関数と`frozenset`関数があり、それぞれ`mutable`と`immutable`の`set`を作成することが出来ます。
`List` `Tuple` `Range` `String`から作成することが出来ます。

``` python
value = set([1,2,3,1])
print(value)
>>> {1, 2, 3}
value2 = frozenset([1,2,3,1])
print(value2)
>>> frozenset({1, 2, 3})
```

``` python
value = set((1,2,3,1))
print(value)
>>> {1, 2, 3}
value2 = frozenset((1,2,3,1))
print(value2)
>>> frozenset({1, 2, 3})
```

``` python
value = set(range(10))
print(value)
>>> {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
value2 = frozenset(range(10))
print(value2)
>>> frozenset({0, 1, 2, 3, 4, 5, 6, 7, 8, 9})
```

``` python
value = set('abc')
print(value)
>>> {'a', 'c', 'b'}
value2 = frozenset('abc')
print(value2)
>>> frozenset({'a', 'c', 'b'})
```

`set`や`frozenset`内の要素は混ぜても良いですが、`List`型だけは駄目みたいです。

``` python
value = set([1,2,3,b'4', '5', (1,2,3)])
print(value)
>>> {'5', 1, 2, 3, b'4', (1, 2, 3)}
```

``` python
value = set([1,2,3,'4',[1,2,3]])
print(value)
>>> TypeError: unhashable type: 'list'
```

``` python
value = {1,2}
print(value)
>>> {1, 2}
```

また、`set`であれば`{}`で作成することも出来ます。

## 要素の操作
`Set`では`Set`と`Set`を組み合わせたり`Set`と`List`を組み合わせたりすることで要素の操作を行うことが出来ます。
関数で操作する場合は副作用があるので`frozenset`では出来ません。
演算でやる場合は副作用がなく、新しい集合体を作るので`frozenset`でも使えます。

### 要素を追加する
`update`や`|=`は2つの要素をマージするので、実質追加のような動作をします。

``` python
value = set([1,2,3])
value.update([4])
print(value)
>>> {1, 2, 3, 4}
```

``` python
value = set([1,2,3])
value |= set([4])
print(value)
>>> {1, 2, 3, 4}
```

### 重複している要素を抽出
`intersection_update`や`&=`は2つの要素で重複している要素を抽出します。

``` python
value = set([1,2,3])
value.intersection_update([4])
print(value)
>>> set()
```

``` python
value = set([1,2,3])
value &= set([4])
print(value)
>>> set()
```

### 要素の削除
`difference_update`や`-=`は要素の削除を行います。

``` python
value = set([1,2,3])
value.difference_update([3])
print(value)
>>> {1, 2}
```

``` python
value = set([1,2,3])
value -= set([3])
print(value)
>>> {1, 2}
```

### 要素の追加と重複削除
`symmetric_difference_update`や`^=`は重複していない要素であれば追加し、重複している要素であれば削除を行います。

``` python
value = set([1,2,3])
value.symmetric_difference_update([4])
print(value)
>>> {1, 2, 3, 4}
value = set([1,2,3])
value.symmetric_difference_update([1])
print(value)
>>> {2, 3}
```

``` python
value = set([1,2,3])
value ^= set([4])
print(value)
>>> {1, 2, 3, 4}
value = set([1,2,3])
value ^= set([1])
print(value)
>>> {2, 3}
```

### 単体要素の操作
今までの操作は`Set`や`List`と組み合わせた操作になっています。
多くて面倒になってきたので、まとめて紹介します。

``` python
value = set([1,2,3])
# 追加
value.add(4)
print(value)
>>> {1, 2, 3, 4}
# 削除(存在しなければKeyError)
value.remove(3)
>>> {1, 2, 4}
print(value)
# 存在していれば削除、しなければ何もしない
value.discard(5)
print(value)
>>> {1, 2, 4}
# 存在していれば削除、しなければ何もしない
value.discard(2)
print(value)
>>> {1, 4}
# 任意の要素を削除(空の場合はKeyError)
value.pop()
print(value)
>>> {4}
# 全削除
value.clear()
print(value)
>>> set()
```

# dict型(マッピング、辞書)
mutableなマップ型です。
マップ型なので、key-value方式となっています。

``` python
a = dict(one=1, two=2, three=3)
print(a)
>>> {'one': 1, 'two': 2, 'three': 3}
b = {'one': 1, 'two': 2, 'three': 3}
print(b)
>>> {'one': 1, 'two': 2, 'three': 3}
c = dict(zip(['one', 'two', 'three'], [1, 2, 3]))
print(c)
>>> {'one': 1, 'two': 2, 'three': 3}
d = dict([('two', 2), ('one', 1), ('three', 3)])
print(d)
>>> {'two': 2, 'one': 1, 'three': 3}
e = dict({'three': 3, 'one': 1, 'two': 2})
print(e)
>>> {'three': 3, 'one': 1, 'two': 2}
```

初期化方法は幾つも存在しており、どれでも大丈夫です。

``` python
value = {'one': 1, 'two': 2, 'three': '3'}
print(value)
>>> {'one': 1, 'two': 2, 'three': '3'}
value = {'one': 1, 'two': 2, 3: 3}
print(value)
>>> {'one': 1, 'two': 2, 3: 3}
```

keyとvalueは型が違っていても大丈夫です。

``` python
value = {'one': 1, 'two': 2, 'three': [1,2,3]}
print(value)
>>> {'one': 1, 'two': 2, 'three': [1, 2, 3]}
value = {'one': 1, 'two': 2, 'three': (1,2,3)}
print(value)
>>> {'one': 1, 'two': 2, 'three': (1, 2, 3)}
```

また、valueにはListだったりTupleだったりも大丈夫ですが、

``` python
value = {[1,2,3]: 1, 'two': 2, 'three': 3}
print(value)
>>> TypeError: unhashable type: 'list'
```

``` python
value = {(1,2,3): 1, 'two': 2, 'three': 3}
print(value)
>>> {(1, 2, 3): 1, 'two': 2, 'three': 3}
```

keyはListがダメでTupleが大丈夫という感じで違いがあります。

コメントいただきましたが、keyはimmutableなオブジェクトのみが指定出来るそうです。

## 辞書の操作
基本はListとかTuoleにあるような操作は行えるようです。
重複する感じになってしまうので、ここでは割愛します。

## 読み込み専用辞書の作成
読み込み専用としたい場合は`MappingProxyType`を使います。

``` python
from types import MappingProxyType

value = {(1,2,3): 1, 'two': 2, 'three': 3}
print(MappingProxyType(value))
```

`MappingProxyType`は今までと違って`types`内にあります。
これは`import`しないと使用することが出来ません。
`types`は動的な型生成と組み込み型となっていますが、ここでは扱いません。


## ビューオブジェクト
`keys()` `values()` `items()`で取り出した場合、それはビューオブジェクトとして扱われます。

``` python
value = {'one': 1, 'two': 2, 'three': 3}
print(value.keys())
>>> dict_keys(['one', 'two', 'three'])
print(value.values())
>>> dict_values([1, 2, 3])
print(value.items())
>>> dict_items([('one', 1), ('two', 2), ('three', 3)])
```
ビューオブジェクトはイテレートすることで(`list()` `tuple()` `for`)、それぞれの型へ変換したり使用可能です。
また、ビューオブジェクトは'Set'と同じような操作が可能となっています。

# 最後に
3週くらいに分けてこれ書いたんですが、この間に小さい記事どれだけ書いたと思ってるんだと言いたくなるぐらい時間かかりましたが、この記事の作成にあたってtutorialをものすごく読んで理解が深まったので、こういう機会って重要だなと思いました。
自分は余り読まずに動かしてしまうことが多いのですが、読みながら1つずつやるという重要さを改めて知りました。

# 参考リンク
https://docs.python.jp/3/tutorial/introduction.html
https://docs.python.jp/3/library/stdtypes.html