はじめに
Pythonを勉強していると、
「ミュータブルとイミュータブル」
という言葉に出会うことがあります。
特に、
x = [1, 2, 3]
y = x
y.append(4)
print(x)
を実行すると、
[1, 2, 3, 4]
となるため、
「y を変更したのに、なぜ x まで変わるの?」
と疑問に感じることがあります。
この動きを理解するうえで重要なのが、ミュータブル(mutable)とイミュータブル(immutable)という考え方です。
簡単に言えば、
ミュータブル:オブジェクトそのものを変更できる
イミュータブル:オブジェクトそのものを変更できない
という違いです。
この記事では、Pythonでよく使う型を中心に、
- ミュータブルとイミュータブルとは何か
- どの型のオブジェクトがミュータブルなのか
- なぜ
listは変更できて、intやstrは変更できないのか tupleが少しややこしい理由
について整理していきます。
この記事でわかること
この記事では、次の内容の理解を目指します。
- ミュータブルとイミュータブルの意味
- Pythonの代表的な型のオブジェクトがミュータブル・イミュータブルのどちらなのか
intやstrのオブジェクトがイミュータブルである理由listやdictのオブジェクトがミュータブルである理由tupleのオブジェクトがイミュータブルでも注意が必要な理由- ミュータブル・イミュータブルと変数の参照の関係
1. ミュータブルとイミュータブルとは?
まずは言葉の意味から確認しましょう。
ミュータブルには「変更可能」という意味があります。
一方、イミュータブルは「変更できない」という意味です。
Pythonでは、オブジェクトについて、
そのオブジェクト自身の状態を変更できるか?
という観点で、ミュータブルとイミュータブルを考えます。

ミュータブル
ミュータブルなオブジェクトは、オブジェクトそのものを変更できます。
代表的なものには、
listdictsetbytearray
などがあります。
イミュータブル
イミュータブルなオブジェクトは、オブジェクトそのものを変更できません。
代表的なものには、
intfloatboolstrtuplebytes
などがあります。
2. Pythonの型を一覧で整理
まずは代表的な型を一覧で見てみましょう。
| 型 | mutable / immutable |
|---|---|
bool |
immutable |
int |
immutable |
float |
immutable |
complex |
immutable |
str |
immutable |
tuple |
immutable |
bytes |
immutable |
list |
mutable |
set |
mutable |
dict |
mutable |
bytearray |
mutable |
ざっくり覚えるなら、
list・dict・set のオブジェクトはミュータブル
int・float・complex・bool などの数値系、str・tuple・bytes のオブジェクトはイミュータブル
と覚えておくとよいでしょう。
ただし、単純に暗記するだけではなく、この違いがPythonのコードにどのように影響するのかを理解することが重要です。
3. int のオブジェクトはイミュータブル
まず、もっとも身近な例として整数を見てみましょう。
x = 10
x は 10 という整数オブジェクトを参照しています。
ここで、
x = 20
とすると、
「10 というオブジェクトが 20 に変わった」
わけではありません。
x が参照するオブジェクトが変わっています。
イメージすると、

です。
10 という整数オブジェクトそのものを変更したわけではありません。
つまり、int のオブジェクトはイミュータブルです。
4. 「x = 10」は変更ではない
ここで、もう一つ重要なポイントを確認しておきましょう。
x = 10
x = 20
見た目だけを見ると、
x の値を 10 から 20 に変更した
ように感じます。
しかし、Pythonでは、
x の参照先が変わった
と考えるほうが正確です。
つまり、10 という整数オブジェクトそのものを変更したのではありません。
この「変数の参照先を変更すること」と「オブジェクトそのものを変更すること」の違いが、ミュータブル・イミュータブルを理解するうえで重要になります。
Pythonの変数とオブジェクトについては、次の記事で詳しく解説しています。

5. list のオブジェクトはミュータブル
一方、list のオブジェクトはミュータブルです。
例えば、
numbers = [1, 2, 3]
というリストを作ったとします。
そして、
numbers.append(4)
とすると、
[1, 2, 3]
だったリストが、
[1, 2, 3, 4]
になります。
ここでは、numbers が参照している同じリストオブジェクトそのものが変更されています。
イメージすると、

となります。
このように、list はオブジェクトの状態を変更できるミュータブルな型です。
6. なぜ「x まで変わる」のか?
ここで、よくある疑問を考えてみましょう。
x = [1, 2, 3]
y = x
y.append(4)
print(x)
print(y)
結果は、
[1, 2, 3, 4]
[1, 2, 3, 4]
です。
なぜでしょうか?
理由は、x と y が同じリストオブジェクトを参照しているからです。

ここで、
y.append(4)
を実行します。
すると、

となります。
y が参照しているリストを変更した結果、同じリストを参照している x から見ても変更後の状態になっているわけです。
これが、ミュータブルなオブジェクトを扱うときに注意したいポイントです。
7. str のオブジェクトもイミュータブル
文字列のオブジェクトもイミュータブルです。
例えば、
text = "Python"
とします。
ここで、文字列の一部だけを変更しようとして、
text[0] = "J"
とすると、エラーになります。
文字列オブジェクトそのものを変更することはできないからです。
では、
text = "Java"
ならどうでしょうか。
これは問題ありません。
ただし、これは「Python」という文字列を「Java」に変更したわけではありません。
text が参照する文字列オブジェクトを変更したと考えます。
つまり、

から

へと参照先が変わったということです。
8. dict と set のオブジェクトもミュータブル
list のオブジェクト以外にも、代表的なミュータブルな型があります。
dict
辞書のオブジェクトはミュータブルです。
user = {
"name": "Taro",
"age": 20
}
例えば、
user["age"] = 21
とすると、辞書の内容を変更できます。
{
"name": "Taro",
"age": 20
}
から
{
"name": "Taro",
"age": 21
}
に変わります。
同じ dict オブジェクトの状態が変更されています。
set
set のオブジェクトもミュータブルです。
numbers = {1, 2, 3}
に対して、
numbers.add(4)
とすると、
{1, 2, 3}
から、
{1, 2, 3, 4}
へ変更できます。
9. tuple のオブジェクトはイミュータブル
ここで少し注意したいのが tuple です。
tuple のオブジェクトはイミュータブルです。
numbers = (1, 2, 3)
に対して、
numbers[0] = 10
のように要素を変更することはできません。
つまり、
tuple そのものの状態は変更できない
ということです。
ただし、ここには少しややこしいポイントがあります。
10. 「tuple の中身」には注意
例えば、次の tuple を考えてみましょう。
data = ([1, 2], [3, 4])
tuple のオブジェクト自体はイミュータブルです。
しかし、tuple の中に入っているのは list です。
list のオブジェクトはミュータブルでした。
そのため、
data[0].append(5)
は実行できます。
結果は、
([1, 2, 5], [3, 4])
となります。
「tuple なのに変更できた?」
と思うかもしれません。
しかし、変更されたのは tuple そのものではありません。
tuple の中に入っている list オブジェクトが変更されたのです。
イメージすると、最初は

ですが、
data[0].append(5)
によって、

となります。
ここから、
tuple は、保持している要素を別のオブジェクトに置き換えることはできない。
しかし、tuple の要素が list のようなミュータブルなオブジェクトなら、要素のオブジェクトを変更することはできる。
という意味であることがわかります。
11. ミュータブル・イミュータブルをどう覚える?
ここまでの内容を整理すると、代表的な型は次のようになります。
イミュータブル
bool
int
float
complex
str
tuple
bytes
ミュータブル
list
dict
set
bytearray
まずは、この程度を覚えておけば十分です。
ただし、単純に一覧を暗記するより、
そのオブジェクト自体を変更できるか?
と考える癖をつけるほうが重要です。
例えば、
x = [1, 2, 3]
なら、
list のオブジェクトだからミュータブル
と判断できます。
x = "Python"
なら、
str のオブジェクトだからイミュータブル
と判断できます。
12. ミュータブル・イミュータブルと代入の関係
最後に、今回の記事の最初に出てきたコードに戻ってみましょう。
x = [1, 2, 3]
y = x
y.append(4)
x と y は同じ list オブジェクトを参照しています。
そして list はミュータブルなので、
y.append(4)
によって、そのリスト自体を変更できます。
そのため、x から見ても、
[1, 2, 3, 4]
になっています。
一方、int なら、
x = 10
y = x
x = 20
としても、

となります。
これは 10 という整数オブジェクトを変更したのではなく、x の参照先が変わったからです。
このように、
「変数がどのオブジェクトを参照しているか」
と、
「そのオブジェクトがミュータブルなのかイミュータブルなのか」
を組み合わせて考えると、Pythonの動きを理解しやすくなります。
まとめ
Pythonのミュータブル・イミュータブルについて整理しました。
ミュータブル
ミュータブルなオブジェクトは、オブジェクトそのものを変更できます。
代表的な型は、
list
dict
set
bytearray
などです。
イミュータブル
イミュータブルなオブジェクトは、オブジェクトそのものを変更できません。
代表的な型は、
bool
int
float
complex
str
tuple
bytes
などです。
そして重要なのは、
「変数の値を変更した」のか、「オブジェクトそのものを変更した」のかを区別すること
です。
例えば、
x = 10
x = 20
では、10 という整数オブジェクトを変更したのではなく、x の参照先が変わっています。
一方、
x = [1, 2, 3]
x.append(4)
では、x が参照しているリストオブジェクトそのものが変更されています。
Pythonのミュータブル・イミュータブルを理解するときは、「そのオブジェクト自身を変更できるか?」と考える。
そして、これを以前の記事で説明した「Pythonの変数は箱ではなく、オブジェクトを参照する名前」という考え方と組み合わせると、リストや辞書を扱うときの「なぜこうなるの?」がかなり理解しやすくなります。
関連記事
Pythonの変数・オブジェクト・参照について詳しく解説しています。

関連書籍
Pythonの文法だけでなく、オブジェクトやデータモデルなど、Pythonの仕組みをもう少し深く理解したい人には『入門 Python 3』もおすすめです。


コメント