Pythonの組み込みデータ型(標準タイプ)イミュータブルimmutable)、ミュータブルmutable)、イテラブルiterable)、シーケンスsequence)、マッピングmapping)について分類し一覧表にまとめてみました。

参考書でこれらの用語は頻出します。最初の頃は、遭遇するたびに、頭の中でこんがらがってしまいます。そんな時は、是非この分類表を見て、頭の中を整理してみてください。

Pythonのおもな組み込み型の分類表

表の左列の組み込み型は、それぞれ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

変更不可(イミュータブル、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文のブロックでは、この変数を用いて処理を実行できます。

シーケンス

整数のインデックスを指定して、要素にアクセスできる」データ型です。組み込み関数len()で要素数(長さ)を取得できます。以下のように文字列(str)もシーケンスです。

# リストの場合
>>> fruits = ["kiwi", "papaya", "mango"]
>>> print(fruits[1])
papaya

# 文字列の場合
>>> neko = "Wagahai ha neko de aru"
>>> print(neko[8])
h
>>> len(neko)
22

なお、シーケンスはすべて「反復できる(イテラブル)」オブジェクトです。

マッピング

任意のキーで要素を検索できる」データ型です。シーケンスが整数のインデックスを指定して要素にアクセスするのに対して、マッピングは任意のキーを使用できます。辞書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を学んだ時に、データ型について欲しかった表を作成してみました。お役に立てば嬉しいです。