Python のクラス変数とインスタンス変数って何?



違い 1. 使い分け


インスタンス
変数
そのインスタンスだけで
使う変数
  
クラス
変数
すべてのインスタンス
共有して使う変数



違い 2. 定義する場所
クラス変数とインスタンス変数は、どこで定義するの?

例えば、恋愛ゲームについて考えましょう。彼女 GirlFriend には名前 name, 彼氏への親密度 intimacy があり最小値 0, 最大値 100 とします。

× 間違った書き方

class GirlFriend(object):                                                                
  max_intimacy = 100
  min_intimacy = 0
  intimacy = 0
  name = ''

ο 正しい書き方

class GirlFriend(object):
  # インスタンスオブジェクト間で共有される変数
  max_intimacy = 100
  min_intimacy = 0
  
  # インスタンスオブジェクト間で共有しない変数
  def __init__(self, name):
    self.name =  name
    self.intimacy = 0
2.1. クラス変数

クラス定義文の直下で変数に代入。各インスタンスオブジェクトで共有しない変数は、クラス変数として定義します。

class GirlFriend(object):
  # クラス変数
  max_intimacy = 100                                                               
  min_intimacy = 0                                                                 
2.2. インスタンス変数

関数定義文の直下で代入された self の属性に代入。各インスタンスで共有しない値は、名前 name と親密性 intimacy は、__init__ 関数の中で定義してインスタンス変数に代入します。

class GirlFriend(object):
  def __init__(self, name):
    # インスタンス変数
    self.name =  name
    self.intimacy = 0




もう少し違いを追ってみたいと思います。1, 2 まで確認しました。このあと 3, 4, 5 を確認していきます。実際に操作をしながらクラス変数とインスタンス変数の違いを確認していきたいと思います。

違い クラス変数 インスタンス変数
1. 使い分け 全てのインスタンス
共有する値
そのインスタンス
だけで使う値
2. 定義場所 クラス定義文の直下で
代入された変数
関数定義文の直下で
代入された self の属性
3. クラス
オブジェクト
から参照
できる できない
4. インスタンス
オブジェクト
から参照
できる できる
5. 変更すると 全ての
インスタンスの属性
が変更される。
その
インスタンスの属性
だけが変更される。




違い 3. 参照
クラスブジェクトから参照できるか、できないか

引き続き、このような GirlFriend クラスについて考えます。

class GirlFriend(object):
  # インスタンスオブジェクト間で共有される変数
  max_intimacy = 100
  min_intimacy = 0
  
  # インスタンスオブジェクト間で共有しない変数
  def __init__(self, name):
    self.name =  name
    self.intimacy = 0
3.1. クラスオブジェクトからクラス変数を参照してみる。

クラスオブジェクトは、クラス定義の内部で属性に代入されたインスタンスオブジェクトを、外部から参照できる仕様になっています。

>>> # 参照できる
>>> GirlFriend.max_intimacy
100
>>>
>>> # 参照できる
>>> GirlFriend.min_intimacy
0
>>>
3.2. クラスオブジェクトからインスタンス変数を参照してみる。
>>> # 参照できない
>>> GirlFriend.name
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: type object 'GirlFriend' has no attribute 'name'
>>>
>>> # 参照できない
>>> GirlFriend.intimacy
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: type object 'GirlFriend' has no attribute 'intimacy'
>>>



違い 4. 参照
インスタンスブジェクトから参照できるか、できないか

>>> # 変数 girl_friend に代入されたものが
>>> # インスタンスオブジェクト
>>> girl_friend = GirlFriend('サーバルちゃん')
>>>
4.1 インスタンスオブジェクトからクラス変数を参照してみる。
>>> # 参照できる
>>> girl_friend.max_intimacy
100
>>>
>>> # 参照できる
>>> girl_friend.min_intimacy
0
>>>
4.2. インスタンスオブジェクトからインスタンス変数を参照してみる。
>>> # 参照できる
>>> girl_friend.name
サーバルちゃん
>>>
>>> # 参照できる
>>> girl_friend.intimacy
0
>>>

違い 5. 変更
クラス変数とインスタンス変数を変更してみる。

5.1. クラス変数を変更する。

クラス変数を変更すると、すべてのクラス変数が変更されます。

girl_friend1 = GirlFriend('サーバルちゃん')
girl_friend2 = GirlFriend('岩倉玲音')
girl_friend1.max_intimacy
girl_friend2.max_intimacy


# クラス変数を変更すると
# そこから生成された
# 全てのインスタンスオブジェクトの
# すべてのクラス変数が変更されます。
GirlFriend.max_intimacy = 50
girl_friend1.max_intimacy
girl_friend2.max_intimacy
>>> girl_friend1 = GirlFriend('サーバルちゃん')
>>> girl_friend2 = GirlFriend('岩倉玲音')
>>> 
>>> girl_friend1.max_intimacy
100
>>> girl_friend2.max_intimacy
100
>>> 
>>> # クラス変数を変更すると
>>> GirlFriend.max_intimacy = 50
>>>
>>> # そこから生成された全てのインスタンスオブジェクトの
>>> # すべてのクラス変数が変更されます。
>>> girl_friend1.max_intimacy
50
>>> girl_friend2.max_intimacy
50
>>>




ここまでは、表面的な動作の違いを復習しました。

もし、Python を習いたてて、すでに、お腹いっぱいだったり
クラスとは何かについて、まだ興味がわかなければ
ここまでで十分だと思います。

焦らず無理せず
興味が湧いた時を見計らって勉強をすることは、
効率的に学習する上で大切だと思っています。

山口県萩市
















(後編) クラス変数とインスタンス変数、動作の仕組み



いよいよ、ここからは、
もう少し突っ込んだ動作原理を見ていきたいと思います。

実は、この記事は以下の記事からの続きになります。
インスタンスオブジェクトとクラスオブジェクトって
何かをざっくりと押さえておくと理解しやすいかと思います。

疑問: なんでクラスオブジェクトの属性を変更すると、
そこから生成された全てのインスタンスオブジェクトの属性も変更されるの?


f:id:domodomodomo:20180414165056j:plain


答え:
インスタンスオブジェクトの属性
→ クラスオブジェクトの属性
→ クラスオブジェクトの親クラスオブジェクトの属性
→ クラスオブジェクトの親クラスオブジェクトの親クラスオブジェクトの...
といった順番に属性にアクセスしてるから。

属性アクセスのデフォルトの振る舞いは、オブジェクトの辞書の属性の取得、設定、削除です。例えば a.x は、まず a.__dict__['x']、それから type(a).__dict__['x']、さらに type(a) のメタクラスを除く基底クラスへと続くというように探索が連鎖します。
デスクリプタ HowTo ガイド — Python 3.6.5 ドキュメント





f:id:domodomodomo:20180113154538j:plain

問題 1. 復習

実行結果 1, 2 には何が表示されるでしょうか?

class GirlFriend(object):
    max_intimacy = 100
    min_intimacy = 0
    
    def __init__(self, name):
        self.name = name
        self.intimacy = 0


girl_friend1 = GirlFriend('サーバルちゃん')
girl_friend2 = GirlFriend('岩倉玲音')


# 1. クラスオブジェクトの属性を
girl_friend1.max_intimacy
girl_friend2.max_intimacy

# 2. 変更すると
GirlFriend.max_intimacy = 50

# 3. そこから生成された全ての
#    インスタンスオブジェクトの属性も変更される。
girl_friend1.max_intimacy  # 実行結果 1
girl_friend2.max_intimacy  # 実行結果 2
>>> # 答え(実行結果を抜粋したもの)
>>> girl_friend1.max_intimacy  # 実行結果 1
50
>>> girl_friend2.max_intimacy  # 実行結果 2
50
>>> 


問題 2. 本題

実行結果 3, 4, 5, 6 には何が表示されるでしょうか?

class GirlFriend(object):
    max_intimacy = 100
    min_intimacy = 0
    
    def __init__(self, name):
        self.name = name
        self.intimacy = 0


girl_friend1 = GirlFriend('サーバルちゃん')
girl_friend2 = GirlFriend('岩倉玲音')


# step1. girl_friend1 の属性 max_intimacy に
#          インスタンスオブジェクトは代入されてるかな?
# -> されてない...

# step2. GirlFriend の属性 max_intimacy に
#          インスタンスオブジェクトは代入されてるかな?
# -> 100 があった!
girl_friend1.max_intimacy


#
# インスタンスオブジェクトの属性を変えても...
#
girl_friend1.max_intimacy = 1000

# そのインスタンスオブジェクトは変わるが
girl_friend1.max_intimacy  # 実行結果 3

# クラスオブジェクトの属性にも
GirlFriend.max_intimacy  # 実行結果 4

# 他のインスタンスオブジェクトにも影響はない
girl_friend2.max_intimacy  # 実行結果 5


#
# インスタンスオブジェクトの属性を消せば...
#
del girl_friend1.max_intimacy

# またクラスオブジェクトの属性が参照される。
girl_friend1.max_intimacy  # 実行結果 6
>>> # 答え(実行結果を抜粋したもの)
... girl_friend1.max_intimacy  # 実行結果 3
1000
>>> 
>>> GirlFriend.max_intimacy  # 実行結果 4
100
>>> 
>>> girl_friend2.max_intimacy  # 実行結果 5
100
>>>
>>> girl_friend1.max_intimacy  # 実行結果 6
100
>>> 

だいたいの属性アクセスを動作の導出してみる。

マニュアルの文章から上の図を導き出したいと思います。

そのために、関数呼び出し、日本語の表記を除いて、最終的に obj.attr のような属性参照だけに書き換えていきます。厳密に書いてるわけではないので、ざっくり眺めてもらえると嬉しいです。

属性アクセスのデフォルトの振る舞いは、オブジェクトの辞書の属性の取得、設定、削除です。例えば a.x は、まず a.__dict__['x']、それから type(a).__dict__['x']、さらに type(a) のメタクラスを除く基底クラスへと続くというように探索が連鎖します。
デスクリプタ HowTo ガイド — Python 3.6.5 ドキュメント

Step1: マニュアルの文章を、ちょっと書き換えてみます。

a.x
→ a.__dict__['x']
→ type(a).__dict__['x']
→ type(a) のメタクラスを除く基底クラスの __dict__['x']
→ type(a) のメタクラスを除く基底クラスの メタクラスを除く基底クラス..
...

Step2: class.__bases__

「type(a) のメタクラスを除く基底クラス」→ type(a).__bases__[0]

a.x
→ a.__dict__['x']
→ type(a).__dict__['x']
type(a).__bases__[0].__dict__['x']
type(a).__bases__[0].__bases__[0].__dict__['x']
...


「type(a) のメタクラスを除く基底クラス」とは、オブジェクト a を生成したクラスの親クラスのことを指しています。だいたいの場合 type(a).__bases__[0] と同じです。__bases__ がタプルになっているのは、多重継承している場合があるからです。

クラスオブジェクトの基底クラスのタプルです。
class.__bases__



Step3: instance.__dict__

a.__dict__['x'] → a.x

a.x
→ a.x
→ type(a).x
→ type(a).__bases__[0].x
→ type(a).__bases__[0].__bases__[0].x
...


もう1回書き換えて
a.x
→ a.x
→ type(a).x
→ type(a).__bases__[0].x
→ type(a).__bases__[0].__bases__[0].x
...


a.__dict__['x'] は、だいたいの場合 a.x と同じです。 Python では、属性を自由に追加できます。 このような機能を、辞書を使って実装していることがわかります。

オブジェクトの (書き込み可能な) 属性を保存するために使われる辞書またはその他のマッピングオブジェクトです。
__dict__ 属性


Python では属性だけではなく変数も辞書です。名前空間そのものが辞書で表現されています。

# var = 0
locals().update({'var': 0})
print(var)  # 0


# obj.attr = 1
class Cls(object):
    pass


obj = Cls()
obj.__dict__.update({'obj_attr': 1})
print(obj.obj_attr)  # 1


Step4: type 関数, instance.__class__

type(a) → a.__class__

a.x
a.__class__.x
a.__class__.__bases__[0].x
a.__class__.__bases__[0].__bases__[0].x ...

tyep(a) は、だいたいの場合 __class__ と同じです。

返り値は型オブジェクトで、一般に object.__class__ によって返されるのと同じオブジェクトです。
type 関数

クラスインスタンスが属しているクラスです。
__class__ 属性

 

◯ __class__ 属性への代入

__class__ 属性に別のクラスオブジェクトを代入することでクラスキャストのような動作をします。
Python でクラスキャスト -  

ポイント

Python のクラスは
名前空間をつなぎ合わせただけで
表現されています。


最初の図は、Python のクラスが、名前空間をつなぎ合わせただけで表現されていることを伝えようとしていました。どのようにしてつなぎ合わせているかというと、単純に属性に代入しているだけです。

具体的に言えば、インスタンスオブジェクトの __class__ 属性には、クラスオブジェクトが代入されています。クラスオブジェクトの __bases__ 属性には、親クラスオブジェクトが代入されています。

そこから、インスタンス→クラス→親クラスと、属性を順番に参照する機能をつけ加えて、クラスそして継承、多態性を表現しています。

◯「インスタンス化」とは何ですか?

と聞かれたら、「新しい名前空間インスタンスオブジェクト)を作って、そこにクラスオブジェクトを付け足すこと。」と説明できたりするのかなと思ったりもします。

◯「継承」とは何ですか?

と聞かれたら、「新しい名前空間(クラスオブジェクト)を作って、そこにクラスオブジェクトを付け足すこと。そして、インスタンス→クラス→親クラスと、属性を順番に参照する機能によって、継承を表現しています。」と説明できたりするのかなと思ったりもします。

Python では「継承」が「コンポジション」で表現されています。

コンポジション(Composition)は、日本語で「混合物」を意味する単語である。あるクラスの機能を持つクラスのことを指す。 特定のクラスの機能を、自分が作るクラスにも持たせたい場合に、継承を使わずフィールドとしてそのクラスを持ち、 そのクラスのメソッドを呼び出すメソッドを持たせること。そうすることで、クラスに他のクラスの機能を組み込むことができる。
継承とコンポジションをどう使い分けるか - いぬごやねっと


継承よりもコンポジション
- composition over inheritance

実装を伴う「継承」は、避けるべきだとされているらしいです。
composition over inheritance - Wikipedia

ただ Python では 実装による「継承」 が「コンポジション」によって表現されていたとしても、動作が実装による「継承」と同じなので、結局、可能なら避けた方が望ましいことに変わりはないのかなと思ったりもします。

Java extends は悪だ; チャールズ・マンソンほどでないかもしれないが、可能ならいつでも避けなければならないほど悪いものだ。GoFデザインパターンの本は、ページ数を割いて、実装による継承 (extends) をインターフェイス (implements) による実装に置き換える方法について議論している。
The extends keyword is evil; maybe not at the Charles Manson level, but bad enough that it should be shunned whenever possible. The Gang of Four Design Patterns book discusses at length replacing implementation inheritance (extends) with interface inheritance (implements).

良い設計者は、ほとんどのコードをインターフェイスについて書いていて、具象ベースのクラスについては書いていない。この記事は、なぜ設計者がそのような奇妙な習慣を持つのかを説明し、2、3のインターフェイスベースのプログラミングの基礎について導入する。
Good designers write most of their code in terms of interfaces, not concrete base classes. This article describes why designers have such odd habits, and also introduces a few interface-based programming basics.
Why extends is evil; Improve your code by replacing concrete base classes with interfaces


これにしても別の言語の話になりますが最近生まれた Go にしても Rust にしても継承という機能そのものを切ってしまっています。


属性を順番に参照する機能は、実装による「継承」を実現できるというメリットもありますが、実装による「継承」そのものがデメリットだったりもするらしいです。

かつて私は Java の開発者である James Gosling Java がゲストスピーカーに呼ばれたユーザミーティングに参加しました。印象的な Q&A セッションの間、誰かが彼に尋ねました。「もし、 Java をもう一度開発し直せるなら、何を変えますか?」。彼は答えました「クラスを除きます。」
I once attended a Java user group meeting where James Gosling (Java's inventor) was the featured speaker. During the memorable Q&A session, someone asked him: "If you could do Java over again, what would you change?" "I'd leave out classes," he replied.

笑いが収まった後、彼は本当の問題がクラスそのものではなく、むしろ(extends の関係である)実装継承にあることを説明しました。(implements の関係である)interface 継承が望ましい。もしできるなら、実装継承は、いつも避けるべきだ。
After the laughter died down, he explained that the real problem wasn't classes per se, but rather implementation inheritance (the extends relationship). Interface inheritance (the implements relationship) is preferable. You should avoid implementation inheritance whenever possible.
Why extends is evil; Improve your code by replacing concrete base classes with interfaces


他にも Java でさえ意図的に切った実装を伴う多重継承という機能についても、Python では、しっかり実装されてしまっています。Python は、可読性をかなり重視して、設計をしているにも関わらず。

なぜなら多重継承は問題点が多いと思われたためである。

  1. 継承関係が複雑になるため全体の把握が困難になる。
  2. 名前の衝突。同じ名前を複数の基底クラスがそれぞれ別の意味で用いていた場合、その両方を派生クラスでオーバーライドするのが困難。
  3. 処理系の実装が複雑になってしまう。
  4. ... 省略 ...

しかしながら多重継承を使う方が直感的になる場合もあるとの主張もあり、どちらが正しいとは言えない状況である。
継承 (プログラミング) - Wikipedia


わかっていながらなんで「実装による多重継承」なんていう機能をつけたかというと、完全に推測ですが、恐らく、わかっていなかったからです。1995 年に Java が生まれて多くのシステムが開発され、どうも実装を伴う「継承」はあかんらしいという知見が得られる前の 1991 年に Python は生まれました。Python は、日本では最近人気を得た言語ですが、実際には Java よりも Python の方が、ずっとおじいちゃんな言語です。

Pythonは死にかけの言語なのか
前略 ...「いいえ、そうではありません。」Pythonは”比較的に”古い言語です。最初に登場したのは1990年代初期です(仮に1991年としましょう)。そして、他のプログラミング言語と同様に、Pythonも正しい選択をすることと、妥協することが必要でした。どんなプログラミング言語にもそれぞれのクセがあります。より最近の言語は過去の失敗から学んでいる傾向にあります。これはいい傾向です(ちなみに、RのリリースはPythonが登場してからそれほど経過していない1995年でした)。当時のPythonは、他の言語もそうであるように、”完璧”には程遠く、欠点だらけでした。
Pythonや機械学習、そして言語の競争について - POSTD

変数, 属性の命名規則

◯ システムで定義された名前 (system-defined name)

__bases__, __class__, __dict__ とかアンダースコア _ をたくさん使った変数が出てきました。これは何でしょうか?システムで定義された (system-defined) 名前です。Python が使っているか、あるいは Python が使い方を指定した変数です。__init__ なんかもこれの仲間に入ります。

システムで定義された (system-defined) 名前です。これらの名前はインタプリタと (標準ライブラリを含む) 実装上で定義されています; ... (中略) ... このドキュメントで明記されている用法に従わない、 あらゆる __*__ の名前は、いかなる文脈における利用でも、警告無く損害を引き起こすことがあります。
2. 字句解析 — Python 3.6.5 ドキュメント


__*__ という名前を勝手に使わないでね、という命名規則がなぜあるのでしょうか?例えば、使っていない名前があるなら、使いたくもなります。__*__ という名前を勝手に使われてしまうと、Python が、新しくシステムが使う変数やメソッドを定義しようと思ったときに、問題が起こります。Python がバージョンアップするときに、新しい変数やメソッドを追加できなくなってしまうからです。

例えば Python 3.5 では新しく __matmul__, __rmatmul__, __imatmul__ が追加されました。これらの属性をあるコードが勝手に別の用途で使っていたら Python 3.5 では、そのコードが動かなくなってしまいます。つまり、Python 3.5 は前のバージョンのコードに対して、後方互換性を失ってしまうことになります。以下、Guido の文章を引用します。
PEP 465 - 行列の乗算専用の中置演算子
特殊属性 special attribute
特殊メソッド special method

私が説明すると約束した2つ目の Python の設計背景は、なぜ特殊メソッドの見た目を単純に special とはせずに __special__ したのかだ。
The second bit of Python rationale I promised to explain is the reason why I chose special methods to look __special__ and not merely special.

私は、多くのクラスがメソッドをオーバーライドするだろうと考えた。例えば、一般的なメソッド(例えば __add__ や __getitem__)、あまり一般的でないメソッド(例えば pickle の __reduce__。これは、長いこと C 言語で一切サポートされていませんでした。)
I was anticipating lots of operations that classes might want to override, some standard (e.g. __add__ or __getitem__), some not so standard (e.g. pickle‘s __reduce__ for a long time had no support in C code at all).

私はこれらの特殊メソッドには、一般的なメソッド名を使って欲しくなかった。なぜなら、すでに設計されたクラス、またはこれらの全ての特殊メソッドを覚えていないユーザによって書かれたクラスが、意図せずメソッドをオーバーライドしてしやすく、結果として悲劇的な結果を引き起こす可能性を秘めているからだ("すでに設計されたクラス" というのは、特殊メソッドを追加した時に、古いコードで同じ特殊メソッド名が既に使われてしまうと、後方互換性が失われることを指しているのかな..)
I didn’t want these special operations to use ordinary method names, because then pre-existing classes, or classes written by users without an encyclopedic memory for all the special methods, would be liable to accidentally define operations they didn’t mean to implement, with possibly disastrous consequences.

Why does Python use methods for some functionality (e.g. list.index()) but functions for other (e.g. len(list))?

◯ プライベートな識別子

その他にもアンダースコア _ を使う命名規則として、プライベートな識別子であるかどうかを明示するために使います。プライベートな識別子であるとは、obj.attr のようにオブジェクトの属性として外部から参照されないということです。

項番 レベル 命名規則 参照の仕方
1 モジュール _val1 参照できない
2 クラス _val2 obj._val2
3 クラス __val3 obj._クラス名__val3
4 インスタンス _val4 obj._val4
5 インスタンス __val5 obj._クラス名__val5
6 関数 val6 参照できない
# モジュール定義文

# 1. モジュールのローカル変数
_val1 = 1
"""
# 参照
できない
# 説明
モジュールを import しても
この変数 _val は import されません。
"""


class Person(object):
    # クラス定義文

    # 2. クラスのローカル変数
    _val2 = 10
    """
    # 参照
    obj._val2
    # 機能
    無。命名規則です。
    """
    __val3 = 100
    """
    # 参照
    Person._Person__val3
    # 機能
    Person.__val3 では参照できない。
    Person._Person__val3 として参照します。
    # いつ使うの?
    他のクラスから継承されたときに
    名前が上書きされてしまうのを避けるため。
    """

    def func(self):
        # 関数定義文

        # 3. インスタンスのローカル変数
        self._val4 = 1000
        self.__val5 = 10000
        """
        クラスのローカル変数と同じ
        """

        # 4. 関数のローカル変数
        val6 = 100000
        """
        # 参照
        参照できない
        # 機能
        参照できない
        # 説明
        _val6 = 100000
        とはしないことの方が多い気がします。

        1つ目の理由は、
        関数のローカル変数なら、
        スコープが小さいので大抵自明だから。

        2つ目の理由は、
        関数には属性という概念は無いので
        func.val という様に外部から参照できないから。
        """

2.3.2. 予約済みの識別子種 (reserved classes of identifiers)
実践されている命名方法

クラス変数とインスタンス変数って何?



インスタンス変数はインスタンスオブジェクトの属性です。
クラス変数はクラスオブジェクトの属性です。





次は、関数とメソッドの違いを明確にして、さらにクラスの理解を深めます。