Pythonのオブジェクトとクラスのビジュアルガイド – 全てがオブジェクトであるということ

Python開発者が大好きな言葉があります。それは”全てはオブジェクトである”です。実際、私自身もPythonのクラスを教えているときに何度も口にしています。多くの人が何度も聞いた言葉かもしれないのですが、私が言う度に生徒は賛同の相槌をしてくれます。結局のところ、Javaにおいて全ては(対象がそうでない場合を除いて)オブジェクトであり、.NETにおいても全てはオブジェクトであると、よく言われています。

しかしPythonにおいて全てがオブジェクトであると言う時、(私の生徒は驚いていましたが)「全て」にはクラスも含まれているのです。これはとても筋が通っていて、オブジェクトシステム全体が理解しやすくなります。しかしそれでも、広い視野で物事を見ることは難しいのです。

今回のブログ記事では、Pythonのオブジェクト間のつながりをざっと見ていきたいと思います。そして”全てはオブジェクトである”という考え方に由来するアイデアを固める助けになれば光栄です。またオブジェクト階層を構築するときに起こる興味深い点と、それがどのようにしてややこしくなるのかについて、ご紹介しましょう。

まずはシンプルなクラス(MyClass)とそのクラスのシンプルなインスタンス(m)から初めていきましょう。Pythonでは次のように書いていきます。

class MyClass(object):
    pass

m = MyClass()

Python 3では、MyClassがobjectから継承していると明示的に言う必要はありません。全てのクラスにとってこれが事実だからです。しかしPython 2ではobjectから継承する必要があります。もしそうしなければ、旧形式のクラスになってしまいますが、そうなってほしくないからです。

それでは、このことが視覚的にどう見えるかについて、mがMyClassのインスタンスであることを示す矢印を使って見ていきましょう。

Python-objects
今のところちょっと面白みはないですね。ところで、Pythonでは全てがオブジェクトだということを思い出してください。つまりmはMyClassのインスタンスです。type関数を使えば調べられます。

>>> type(m)
__main__.MyClass

では、MyClassの型が何であるかを確認するとどうなるでしょうか。

>>> type(MyClass)
type

そう、str、int、boolや他のPythonのクラスがtypeのインスタンスであるように、MyClassはtypeのインスタンスなのです。図表が少し複雑になってきましたね。
Python-objects-2
上記の図表で、mはMyClassのインスタンスであり、MyClassはtypeのインスタンスであるということが表されています。

標準のオブジェクトとクラスの主な違いとは、クラスは__bases__属性、つまりこのクラスの継承元を示すタプルを持っているということです。MyClassは、他のクラスと同様に、図表上に2つのポインタを持たせなくてはなりませんね。1つはtypeを表し、もう1つは継承元のクラス(object)を表すものです。

Python-objects-3
私が教えるPythonのクラスでは、型とオブジェクトの区別やオブジェクトの生存期間中のこれらの役割について混乱する生徒が多くいます。次を考えてみてください。

  • MyClassはtypeのインスタンスですから、クラスが生成された時に、そのクラスに何が起るかはtype.__init__によって判断されます。

  • MyClassはobjectから継承するので、mでメソッドを起動すると最初にMyClassのメソッドを探しに行きます。MyClassにメソッドが存在していない場合、Pythonがobjectにメソッドがないかを探します。
    ここまではいいですね。もう少し理解を深めましょう。MyClassがtypeのインスタンスだということは分かりました。これはつまり、typeそのものがクラスだということを意味しますよね? このtypeクラスの型は何でしょうか?

>>> type(type)
type

そう、私がPythonで好きなのは、typeの型がtypeであることです。言い方を変えれば、typeはそれ自身のインスタンスなのです。カッコいいでしょう? これを図表に当てはめてみましょう。

Python-objects-4
typeがクラスであれば、図表には2つのポインタがなくてはなりません。1つはtypeクラス、つまりtypeそのものを指すポインタと、このクラスが継承するクラスを指すポインタです。では、このtypeはどこから継承しているのでしょうか。

>>> type.__bases__
(object,)

これに従って、typeがobjectから継承していることを示す図表にアップデートしたいと思います。str(MyClass)を呼び出すと、個別のtype.__str__を作成することなく、継承された実装であるobject.__str__を当てにすることができるので、これは理にかなっています。それに、実際に以下のようなことが起こっています。

>>> type.__str__ is object.__str__
True

では、typeがオブジェクトから継承していることを示すように図表をアップデートしてみましょう。

Python-objects-5
最後に、objectクラスを無視しない場合を見てみます。objectはひとつのオブジェクトなので、型を持っています。クラスの型はtypeということは先ほどもうわかっていますね。これらを図表に加えてみます。

Python-objects-6
objectは継承階層の一番上にくることを覚えておいてください。Pythonでは空のタプルによって表現されます。

>>> object.__bases__
()

図表上では、以下のように表示することができます。

Python-objects-7
最後に、この階層に、MyClassのサブクラスとして新しいクラスを追加するとどうなるか見てみましょう。MySubClassはMyClassから継承していますが、typeのインスタンスであることは変わりありません。

Python-objects-81
Pythonの開発を経験したことがある人であれば、こういったことは当たり前のことでしょう。ですが様々なオブジェクトやクラスが相互に作用するので、Pythonに慣れてない人にとっては、この記事が理解を深める手助けになったことを願います。この他、理解に苦しむことなどあれば、是非ご連絡ください。今後、その説明をブログに掲載していきたいと思います。

この解説を気に入ってくれたようであれば、私の電子書籍『Practice Makes Python』も気に入ってくれることと思います。この書籍に掲載されている50の演習を行えば、Python言語の扱いが上達することでしょう。