【中級者への道】Pythonはすべてオブジェクトらしい


まとめへのリンク

はじめに

Pythonはどうやらすべてオブジェクトらしいです。

?????本当なのでしょうか?????

オブジェクトとは?

Pythonのオブジェクトは、Pythonが扱うすべてのものをさします。
そして、オブジェクトは属性を持ちます。

例えば

class A:

    def __init__(self):
        self.x = 50

a = A()
print(a.x)

以上のコードで、a.xのように「ピリオド」を使って値を取り出しています。
このピリオドを使って取り出せるのが「属性」(x)であり、属性を持つということはオブジェクト(a)という意味でもあります。

オブジェクトの例を見てみる

class A:

    def __init__(self):
        self.x = 50


a = A()
print(type(a))
print(type(A))
print(type(30))
print(type(int))

'''
<class '__main__.A'>
<class 'type'>
<class 'int'>
<class 'type'>
'''

以上のような例を見て、実際にPythonのデータはすべてオブジェクトなんだなぁ〜というのを見てみます。

以上のコードでは、typeという組み込み関数を使用しています。
typeは引数に渡したオブジェクトの型を返してくれる関数です。

例えば、aというオブジェクトはmainモジュールのAというクラスの型になっています。
また、そのそもそもの型であるAというクラスは、typeという型になっています。
同様に、Pythonの数値である30も、intという型であり、30はintのインスタンスオブジェクトであるとわかります。

このように、どうやらPythonのデータはすべてオブジェクトで構成されており

  • クラスオブジェクト
  • インスタンスオブジェクト

で構成されているようです。

クラスオブジェクト

それでは、クラスオブジェクトとはなんでしょうか?

クラスオブジェクトとは、その名の通り
クラスを定義し終わったときのそのクラスのオブジェクト をさします。

例えば、

class A:
    x = 50
    print(10)

    def __init__(self):
        self.x = 100
        print(self.x)


print(A.x)
a = A()

以上のようなソースを考えてみます。

これの実行結果はどうなるでしょうか?

これは

10
50
100

のようになります。

Pythonのクラスは、def以外の直下は、そのまま実行され各行が通常の文のように実行されます。

そのため、Pythonファイルを上から読み込む時 class Aを見つけると
そのまま下に下っていき

  • x = 50
  • print(10)

を実行し、そしてclass Aを読み込み終わったら、Aというクラスオブジェクトを生成します。
そのため、Aは読み込みが終了した時点でクラスオブジェクトというものになります。

クラス の オブジェクトであるということが大事です。

インスタンスオブジェクト

インスタンスオブジェクトは「クラスオブジェクト」から生成されるオブジェクトです。

具体的には

  • 30(intから生成される)
  • a(Aから生成される)
  • 関数

などがあります。

クラスオブジェクトから生成されるため、

class A:

    def __init__(self):
        self.x = 100


a = A()
print(type(a))
print(type(30))
print(type(int))

'''
<class '__main__.A'>
<class 'int'>
<class 'type'>
'''

aは、aの型であるクラスオブジェクトmain.A
30は、30の型であるクラスオブジェクトint

になっています。

このように、インスタンスオブジェクトはクラスオブジェクトから生成されます。

関数も同様にfunctionクラスというクラスオブジェクトから生成されます。

応用

関数も同様にオブジェクトです。
オブジェクトということは、属性を使用することができます。
特に、関数は自由に属性を設定できるインスタンスオブジェクトのため、

例えば

  • matplotlibを読み込めるか、一回のみチェックする

という関数を作成できます。

def can_load_matplotlib():
    if can_load_matplotlib.cache is None:
        try:
            import matplotlib
        except ImportError:
            can_load_matplotlib.cache = False
        else:
            can_load_matplotlib.cache = True
    return can_load_matplotlib.cache


can_load_matplotlib.cache = None

print(can_load_matplotlib())

can_matplotlib関数を用意します。

Pythonファイルは、関数が実行されるまで、「関数がこの行にあるな〜〜」という同一性(メモリ)の作成しか行いません。

そのため、まず上記のコードでは

  • can_load_matplotlibという関数があるな〜となる
  • can_load_matplotlibはFunctionクラスのインスタンスオブジェクトで、属性が自由に外側から設定できるため、cache属性を追加する
  • 関数を実行すると、初回はNoneのため、内部でtryする
  • その結果、上手く呼び出せるか呼び出せないかで、呼び出しを変数にキャッシュできる

というコードを書くことができます。

このコードは、海外の型のGitHubのコードを写経した時出会い、すごいなぁと思いました。
ただ、キャッシュすることで、どれくらい速度が上がるのか?というのはわかってないです。

最後に

Pythonでは、クラスも数値も文字列もすべてオブジェクトということを見てきました。

インスタンスかクラスかのオブジェクトを見分けながら、書いていきたいと思います。

参考文献