こんにちは、ナナです。
Pythonは「オブジェクト指向言語」と呼ばれる種類のプログラミング言語に含まれます。
この「オブジェクト指向」という考え方は、プログラミング初心者の方にはなかなか理解しづらいのですが、Pythonのプログラムを使いこなすためには乗り越えなければなりません。
まずは、「オブジェクト」というものと「変数」との関係性を理解しましょう。
本記事では次の疑問点を解消する内容となっています。
では、「変数」と「オブジェクト」の関係性について学んでいきましょう。
Pythonの『オブジェクト』と『オブジェクトID』の関係
師匠!Pythonは情報を『オブジェクト』で管理しているってどういうことなんですか?C言語には『オブジェクト』ってないですよね?
『オブジェクト』っていうのが何なのか、よくわかりません。
Pythonでプログラムをしていくためには、この『オブジェクト』というものへの理解が欠かせません。
C言語にはない『オブジェクト』の考え方を学んでいきましょう。
実は、ここまでカリキュラムを進めてきた皆さんは『オブジェクト』というものにすでに触れています。改めて『オブジェクト』が何なのかを理解していきましょう。
Pythonで管理される『オブジェクト』とは?
Pythonでは「数」「文字列」「リスト」「タプル」といったあらゆる情報が『オブジェクト』として管理されています。
つまり、次のような情報は全て『オブジェクト』として管理されているということです。
10 # 整数値
3.14 # 浮動小数点数
"Hello" # 文字列
[10, 20, 30] # リスト
(40, 50, 60) # タプル
このようにPythonという言語では、様々な情報は『オブジェクト』という枠組みの中で管理されているのです。
「数」や「文字列」もオブジェクトなんです。つまり、今まで皆さんはプログラムの中で『オブジェクト』をたくさん使ってきたということですね。
『オブジェクト』に紐づく『オブジェクトID』とは
プログラムが動く中で『オブジェクト』は大量に生成されることになります。そのオブジェクトを適切に扱うためには、オブジェクトを識別できる仕組みが必要となります。
それが、『オブジェクトID』と呼ばれる識別子です。
『オブジェクトID』は、「id関数」を使うことで取得することができます。
書式
id(オブジェクト)
使用例
id(1)
id(”Hello”)
意味
指定したオブジェクトのオブジェクトIDを取得する。
実際にプログラムから使用してみましょう。print関数と組み合わせて画面に『オブジェクトID』を表示してみます。
print(id(10))
print(id(3.14))
print(id("Hello"))
print(id([10, 20, 30]))
print(id((40, 50, 60)))
実行結果は次のように、オブジェクト毎に異なる『オブジェクトID』が付与されているのがわかりますね。
140720290641008
2705538589368
2705548071856
2705547616776
2705551508824
ものすごく大きな数字が紐づいていますが、これが『オブジェクトID』です。
プログラムを動かす度にIDは変わります。皆さんの環境では違う数字が表示されることでしょう。
「変数」と「オブジェクト」の関係性
「10」みたいな数も『オブジェクト』とは。わたしは知らず知らずのうちにオブジェクト使ってたんですね。
「変数」に「10」を代入するってことは、「変数」に「オブジェクト」を入れるってことなんですね!
いや、ここはね注意が必要なんですよ。Pythonという言語において「変数」というのは、「オブジェクト」入れるものではないんです。
Pythonにおける「変数」と「オブジェクト」の関係性を学んでいきましょう。
Pythonの「変数」が管理する情報とは?
C言語やJavaといった言語の「変数」と、Ptyhonの「変数」は同じように見えても情報の管理方法が違います。
C言語の変数定義
char num = 10;
Pythonの変数定義
num = 10
C言語の「変数」は変数自身が情報を管理していますが、Pythonの「変数」は、オブジェクトを参照するための『オブジェクトID』を管理しています。
つまり、Pythonにおける変数の役割とは
『オブジェクト』を参照するための道具
なのです。
Pythonで正確に情報を操るためには、「変数」が「オブジェクトへの参照」を管理していることを理解することが必須知識となります。
C言語のような言語とは異なる情報の管理方法になっているんですね。これこそが、Pythonのデータ管理の柔軟性を作り出しているのです。
Pythonにおける「変数への代入」の本当の意味とは
ここまで「num = 1」といったプログラムを作ってきましたが、このプログラムの本当の意味は
num変数に「1」を設定している
ではなく
num変数に「1」というオブジェクトへの参照を設定している
が、正しい表現方法です。
次のプログラムでその意味を確認してみましょう。
# 「1」という数のオブジェクトIDを表示
print("1:", id(1))
# 変数へ「1」オブジェクトへの参照を設定
num1 = 1
num2 = 1
# 変数が示すオブジェクトIDを表示
print("num1 -->", id(num1))
print("num2 -->", id(num2))
1: 140719500145488
num1 --> 140719500145488
num2 --> 140719500145488
このように変数「num1」と「num2」のオブジェクトIDは共に「1」というオブジェクトIDと同じになっていますね。
このプログラムの「変数」と「オブジェクト」の関係を図示化してみましょう。
このように「1」というオブジェクトが1つ存在しており、「num1 = 1」と「num2 = 1」は、オブジェクト「1」への参照を「変数」に対して設定しているのです。
id関数の引数に「変数」を指定した場合の動作
ちなみに「id関数」は、次のように引数を変数として与えることもできます。
書式
id(変数名)
使用例
id(num)
意味
変数が参照しているオブジェクトIDを取得する。
id関数に変数を与えた場合は、変数が参照しているオブジェクトIDが取得されることに注意しましょう。
id関数に変数を渡すと「変数」自身のオブジェクトのIDが取れるように思ってしまいがちですが違います。
Pythonにおいて変数自身は『オブジェクト』ではありません。変数が参照しているオブジェクトIDが取得されます。
変数の参照が切り替わる実例プログラムの紹介
Pythonの「変数」ってC言語の変数となんか違うなーっと思ってましたが、これがその違和感の正体だったのですね。なるほどー。
でも、変数がオブジェクトの参照を管理しているっていうのが、プログラムの中でどのような動きなのかが、まだイメージを掴めていません。
それじゃあ、簡単なプログラムを使ってプログラムの流れとともに、変数の参照がどのように変わっていくかを見てみようね!
オブジェクトが生成され、変数がどのように参照を更新していくのかを解説しましょう。
オブジェクトの参照が切り替わるプログラム例
次のプログラムは、シーン1~3に分けてオブジェクトの生成と、変数の参照の切り替わりを表現したものです。
# シーン1:オブジェクトの生成とIDの表示
print("オブジェクトの生成")
print("1:", id(1))
print("2:", id(2))
# シーン2:変数へのオブジェクトの参照設定
num1 = 1
num2 = 1
print("参照先設定直後")
print("num1 -->", id(num1))
print("num2 -->", id(num2))
# シーン3:num2の参照先更新
num2 += 1
print("更新後の参照先")
print("num1 -->", id(num1))
print("num2 -->", id(num2))
オブジェクトの生成
1: 140719500145488
2: 140719500145520
参照先設定直後
num1 --> 140719500145488
num2 --> 140719500145488
更新後の参照先
num1 --> 140719500145488
num2 --> 140719500145520
シーン1:オブジェクトの生成とID表示
シーン1では、オブジェクトの生成が行われています。
print("オブジェクトの生成")
print("1:", id(1))
print("2:", id(2))
id関数の引数に「1」「2」の数値を指定したことにより、Pythonのプログラムではこの瞬間「オブジェクトを作ろう!」ということになります。
オブジェクトが生成されたため、オブジェクトIDが付与され、次のようにprint関数で表示ができることになります。
オブジェクトの生成
1: 140719500145488
2: 140719500145520
シーン2:変数へのオブジェクトの参照設定
次に2つの変数「num1」「num2」に対してオブジェクトの参照を設定しています。
num1 = 1
num2 = 1
print("参照先設定直後")
print("num1 -->", id(num1))
print("num2 -->", id(num2))
「1」という数値のオブジェクトはすでに存在しているため、このプログラムではオブジェクトの生成は行われません。
「変数」に対して、オブジェクトの参照設定ができたため、「num1」「num2」に対するid関数の結果は「1」というオブジェクトIDが取得できることになります。
参照先設定直後
num1 --> 140719500145488
num2 --> 140719500145488
このようにシーン1にて作成されたオブジェクトIDを「num1」「num2」が共に参照していることがわかりますね。
シーン3:変数「num2」の参照先の更新
それでは、変数「num2」に対して+1を行い「2」という情報を管理するように変更してみます。
num2 += 1
print("更新後の参照先")
print("num1 -->", id(num1))
print("num2 -->", id(num2))
「2」というオブジェクトは、シーン1ですでに作成済みですね。そのため、変数「num2」はすでに生成済みのオブジェクト「2」へ参照先が切り替わるように処理されます。
結果、確かに次のように「2」のオブジェクトに参照先が変更されているのがわかります。
更新後の参照先
num1 --> 140719500145488
num2 --> 140719500145520
「オブジェクトの生成」と「変数のオブジェクト参照」の関係性をしっかりと理解しましょう。
Q&A:Pythonのオブジェクトに関するよくある質問
オブジェクトについて質問ある人はどうぞ!
Q:「id関数」は引数にオブジェクトも変数でも指定できるって不思議です?どうしてなの?
師匠!なんで「id関数」にはオブジェクトを指定することもできて、変数を指定することもできるんですか?
いや、それはね、実はちょっと違うんですよ。「id関数」の引数自体はやっぱりオブジェクトしか受け取れません。
次のように、id関数には「オブジェクト」も「変数」も指定することができます。
# 変数にオブジェクトの参照を設定
data = 10
# id関数の呼び出し
print(id(10)) # オブジェクトを指定
print(id(data)) # 変数を指定
140719500145776
140719500145776
ただし、変数を渡しているようで、実は変数ではないのです。
オブジェクトを参照している変数は、プログラムで使用すると参照先オブジェクトとして置き換えられます。そのため、結局はオブジェクトとして解釈されます。
変数への代入のときは、変数への参照設定として解釈されますが、変数を利用するときは、参照先のオブジェクトとして解釈されます。
C言語技術者が知るべきPython言語との違い:変数の扱い
すでにC言語を習得している人は、次のポイントに気を付けよう!
Pythonにおける「変数」は、C言語においては「ポインタ変数」にイメージが似ています。
- C言語の「ポインタ変数」とは、とあるメモリ(変数)を参照するための道具
- Pythonの「変数」とは、とあるオブジェクトを参照するための道具
C言語
char Num = 10;
char * pNum = &Num;
Python
num = 10
C言語の「ポインタ変数」はchar*型というように参照先のデータ型の情報を管理しています。
しかし、Pythonの「変数」は変数自身はデータ型を管理していません。オブジェクト側がデータ型を管理しているのです。
そのため、Pythonの「変数」には、数でも文字列でも好きなタイミングでオブジェクトの参照を設定することが可能となります。
# 参照先を整数値オブジェクトに設定
data = 10
print(data, "ID:", id(data))
# 参照先を文字列オブジェクトに設定
data = "Hello"
print(data, "ID:", id(data))
# 参照先をリストオブジェクトに設定
data = [0, 1, 2]
print(data, "ID:", id(data))
10 ID: 140719500145776
Hello ID: 2299682313304
[0, 1, 2] ID: 2299681858056
これは変数自身がデータ型を管理していない構造となっているため、可能なプログラムなのです。
変数側に型情報を持たせないことにより、変数を扱う自由度を引き上げているのがPythonの「変数」なんです。C言語とは全く異なるアプローチですね。