【Python 文法】すべてがオブジェクト?Pythonの「オブジェクト」の正体を初心者向けに徹底解説!

Python

Pythonの学習を始めると、必ずと言っていいほど耳にする「オブジェクト」という言葉。「Pythonではすべてがオブジェクトである」と聞いても、いまいちピンとこない方も多いのではないでしょうか?

「変数と何が違うの?」「オブジェクトって、具体的にどういうものなの?」

この記事では、そんな疑問を抱えるPython初学者の方に向けて、Pythonの根幹をなす「オブジェクト」という概念を、できるだけ専門用語を避けて分かりやすく解説していきます。

この記事を読み終える頃には、

  • Pythonの「オブジェクト」が何なのかがわかる
  • 変数とオブジェクトの関係がスッキリ理解できる
  • id()type() を使ってオブジェクトの正体を確認できるようになる
  • 「イミュータブル」と「ミュータブル」という重要な違いがわかる

ようになり、Pythonへの理解が一段と深まるはずです。

なお、この記事で紹介するコードはPython 3系であればバージョンに依存せず広く動作するため、安心してお試しいただけます。


Pythonにおける「オブジェクト」とは?

まず、オブジェクトの基本的なイメージを掴みましょう。

「データ」と「それを操作する機能」をまとめたもの

Pythonにおけるオブジェクトとは、単なる「データ」だけではありません。「データそのもの」と、そのデータを「操作するための機能(メソッド)」がセットになったもの、と考えると分かりやすいです。

例えば、"hello" という文字列を考えてみましょう。 これは単なる文字の集まりという「データ」ですが、Pythonでは、このデータに関連する様々な機能もセットになっています。

# "hello" というデータを持つ文字列オブジェクト
s = "hello"

# データを操作する機能(メソッド)を使ってみる
# .upper() は、文字列をすべて大文字に変換する機能
print(s.upper()) # 出力: HELLO

# .count() は、指定した文字がいくつ含まれているか数える機能
print(s.count('l')) # 出力: 2

このように、"hello" というデータ(文字列オブジェクト)は、自分自身を大文字に変換する機能(.upper())や、特定の文字を数える機能(.count())を持っています。

オブジェクトは、「自分のことは自分でできる」賢いデータだとイメージすると、より理解が深まるかもしれません。

Pythonでは「すべて」がオブジェクト

Pythonの大きな特徴は、整数、浮動小数点数、文字列、リスト、関数など、プログラムで扱うほとんどすべてのものがオブジェクトであるという点です。

num = 100         # 整数(int)もオブジェクト
pi = 3.14         # 浮動小数点数(float)もオブジェクト
name = "Gem"      # 文字列(str)もオブジェクト
items = [1, 2, 3] # リスト(list)もオブジェクト

def say_hello():  # 関数(function)だってオブジェクト
    print("hello")

他のプログラミング言語(例えばC言語など)では、数値は単なるメモリ上の値として扱われることが多いですが、Pythonでは数値でさえもオブジェクトとして扱われ、関連する機能を持っています。これが「Pythonではすべてがオブジェクトである」と言われる理由です。


変数とオブジェクトの関係をスッキリ理解しよう

「すべてがオブジェクトなのは分かったけど、じゃあ『変数』って何なの?」という疑問が湧いてきますよね。ここがPythonを理解する上で非常に重要なポイントです。

変数はデータを入れる「箱」ではなく「ラベル(名札)」

多くのプログラミング言語入門書では、変数を「データを入れておくための箱」と説明します。しかし、Pythonの場合は**「オブジェクトに貼られたラベル(名札)」**と理解する方がより正確です。

  1. a = 100 というコードを実行すると、Pythonはまずメモリ上に 100 という値を持つ整数オブジェクトを作成します。
  2. 次に、a という名前の「ラベル」を、その 100 オブジェクトにペタッと貼り付けます。

つまり、変数 a の中身が 100 なのではなく、変数 a100 というオブジェクトを指し示している、という関係になります。

この「ラベル」という考え方は、次のコードを見るとより明確になります。

a = 100
b = a # 変数bに変数aを代入

print(a) # 出力: 100
print(b) # 出力: 100

このとき、メモリ上では何が起きているのでしょうか? b = a という処理は、「100というデータをコピーして新しい箱bに入れる」のではありません。 a というラベルが貼られているオブジェクトに、b というラベルも一緒に貼る」 という処理が行われます。

結果として、ab の2つのラベルが、同じ 100 オブジェクトを指し示すことになります。

id()関数でオブジェクトの正体(住所)を突き止める

本当に ab が同じオブジェクトを指しているのか、証拠を見てみましょう。Pythonには、オブジェクトの「身元」を確認するための便利な組み込み関数が用意されています。

id() 関数を使うと、オブジェクトに割り当てられたユニークなID(メモリ上のアドレスのようなもの)を取得できます。

a = 100
b = a

# aとbが指すオブジェクトのIDを調べてみる
print(id(a))
print(id(b))

# 2つのIDが同じかどうかを比較
print(a is b) # is演算子は、2つの変数が同じオブジェクトを指している場合にTrueを返す

# -- 実行結果の一例 --
# 140705359788048
# 140705359788048
# True

実行結果を見ると、id(a)id(b) の値がまったく同じであることがわかります。これは、変数 ab が、メモリ上のまったく同じ場所に存在する、ただ一つのオブジェクトを指し示している確固たる証拠です。

type()関数でオブジェクトの種類(型)を調べる

もう一つ便利な関数が type() です。これは、オブジェクトがどのような種類(クラスや型)なのかを教えてくれます。

num = 100
name = "Gem"
items = [1, 2, 3]

print(type(num))   # 出力: <class 'int'>
print(type(name))  # 出力: <class 'str'>
print(type(items)) # 出力: <class 'list'>

type() を使うことで、その変数が指し示しているオブジェクトが整数(int)なのか、文字列(str)なのか、リスト(list)なのかを正確に知ることができます。


必ず知っておきたい2種類のオブジェクト:イミュータブルとミュータブル

Pythonのオブジェクトには、性質によって大きく2つのグループに分けられます。これは非常に重要な概念なので、しっかり押さえておきましょう。

イミュータブル(変更不可能)なオブジェクトとは?

イミュータブル (immutable) とは、「変更不可能」という意味です。 一度作成すると、そのオブジェクトの中身(値)を後から変更することはできません。

代表的なイミュータブルなオブジェクトには、整数 (int)、浮動小数点数 (float)、文字列 (str)、タプル (tuple) などがあります。

「え? a = 100 の後に a = 200 って書けるじゃないか!」と思いますよね。 ここで「変数はラベルである」という考え方が活きてきます。

a = 100
print(f"最初のaのID: {id(a)}")

# aに新しい値を代入
a = 200
print(f"新しいaのID: {id(a)}")

# -- 実行結果の一例 --
# 最初のaのID: 140705359788048
# 新しいaのID: 140705359789648

id() を見てください。a200 を代入した後、IDが変わっています。 これは、100 というオブジェクトの中身が 200 に書き換えられたのではなく、

  1. 新しく 200 という整数オブジェクトがメモリ上に作られる。
  2. 変数 a というラベルが、100 オブジェクトから剥がされ、新しく作られた 200 オブジェクトに貼り替えられる。

という処理が行われているからです。元の 100 オブジェクトは変更されていません。これがイミュータブルです。

ミュータブル(変更可能)なオブジェクトとは?

ミュータブル (mutable) とは、「変更可能」という意味です。 一度作成した後でも、そのオブジェクトの中身を自由に変更できます。

代表的なミュータブルなオブジェクトには、リスト (list)、辞書 (dict)、セット (set) などがあります。

リストを例に見てみましょう。

list_a = [1, 2, 3]
print(f"最初のlist_aのID: {id(list_a)}")

# list_aの中身を変更する
list_a.append(4)

print(list_a) # 出力: [1, 2, 3, 4]
print(f"変更後のlist_aのID: {id(list_a)}")

# -- 実行結果の一例 --
# 最初のlist_aのID: 2315481711168
# 変更後のlist_aのID: 2315481711168

.append(4) を実行してリストに要素を追加した後も、id() の値が変わっていません。これは、新しいリストオブジェクトが作られたのではなく、既存のリストオブジェクトそのものの中身が変更されたことを意味します。

この性質は、複数の変数が同じミュータブルなオブジェクトを指している場合に、予期せぬ挙動の原因となることがあるので注意が必要です。

list_a = [1, 2, 3]
list_b = list_a # list_bも同じリストオブジェクトを指す

# list_b を変更すると...
list_b.append(99)

print(f"list_b: {list_b}") # 出力: list_b: [1, 2, 3, 99]
print(f"list_a: {list_a}") # 出力: list_a: [1, 2, 3, 99]  <-- なんとlist_aも変わってしまう!

list_b を変更しただけなのに、list_a の中身まで変わってしまいました。これは、list_alist_b が同じ一つのリストオブジェクトを指しているためです。

この違いを理解しておくことは、Pythonでバグの少ないコードを書く上で非常に重要です。


まとめ

今回は、Pythonの根幹をなす「オブジェクト」について解説しました。最後に、重要なポイントを振り返りましょう。

  • Pythonのデータはすべて「オブジェクト」: オブジェクトは「データ」と「操作する機能」がセットになったもの。
  • 変数は「ラベル」: 変数はデータを入れる箱ではなく、メモリ上のオブジェクトに貼られた名札。
  • id()type(): id() でオブジェクトの身元(ID)を、type() で種類(型)を確認できる。
  • イミュータブルとミュータブル:
    • イミュータブル (変更不可能): 中身を変えられないオブジェクト (int, strなど)。値を変更すると新しいオブジェクトが作られ、ラベルが貼り替わる。
    • ミュータブル (変更可能): 中身を変えられるオブジェクト (list, dictなど)。オブジェクトそのものを直接変更できる。

「オブジェクト」の概念を理解すると、Pythonのコードがメモリ上でどのように動いているのかをイメージしやすくなり、プログラムの挙動をより深く理解できるようになります。

最初は少し難しく感じるかもしれませんが、コードを書きながら id()type() でオブジェクトの正体を確かめる癖をつけると、自然と身についていくはずです。ぜひ、ご自身のPCで手を動かしながら試してみてください。

コメント

タイトルとURLをコピーしました