質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

Q&A

解決済

3回答

1524閲覧

インスタンス変数とクラス変数について

ruuuu

総合スコア174

Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

0グッド

0クリップ

投稿2020/01/12 10:33

こちらでクラス変数とインスタンス変数について、調べていたのですが、分からないことがあり質問させて貰いました。

class TestClass(object): common_var = "Initial Value" def get_variable(self): return self.common_var def set_variable(self, value): self.common_var = value tc1 = TestClass() tc2 = TestClass() tc1.set_variable("Set Variable") print(tc2.common_var) >>> Initial Value
class TestClass(object): common_var = [] def get_variable(self): return self.common_var def set_variable(self, value): self.common_var.append(value) tc1 = TestClass() tc2 = TestClass() tc1.set_variable("Set Variable") print(tc2.common_var) >>> ['Set Variable']

こちら2つのコードの内、下側のコードなのですが、上側のコードとほとんど処理の流れとしましては、同じかと思います。
print(tc2.common_var)こちらの結果なのですが、tc2インスタンスのプロパティであるはずの、common_var['Set Variable']となるのは何故なのでしょうか?
どなたか、ご助言頂けましたら幸いです。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答3

0

ベストアンサー

変数参照するとき、インスタンスに変数がなければクラス変数を探しにいきます。
代入は、インスタンスに代入します。

その様子を図にして、Qiitaのコメントに書いておきました。
https://qiita.com/msssgur/items/12992fc816e6adf32cff#comment-9244f5c1b6d7e194d59d

self.common_var = value

は、インスタンスの中に個別の common_var 変数を作って代入します。

self.common_var.append(value)

var = self.common_var
var.append(value)

と2段階に処理していて、self.common_var でオブジェクトを取り出して、取り出されたオブジェクトに対して valueappend します。
インスタンス変数には common_var がないので、クラス変数の中から common_var を取り出し、クラス変数の中のリストオブジェクトに value が追加されます。

クラス変数辞書とインスタンス変数辞書を、代入前と代入後でprintして比較してみてください。

質問の下側のクラスでの確認:

python

1class TestClass(object): 2 common_var = [] 3 def get_variable(self): 4 return self.common_var 5 def set_variable(self, value): 6 self.common_var.append(value) 7 8tc1 = TestClass() 9tc2 = TestClass() 10print("TestClass variables:", vars(TestClass)) 11print("tc1 variables:", vars(tc1)) 12print("tc2 variables:", vars(tc2)) 13tc1.set_variable("Set Variable") 14print(tc2.common_var) 15print("TestClass variables:", vars(TestClass)) 16print("tc1 variables:", vars(tc1)) 17print("tc2 variables:", vars(tc2))

実行結果

python

1TestClass variables: {'__module__': '__main__',, 'get_variable': <function TestClass.get_variable at 0x6fffffd0d730>, 'set_variable': <function TestClass.set_variable at 0x6fffffd0d7b8>, '__dict__': <attribute '__dict__' of 'TestClass' objects>, '__weakref__': <attribute '__weakref__' of 'TestClass' objects>, '__doc__': None 2 'common_var': []} 3tc1 variables: {} 4tc2 variables: {} 5['Set Variable'] 6TestClass variables: {'__module__': '__main__', 'get_variable': <function TestClass.get_variable at 0x6fffffd0d730>, 'set_variable': <function TestClass.set_variable at 0x6fffffd0d7b8>, '__dict__': <attribute '__dict__' of 'TestClass' objects>, '__weakref__': <attribute '__weakref__' of 'TestClass' objects>, '__doc__': None, 7 'common_var': ['Set Variable']} 8tc1 variables: {} 9tc2 variables: {}

クラス変数とインスタンス変数の両方を定義する:

python

1class TestClass(object): 2 common_var = [] # クラス変数 3 4 def __init__(self): 5 self.common_var = [] # インスタンス変数 6 7 def get_variable(self): 8 return self.common_var 9 10 def set_variable(self, value): 11 self.common_var.append(value) 12 13tc1 = TestClass() 14tc2 = TestClass() 15TestClass.common_var.append("Set Class Variable") 16tc1.set_variable("Set Instance Variable 1") 17tc2.set_variable("Set Instance Variable 2") 18print(TestClass.common_var) 19print(tc1.common_var) 20print(tc2.common_var)

実行結果

python

1['Set Class Variable'] 2['Set Instance Variable 1'] 3['Set Instance Variable 2']

投稿2020/01/12 11:13

編集2020/01/13 01:02
shiracamus

総合スコア5406

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

ruuuu

2020/01/12 12:26

ご回答ありがとうございます。 >インスタンスに変数がないのでクラス変数を参照します インスタンスに変数がないとのことなのですが、提示させて頂きました下側のコードの「 common_var = []」こちらの部分が、インスタンスプロパティになるのではないのでしょうか? tc1のcommon_varに対して、「.append」で値を追加している為、tc2へは影響がないのではないでしょうか...?
shiracamus

2020/01/12 12:31 編集

> common_var = []」こちらの部分が、インスタンスプロパティになるのではないのでしょうか? それがクラス変数です。 self. がインスタンスプロパティです。 回答欄に print(vars(...)) の実行例を追記しましたので、それぞれの変数辞書に何が格納されるか確認してみてください。
ruuuu

2020/01/12 12:37

ご返信ありがとうございます。 >それがクラス変数です。 self. がインスタンスプロパティです。 こちらなのですが、質問に記載させて頂きました上側のコードにも、「common_var = "Initial Value"」とあります。 こちらはクラス変数ではないということでしょうか...? 上側のコードにも、 「def set_variable(self, value): self.common_var = value」 このように、「self」と書いてあります為、こちらの部分について、混乱してしまっている部分ではございます。
shiracamus

2020/01/12 12:44 編集

> こちらはクラス変数ではないということでしょうか...? それもクラス変数です。 > self.common_var = value これによって self. の インスタンス変数に value が登録されます。 self.common_var.append(value) は、self.common_var は参照しているだけで代入はしません。.append によってクラス変数のリストオブジェクトの中に追加されます。 回答欄に追記した print(vars(...)) によってその様子を確認できます。
hayataka2049

2020/01/12 12:45

横からコメント。 クラス変数は、クラスのブロックの中で作られている変数のことです。 インスタンス変数は、 self.属性名 = なにか のように代入されたときに作られるものです。 selfではどちらも参照できる仕組みになっています。 同名のクラス変数とインスタンス変数が存在するという状況は、あり得ます。というか普通に起きます。その場合、selfから参照するとインスタンス変数が優先されます(それでもクラス変数が存在しなくなる訳ではない。インスタンス変数をdel インスタンス変数とかで消せばまたクラス変数が見えるようになる)。 厳密にクラス変数にアクセスしたいときは「type(self).クラス変数の名前」のようにしてください。 という説明で良いでしょうか。
ruuuu

2020/01/12 12:53

ご返信及びコメントありがとうございます。 >回答欄に追記した print(vars(...)) によってその様子を確認できます。 こちら、検証してみたいと思います。 >self.common_var.append(value) は、self.common_var は参照しているだけで代入はしません。 掲示させて頂きました上側のコードと下側のコードで違いが分からなかったのですが、変数「common_bar」が、配列であるために、インスタンス化した際は、プロパティとして、保持できないということなのでしょうか...?
shiracamus

2020/01/12 13:00 編集

self.common_var = [value] にすれば、インスタンス変数に代入されますよ。 あるいは self.common_var = [] としてから self.common_var.append(value) すればインスタンス変数にappendされます。
ruuuu

2020/01/12 12:58

すみません。少々混乱してしまい、言っていることが二転三転してしまっているのですが、プロパティとして保持は出来るが、参照、追加などの場合は、クラス変数に対して直接行うといった感じなのでしょうか...?
ruuuu

2020/01/12 13:00

>self.common_var.append(value) では、インスタンス変数に common_var がない こちらですが、だとすると上側のコードの「common_bar」もインスタンス変数がないということになるのではないでしょうか...?
shiracamus

2020/01/12 13:02 編集

> プロパティとして保持は出来るが、参照、追加などの場合は、クラス変数に対して直接行うといった感じなのでしょうか...? インスタンスに保持してあれば、インスタンス変数を参照したり追加したりできます。
shiracamus

2020/01/12 13:04 編集

> こちらですが、だとすると上側のコードの「common_bar」もインスタンス変数がないということになるのではないでしょうか...? self.common_var = value によってインスタンス変数が新しく作られます。 クラス変数にある common_var もそのまま存在します。 クラスとインスタンスに同じ名前で別の値の変数が存在することになります。
ruuuu

2020/01/12 13:07

んーなるほどです... となりますと、もしかすると「append」による追加の為、クラス変数に対して直接参照や追加が行われていると言ったことなのでしょうか。。?
hayataka2049

2020/01/12 13:08

「代入」がものっっっすごく特別な操作である、というのはPythonの勘所の一つなのですが、わかりづらいところでもありますね。
shiracamus

2020/01/12 13:09

回答欄に、クラスとインスタンスの両方にリストを持つようにした例を追加してみました。
ruuuu

2020/01/12 13:19

追記ありがとうございます。 大変分かりやすく理解することができました。 ここまで丁寧に教えて頂き、理解できないのも申し訳ないとも思うのですが、どうしても、質問させて頂きました上側のコードの「print(tc2.common_var)」こちらの結果が、「Set Variable」ではなく、「Initial Value」になるのかが分らないです... 関数「set_variable」とは別に、追記頂きました以下のようなメソッドが必要となってくるのではないのでしょうか...? 「def __init__(self): self.common_var = [] # インスタンス変数」
shiracamus

2020/01/12 13:49

インスタンス変数がないときにクラス変数を探しに行く、ということを説明するためのコードでしょうから、あえて __init__ を書いていないのだと思います。 試しに、 def __init__(self): を定義して self.common = 123 とでも書いて実行してみてはいかがでしょうか?
shiracamus

2020/01/12 13:52

Qiitaの記事のコメントに図を書いてみます。
hayataka2049

2020/01/12 13:56 編集

>上側のコードの「print(tc2.common_var)」こちらの結果が、「Set Variable」ではなく、「Initial Value」になるのかが分らない tc1.common_varも見てみると理解が深まるかもしれない。 「インスタンス」変数はインスタンスごとに独立しているわけです(だから、あるインスタンスでは存在するが、他のインスタンスではそもそも存在しないかもしれない)。 この辺は静的型付け言語とは流儀が違います。そっちの知識で理解しようとして混乱しているのかもしれません。
ruuuu

2020/01/13 00:33 編集

ご親切に、ありがとうございます。 図でご解説頂きましたおかげで、大分理解出来て参りました。 自分はかなり勘違いをしていたかもしれないのですが、そもそもインスタンス化「tc1 = TestClass()」した時点では、インスタンスはインスタンス固有のプロパティ(common_var)を所持しておらず、変数、メソッドにおいて、クラスに定義されたものを使用する、といった理解で合っていますでしょうか...? そして、インスタンス変数を所持するには、「self.common_bar = 123」などのように、.appendなどではなく、代入をしなければならない、という理解で大方間違いありませんでしょうか...?
shiracamus

2020/01/13 00:50 編集

はい。その理解で正しいです。 一般的なクラス定義では、インスタンス生成時に自動的に呼び出される __init__ メソッド(初期化メソッド)を用意して、インスタンス self に初期値を代入する処理を書きます。
ruuuu

2020/01/13 00:50

ご丁寧にありがとうございました。 >print("tc1 variables:", vars(tc1)) ご回答に記載頂きました、こちらなどの、「vars」メソッドはクラスやインスタンス毎に対応づけられているプロパティと値を確認することが出来るのですね。かなり便利ですし、使いやすいですね。 色々と勉強になりました。 誠に、ご親切に教えて頂きありがとうございました。
ruuuu

2020/01/13 00:57

あ、そうなのですね... 確かに、調べて見ましたら「組み込み関数」と書いてありました。なんでもかんでも、関数 =「メソッド」という訳ではないのですね... >一般的なクラス定義では、インスタンス生成時に自動的に呼び出される __init__ メソッド(初期化メソッド)を用意して、インスタンス self に初期値を代入する処理を書きます。 こちら、他言語でいう所のコンストラクターのようなものなのですね。 クラス定義をする際は、忘れないように覚えておきます。
guest

0

class で最初に定義された common_var と、self.common_var は別物と考えてプログラムしないと、混乱は続きます。
最初の common_var はスタティクで、class の使用中、共有されます self.common_var はインスタンス毎に作成されて、インスタンスの数だけ存在します。self.common_var を 改名して、self.instance_var とします。common_var にアクセスする時に、TestClass.common_var とすることで、二つを別物に取り扱うことがはっきりします。

投稿2020/01/13 00:59

mmaeda

総合スコア269

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

ruuuu

2020/01/13 01:03 編集

ご回答ありがとうございます。 他、ご回答者様のご回答もあり、理解することが出来ました! >TestClass.common_var とすることで、二つを別物に取り扱うことがはっきりします。 こちらの部分に注意し、コーディングを行なっていきたいと思います。
guest

0

下のコードではメソッド内でcommon_varに代入が無いので、common_varはクラス変数でオブジェクト間で共有されているということでしょう。
上のコードは、メソッド内でcommon_varに代入があるので、common_varはインスタンス変数になります。

投稿2020/01/12 11:23

otn

総合スコア84576

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

ruuuu

2020/01/12 13:06

>下のコードではメソッド内でcommon_varに代入が無いので、common_varはクラス変数でオブジェクト間で共有されているということでしょう。 掲示さして貰いました、下側のコードも「self.common_var.append(value)」このように、値が追加されているかと思うのですが、「append」での追加であるため、クラス変数に直接代入されてしまうと言った感じでしょうか。。?
otn

2020/01/12 16:04

代入は、 変数 = 式 で、 変数.メソッド名(引数) は、メソッド呼び出しです。代入ではありません。 変数の保持するオブジェクトが書き換わるかどうかと、変数への代入は、無関係です。
ruuuu

2020/01/13 00:58

ご返信ありがとうございます。 勉強になりました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問