objectとtypeの関係を図にまとめた

なお、図では以下のようなクラスを使いました。

# 何も継承指定しないとobjectを継承したことになる(※Python3から)
class Foo: pass
# Fooを継承
class Bar(Foo): pass

# 独自のメタクラスを作成して使用
class MyType(type): pass
class Hoge(metaclass=MyType): pass

# クラスのインスタンス化
f = Foo()
b = Bar()
h = Hoge()

# ビルトインタイプのインスタンス化
n = 123
s = 'hello'

object

クラスの継承元を辿っていくと必ずobjectにたどり着きます。

f:id:itasuke:20171210224844p:plain

# 再掲
class Foo: pass
class Bar(Foo): pass

# ごく普通の継承
print(Foo.__bases__[0] is object)  # True
print(Bar.__bases__[0].__bases__[0] is object)  # True !!

# ビルトインタイプも同様
print(int.__bases__[0] is object)  # True
print(str.__bases__[0] is object)  # True

なぜ __bases__[0] のように複数形なのかというと、多重継承できるからです。

type

Pythonではクラスも含め全てがオブジェクトということになっています。では「そのオブジェクトはどのクラスから作られたの?」という質問を繰り返すと必ずtypeに辿り着きます。

f:id:itasuke:20171210224841p:plain

# 再掲
n = 123
s = 'hello'
f = Foo()
b = Bar()
h = Hoge()

# あなたのクラスは...?
print(n.__class__ is int)  # True
print(s.__class__ is str)  # True
print(f.__class__ is Foo)  # True
print(b.__class__ is Bar)  # True
print(h.__class__ is Hoge)  # True

# あなたのクラスのクラスは...?
print(n.__class__.__class__ is type)  # True
print(s.__class__.__class__ is type)  # True
print(f.__class__.__class__ is type)  # True
print(b.__class__.__class__ is type)  # True
print(h.__class__.__class__ is MyType)  # True !!
print(h.__class__.__class__.__class__ is type)  # True !!

issubclass / isinstance という関数もあるけれど

実は issubclassisinstance 関数を使うと、継承関係を一発で調べることができます。

print(issubclass(Bar, Foo))  # True
print(issubclass(Bar, object))  # True

print(isinstance(b, Bar))  # True
print(isinstance(b, Foo))  # True
print(isinstance(Bar, type))  # True
print(isinstance(Hoge, MyType))  #True !!

一方で、これらの関数は実際の継承関係になくても、継承していることにしてしまうことがあるので、この記事では使いませんでした。

from collections.abc import Sequence

print(issubclass(str, Sequence))  # True !!
print(isinstance('hello', Sequence))  # True !!

# str は Sequence なんぞ継承しとらんぞ
print(str.__mro__)  # (<class 'str'>, <class 'object'>)

なぜこんな機能が必要かというと、↓このような型チェックのために使います。ダックタイピングってやつです。

import collections.abc

# 何も継承しない(objectは除く)
class ZZZ:
    def __len__(self):
        return 0

def print_length(obj):
    # 抽象基底クラスの確認
    if isinstance(obj, collections.abc.Sized):
        print('あなたの長さは %d です' % len(obj))
    else:
        print('あなたに長さはありません')

# 長さのあるオブジェクト達
print_length('hello')  # 5
print_length([1, 2, 3])  # 3
print_length({'A':'ei', 'B':'bee'})  # 2
print_length(ZZZ())  # 0

# 長さのないオブジェクト達
print_length(None)  # なし
print_length(open)  # なし
print_length(123)  # なし

以上です。