はじめに
先日、自分のために書いたメモをちょっと整形してqiitaの記事にして投稿したところ、@shiracamusさんからのコメントで、ちょこちょこ勘違いをしているしていることが判明したため、反省を含め、インスタンスやクラス周りの勘違いしていたところをまとめました。
勘違い集なので、正しいのは題名の否定です(笑)
間違いを指摘してくださった@shiracamusさん、ありがとうございました。
第一の勘違い: クラスはオブジェクトじゃない
Javaの入門書読んだときの「型がクラスで、その型から作られたものがインスタンス」という記憶につられて、「インスタンスはクラスから作られたオブジェクト!!、クラスはオブジェクトじゃない!!」という思い込みをしていたのでありました。
class hoge:
pass
print(type(hoge))
結果
<class 'type'>
クラスはインスタンスとは違う何かだと思っていました。が、どちらもオブジェクトであり、確かに、typeクラスのインスタンスですね。
ちょっと道をそれた話になってしまいますが、__init__()
の中でクラス変数を定義したいと思います。(引用: @shiracamusさんのコメント)
class Person:
def __init__(self, name):
type(self).name = name
type()
はオブジェクトに対して使うと、クラスを返すためclass.function()
の形になり、クラス変数を定義できます。
第二の勘違い: クラス変数もインスタンス変数もどちらも結局は、インスタンスの持つ変数
この勘違いも、第一の勘違いから来ています。クラスはオブジェクトじゃないと思っていたので、値は保持しないのではないかと思っていました。
実際にはインスタンスとクラスはどちらもそれぞれ、変数の一覧を持っていてます。クラス直下で定義した変数はもちろんクラスの変数の一覧に入りますし、instance.function()
はインスタンスの変数一覧に(self.function()
もこの一例ですね)、class.function()
はクラスの変数一覧に入ります。
クラス変数を参照するときは、単にクラス変数の一覧を探しに行きますが、インスタンス変数を参照するときには、まず、インスタンス変数の一覧を探した後に、なかったらクラス変数の一覧を探しに行く、といった感じです。そのように考えると下の表にまとめた違いも納得できますね。(参考: Python の属性 - クラス変数とインスタンス変数ってなに? | 民主主義に乾杯 )
違い | クラス変数 | インスタンス変数 |
---|---|---|
クラスオブジェクトからの参照 | できる | できない |
インスタンスオブジェクトからの参照 | できる | できる |
変更すると | すべてのインスタンスに影響する | 特定のインスタンスにだけ影響する |
第三の勘違い: インスタンスの関数を呼び出すと自動的に第一引数が補完される
イメージとしてはあっていましたが、実際には、インスタンスの持つメソッドとクラスの持つメソッドはクラスがそもそも違いました。
class hoge:
def piyo():
pass
print(type(hoge.piyo))
hoge1 = hoge()
print(type(hoge1.piyo))
結果
<class 'function'>
<class 'method'>
クラス内に定義したメソッドはfunctionクラスのオブジェクトですが、インスタンス化されると、インスタンス自身を第一引数に追加してクラスの関数を呼び出すmethodクラスのオブジェクトが新たに作られます。ラッパー関数と呼ばれるそうです。このことから、instance.function()
のような場合、第1引数にインスタンスが渡されるが、class.function()
とした場合、引数が渡されない理由がわかりますね。1
参考
拙著に対する@shiracamusさんのコメント
-
厳密にいうと
instance.function()
もinstance.method()
と書くのが正しいのかもしれませんね ↩