LoginSignup
23
33

オブジェクト指向のオブジェクトと型の関係が難しい

Posted at

循環する型

いろいろな事情があり(どんな事情だ?)、Pythonのオブジェクトシステムについて調べていました。そうすると、結構知らないなぁ。ということがあったので、まとめます。

割と自明な話ですが、pythonでは値の型を調べるのにtype関数を使います。

>>> a=1
>>> type(a)
<class 'int'>

ここで1の型がint型だと分かりました。では、type(1)の型は何でしょうか?

>>> ta = type(a) #type(a)の結果を代入
>>> type(ta)
<class 'type'>

ここで、int型の型はtype型であることが分かりました。ここで1つ謎ポイントだと思います。int型にも型が存在し、その型はtype型であるということです。では、type型の型は何でしょうか?

>>> tta = type(ta) #type(ta)の結果を代入
>>> type(tta)
<class 'type'>

ここで、type型の型はtype型であることが分かりました。多分、ゲシュタルト崩壊してくると思うので、図示してみます。

image.png

1に対し、typeを実行することで、型はint型だとわかります。
int型に対し、typeを実行することで、型はtype型だとわかります。
type型に対し、typeを実行することで、型はtype型だとわかります。

(ここでなぜintint型と表記していたのかが分かると思いますが、type型typeと表記してしまうと、type関数と混ざって脳みそが崩壊するので、あえて型には型と表記しています。)

objectとは

では、今度は、基底クラスについて考えてみます。ここでは簡単な例を考えてみます。

>>> class A:
...     def __init__(self):
...             print("A")
...
>>> class B(A):
...     def __init__(self):
...             super().__init__()
...             print("B")
...
>>> B()
A
B

 クラスAを定義し、それを継承したクラスとしてBを定義します。当たり前のことを言いますが、Aを継承したクラスBを定義しようとすると、Aを先に定義する必要があります。
 クラスの基底クラス(継承元)を取得するには__base__を見ればわかります。

>>> B.__base__
<class '__main__.A'>

では、1の基底クラスは何でしょうか?

>>> a=1
>>> a.__base__
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'int' object has no attribute '__base__'. Did you mean: '__hash__'?

1はクラスではないため、基底クラスは取得できません。では、int型の基底クラスは?

>>> ta=type(a)
>>> ta.__base__
<class 'object'>

int型の基底クラスはobject型だそうです。では、type型の基底クラスは?

>>> tta=type(ta)
>>> tta.__base__
<class 'object'>

type型の基底クラスはobject型だそうです。では、object型の基底クラスは?

>>> tab = ta.__base__
>>> tab
<class 'object'>
>>> tab.__base__
>>> tab.__base__ is None
True

こから、object型の基底クラスは存在しないことが分かりました。
ここまでの事実を図示するとこうなります。

image.png

int型の基底クラスはobject型
type型の基底クラスはobject型
object型の基底クラスは存在しない。

object型の型?

では、最後にobject型の型について調べてみます。

>>> tab = ta.__base__
>>> type(tab)
<class 'type'>

object型の型はtype型です。これらを図示すると以下のようになります。

image.png

objectと型の相互循環

たぶん混乱がすごいと思うので、混乱の元凶部分だけ書き出します。

image.png

事実を整理します。

事実1. インスタンスを生成するためにはクラスの定義が必要である。
事実2. 継承したクラスを定義するためには、基底クラスを事前に定義する必要がある。

例えば、1というint型のインスタンスを生成するためには、int型の定義が必要です(事実1)
int型はobject型を継承したクラスなので、基底クラスであるobject型の定義が必要です(事実2)

ここから、2つのパターンを見てみます。

パターン1. object型を先に定義する

object型を定義しようとしたとき、object型は基底クラスがないため、そのまま定義できそうです。(事実2はクリア)
しかし、object型自身はtype型のインスタンスとなっています。object型をtypeしたものがtype型であり、isinstanceでも、object型がtype型のインスタンスであることが確認できます。

>>> tta
<class 'type'>
>>> tab
<class 'object'>
>>> isinstance(tab,tta)
True

ということは、object型の生成には、type型が必要になります。(事実1)
では、type型を定義することを試みます。type型を定義しようとすると、基底クラスであるobjectが必要なります。しかし、今はobject型を定義しようとして、議論していたため、現在はobject型が未定義であるため、typeが定義できません。

パターン2. type型を先に定義する

type型を定義しようとすると、基底クラスであるobject型を定義する必要があります。object型はtype型のインスタンスです。そうすると、object型の定義にtype型が必要になります。ここで、循環が発生してしまい、定義できません。

ChatGPTさんに聞いてみる

どうも解せないので、ChatGPTさんに聞いてみます。

image.png

image.png

image.png

型とobject?

実は、ChatGPTさんによくよく聞いてみるとpythonの型がキモイということが分かりました。では、typescriptの型取得はどうなっているでしょうか?

$ npx ts-node
> const a=1
undefined
> typeof a
'number'
> typeof (typeof a)
'string'

 typescriptでは型の取得はtypeofを使いますが、このtypeofで返ってくる値は"string"型になっています。したがって、上記のような型とオブジェクトの循環定義問題のようなものは起きないようです。
 では、Javaの場合は?途中の過程は端折りますが、循環定義問題は起きてます。

image.png

型を表すjava.lang.Classは自分自身の型をjava.lang.Classとしており、その基底クラスをjava.lang.Objectとしています。java.lang.Objectの基底クラスはnullですが、型はjava.lang.Classとなっています。これはpythonのtype型とobject型の構図とほぼ同じです。

すべてはオブジェクトである。

オブジェクト指向のプログラミング言語でいいがちな話ですが、「すべてがオブジェクトである。」ということです。Pythonにはこういう記述があります。

Python における オブジェクト (object) とは、データを抽象的に表したものです。Python プログラムにおけるデータは全て、オブジェクトまたはオブジェクト間の関係として表されます。(ある意味では、プログラムコードもまたオブジェクトとして表されます。これはフォン・ノイマン: Von Neumann の "プログラム記憶方式コンピュータ: stored program computer" のモデルに適合します。)

 こう聞くと割と納得ではありますが、型自身すらobjectとなっていると割と面食らうものがあります。ChatGPTのいうことだけ聞いていると、pythonの型ってのは特殊な実装なんだなーぐらいで済んだのですが、どうやら割と純なオブジェクト指向を標榜する言語はどれでも起こってそうです。検証してないですがC#も恐らくそう。
 もともとオブジェクト指向を調べており、型を生成する型とかを考えると、そもそもクラスとインスタンスの境界があいまいになってきて、自分自身が迷宮に入ってきたのでまとめました。実際、クラスとインスタンスの境界が私にはわからなくなりました。
 私自身わかっていないですが、このようなオブジェクト指向の型システムを作ることを考えたとき、objectを定義するとき、その型は未定義にする。type型を定義する。objectの型をtype型と上書き定義する。みたいな、ちょっとした手続きによる工夫とそれができる実装が必要なんだろうなぁ。という感触があります。そこの確信をするには多分言語の実装まで見ないといけないので辛いですが・・・その辺もまたどっかで調べたいです。

23
33
3

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
23
33