Pythonの組み込みデータ型(標準タイプ)をイミュータブル(immutable
)、ミュータブル(mutable
)、イテラブル(iterable
)、シーケンス(sequence
)、マッピング(mapping
)について分類し一覧表にまとめてみました。
参考書でこれらの用語は頻出します。最初の頃は、遭遇するたびに、頭の中でこんがらがってしまいます。そんな時は、是非この分類表を見て、頭の中を整理してみてください。
本記事の目次
- Pythonのおもな組み込み型の分類表
- 変更不可(イミュータブル、immutable)
- 変更可(ミュータブル、mutable)
- 反復可(イテラブル、iterable)
- シーケンス(sequence)
- マッピング(mapping)
- 最後に
Pythonのおもな組み込み型の分類表
おもな組み込み型は下表のように分類できます。
組み込み型 (built-in types) |
変更不可 (immutable) |
変更可 (mutable) |
反復可 (iterable) |
シーケンス (sequence) |
マッピング (mapping) |
データ (data) |
---|---|---|---|---|---|---|
bool | ◎ | True,False | ||||
int | ◎ | 整数 | ||||
float | ◎ | 浮動小数点数 | ||||
complex | ◎ | 複素数 | ||||
str | ◎ | ◎ | ◎ | 文字列 | ||
list | ◎ | ◎ | ◎ | |||
tuple | ◎ | ◎ | ◎ | |||
range | ◎ | ◎ | ◎ | |||
dict | ◎ | ◎ | ◎ | |||
set | ◎ | ◎ | ||||
bytes | ◎ | ◎ | ◎ | バイナリ | ||
bytearray | ◎ | ◎ | ◎ | |||
file object | ◎ | ◎ |
※ 表中の組み込み型は、それぞれPython公式サイトのドキュメントにリンクしています。
上記以外に「コンテナ」という分類もよく用いられます。list
、tuple
、dict
、set
がコンテナです。コンテナとは、任意の型のデータを複数格納できるデータ型です。
参考にしたPython公式ページのドキュメント(リンク)
Python言語リファレンス:3.データモデル
Python標準ライブラリ:2.組み込み関数
Python標準ライブラリ:4.組み込み型
用語集
変更不可(イミュータブル、immutable)
Pythonに最初に触れた頃は、大抵こんな疑問を抱きます。数値や文字列はイミュータブルだから変更できないはずなのに…、でも変更できてしまう ??。
>>> a = 10
>>> print(a)
10
>>> a = a + 7
>>> print(a)
17 <= aは10から17に変更されている
>>> type(a)
<class 'int'> <= 整数(int)はイミュータブル
実は、イミュータブルの変更不可というのは、「同じオブジェクトのまま、値だけを変更することはできません」という意味なのです。同じオブジェクトというのがわかりにくいですが、「オブジェクトとは箱のようなもの」と考えてください。つまり、以下のように考えるとわかりやすくなります。
- イミュータブル:値を変更するには、箱ごと変えなければいけない(中身を変えられない箱)
- ミュータブル:箱はそのままで、中身の値だけを変えることができる(中身を変えられる箱)
箱が同じかどうかは、オブジェクトのid番号で見分けることができます。Pythonでは、生成されたオブジェクトにid番号が重複なく割り当てられます。id番号は、組み込み関数のid()
で調べることができます。
>>> a = 10
>>> id(a)
4510888928 <= 最初のid番号(箱)
>>> a = a + 7
>>> id(a)
4510889152 <= id番号が変更された(箱が変わった)
このように、a
に新しい値a + 7
を代入すると、id番号が変わります。つまり、17
という値は、新しい違う箱(オブジェクト)に入れられたのです。
変更可(ミュータブル、mutable)
ミュータブルのオブジェクトの値を変更するには、メソッドやインデックスを使います。例として、ミュータブルの代表格であるリストを使ってみます。
# ミュータブル(リスト)の場合
>>> lst = [1,3,5]
>>> print(lst)
[1,3,5]
>>> id(lst)
4516634568
>>> lst.append(7)
>>> print(lst)
[1, 3, 5, 7] <= 末尾に7が追加された
>>> id(lst)
4516634568 <= id番号は変わらない
>>> lst[2] = 3
>>> print(lst)
[1, 3, 3, 7] <= 3つ目(index=2)が3に変更された
>>> id(lst)
4516634568 <= id番号は変わらない
>>> type(lst)
<class 'list'>
このように同じid番号のオブジェクトのまま(同じ箱のまま)、リストの内容を変更することができました。試しに、イミュータブルのタプルで同じようなことをしてみるとエラーになります。
# イミュータブル(タプル)の場合
>>> tpl = (1,3,5,7)
>>> tpl[2] = 3 <= 値を変更できない
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'tuple' object does not support item assignment
>>> tpl.append(9) <= 値を変更(追加)できない
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'tuple' object has no attribute 'append'
>>> id(tpl)
4516636056
>>> tpl = tpl + (9,) <= 値を変更したい場合は代入文を使う
>>> print(tpl)
(1, 3, 5, 7, 9)
>>> id(tpl)
4514527544 <= id番号は変更される
作業には、出し入れできる箱の方が便利で効率的
中身を変わるたびに、箱まで新しくするのは、どう考えても不便です。データをいくつもの過程で加工するには、ミュータブルの方が便利です。ミュータブルのなかでも、リストは汎用的で使いやすく、非常によく用いられます。以下のコードは、1つのリストオブジェクトだけを使用しています。ミュータブルなリストの便利さがわかるはずです。
def add_mango(basket):
"""マンゴを最大2個までになるよう補充する"""
mango = "mango"
if basket.count(mango) < 2:
basket.append(mango)
baskets = []
print("Empty baskets id : ", id(baskets))
baskets.append( ["apple", "orange", "mango"] )
baskets.append( ["mango", "mango"] )
baskets.append( ["apple"] )
for i,basket in enumerate(baskets):
print("basket {} : {}".format(i,basket))
# マンゴの補充
for b in baskets:
add_mango(b)
print("Fill baskets id : ", id(baskets))
for i,basket in enumerate(baskets):
print("basket {} : {}".format(i,basket))
# 出力結果
Empty baskets id : 4328610184 <= 初期化した直後のid番号
basket 0 : ['apple', 'orange', 'mango']
basket 1 : ['mango', 'mango']
basket 2 : ['apple']
Fill baskets id : 4328610184 <= id番号は変わっていない(最初と同じオブジェクト)
basket 0 : ['apple', 'orange', 'mango', 'mango']
basket 1 : ['mango', 'mango']
basket 2 : ['apple', 'mango']
代入文を使えば、ミュータブルでもオブジェクトは新調される!
前述しましたが、ミュータブルの値変更には、あくまでもメソッドやインデックスを使うことに気をつけてください。以下のように、代入文で新しい値を追加すれば、違うオブジェクトに代入されます。
# リストを代入文で変更
>>> lst = [1,3,5]
>>> print(lst)
[1, 3, 5]
>>> id(lst)
4547705672 <= 最初のid番号
>>> lst = lst + [7] <= 新しい値を代入文で追加
>>> print(lst)
[1, 3, 5, 7]
>>> id(lst)
4547731016 <= id番号は変わる(違うオブジェクト)
反復可(イテラブル、iterable)
「要素を1つずつ返すことができるオブジェクト」です。わかりやすく言うと、for文
のin
のうしろに置いて、1つずつデータを取り出せるオブジェクトです。
for 変数 in イテラブルなオブジェクト:
処理(変数を使える)
…
イテラブルなオブジェクトは、ループのたびに要素が1つずつ取り出され、変数に代入されます。for文
のブロックでは、この変数を用いて処理を実行できます。
シーケンス(sequence)
「整数のインデックスを指定して、要素にアクセスできる」データ型です。組み込み関数len()
で要素数(長さ)を取得できます。以下のように文字列(str)もシーケンスです。
# リストの場合
>>> fruits = ["kiwi", "papaya", "mango"]
>>> print(fruits[1])
papaya
# 文字列の場合
>>> neko = "Wagahai ha neko de aru"
>>> print(neko[8])
h
>>> len(neko)
22
なお、シーケンスはすべて「反復できる(イテラブル)」オブジェクトです。
マッピング(mapping)
「任意のキーで要素を検索できる」データ型です。シーケンスが整数のインデックスを指定して要素にアクセスするのに対して、マッピングは任意のキーを使用できます。辞書(dict
)が代表的なマッピング型です。
# 辞書の場合
# キー:顧客番号、値:ポイント
>>> users_point = {"P05142":23, "F14452":8, "F24510":170}
>>> print(users_point["F14452"])
8
# F14452の顧客に3ポイント追加
>>> users_point["F14452"] += 3
>>> print(users_point["F14452"])
11
>>> print(list(users_point.keys()))
['P05142', 'F24510', 'F14452']
>>> "F24510" in users_point
True
# ポイント100点以上の顧客番号
>>> [k for k, v in users_point.items() if v >= 100]
['F24510']
最後に
多くのオブジェクト指向の言語と同様に、Pythonもオブジェクトはすべて参照でやりとりします。だから、値を変更しても、他に影響を及ぼさないイミュータブルなオブジェクトがあることは、プログラミングの混乱を防ぐのに非常に役立ちます。だから、イミュータブルとミュータブルを適切に使い分けることが大事です。
なお、オブジェクトを箱に例える発想は、以下の書籍「入門 Python3」を参考にさせていただきました。オブジェクトをわかりやすく説明することは、通常とても困難ですが、実にうまく説明しており、非常に参考になりました。
自分がはじめてPythonを学んだ時に、データ型について欲しかった表を作成してみました。お役に立てば嬉しいです。