Python3のメタクラスメモ

# Python3のメタクラスメモ

Python初心者がPython3のメタクラスについて学んだことのメモ
コード内のコメントはコンソールの出力結果を意図しています。
誤りや不適正な表現があればご指摘いただけると幸いです。

## メタクラスとは
あるインスタンスがどのクラス型かを調べる場合、type()を利用することができる。

```python
class Hoge:
    pass
h = Hoge()
print(type(h))
# <class '__main__.Hoge'>
```

Hogeクラスにもtype()を適用してみる。

```python
print(type(Hoge))
# <class 'type'>
```
これはHogeクラス自体がtype型のインスタンスであることを示している。hがHogeクラスのインスタンスであると同様に、Hogeクラスもまたtypeクラスのインスタンスということになる。***Python3で定義されたクラスは、typeという特殊なクラス（メタクラス）のインスタンスになる。***通常では、クラスを定義すると3つの引数を持つtype(classname, superclasses, attributes_dict)が呼ばれ、当該クラスのインスタンスが生成される。"通常"の定義方法ではなく、明示的にtypeクラスに3つの引数を与えクラスインスタンスを生成することも可能。上記のHogeクラスはtypeを用いて↓のように定義できる。(第一引数にはクラス名、第二引数には親クラスのタプル、第三引数には名前空間の辞書を渡す）

```python
Hoge = type('Hoge', (), {})
h = Hoge()
print(type(h))
# <class '__main__.Hoge'>
```

クラスインスタンスを生成するtypeクラスは、クラス（のインスタンス）を作る工場のようなもので***メタクラス***と呼ばれる。インタプリタがHogeクラスの宣言に直面した際、デフォルトではtypeクラスの\_\_new\_\_が呼ばれクラスインスタンスが生成されるが、このtypeクラスを継承することでメタクラスをカスタマイズすることができる。下記は、クラスの宣言時にデフォルトのtypeではなくtypeを継承したMetaTestクラスをメタクラスに設定した例である。

```python
class MetaTest(type):
    def __new__(cls, clsname, superclasses, attributedict):
        print("メタクラスのテスト")
class HogeHoge(metaclass=MetaTest):
    pass
# メタクラスのテスト
h = HogeHoge()
# TypeError: 'NoneType' object is not callable
```

MetaTestはデフォルトのメタクラスであるtypeクラスを継承し、クラスインスタンスを生成する\_\_new\_\_メソッドをオーバーライドしている。ここではクラスインスタンスを生成することなく、ただ文字列を出力している。そのため、MetaTestをメタクラスに設定したHogeHogeクラスを宣言した際、クラスインスタンスを生成するtype.\_\_new\_\_ではなく、文字列を返すだけのMetaTest.\_\_new\_\_が呼ばれる。コンソールにも「メタクラスのテスト」と表示された。次にHogeHogeクラスのインスタンスを生成しようとしたところ、エラーとなった。MetaTestクラスではHogeHogeクラスのインスタンスが生成されていない(Noneオブジェクトが生成されている？)。親クラスであるtypeの\_\_new\_\_をオーバーライドしてしまうと、クラスインスタンスが生成されなくなってしまう。\_\_new\_\_の戻り値にtype.\_\_new\_\_を呼んでクラスインスタンスを返すか、\_\_init\_\_をオーバーライドするとうまくいく（\_\_new\_\_が先に呼ばれた段階でクラスインスタンス生成済み？）。

```python
class MetaTestA(type):
    def __new__(cls, clsname, superclasses, attributedict):
        print("メタクラスAのテスト")
        return type.__new__(cls, clsname, superclasses, attributedict)
class MetaTestB(type):
    def __init__(cls, clsname, superclasses, attributedict):
        print("メタクラスBのテスト")
class HogeHogeA(metaclass=MetaTestA):
    pass
# メタクラスAのテスト
class HogeHogeB(metaclass=MetaTestB):
    pass
# メタクラスBのテスト
a = HogeHogeA()
b = HogeHogeB()
print(type(a))
#　<class '__main__.HogeHogeA'>
print(type(b))
#  <class '__main__.HogeHogeB'>
```

## メタクラスの活用例（Singleton）
メタクラスをカスタマイズすることでクラスインスタンスの振る舞いを制御できる。今回参考にした[こちら](https://www.python-course.eu/python3_metaclasses.php)のサイトでは、メタクラスを活用した↓のようなSingletonパターンが紹介されている。

```python
class Singleton(type):
    _instances = {}
    def __call__(cls, *args, **kwargs):
        if cls not in cls._instances:
            cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs)
        return cls._instances[cls]

class SingletonClass(metaclass=Singleton):
    pass
class RegularClass():
    pass
x = SingletonClass()
y = SingletonClass()
print(x == y)
# True
x = RegularClass()
y = RegularClass()
print(x == y)
# False
```

\_\_call\_\_メソッドを持つインスタンス(callableというらしい)は、インスタンス名()で関数のように\_\_call\_\_を呼び出すことができる。typeを継承したメタクラスであるSingletonはtypeクラスの\_\_call\_\_をオーバーライドしている。Singletonをメタクラスとして設定したSingletonClassクラス（Singletonのインスタンスであるクラス）が、SingletonClass()の形式で記述されるたびに（SingletonClassをインスタンス化するたびに）、\_\_call\_\_が呼びだされる。\_\_call\_\_内部では、\_instancesにインスタンスが格納されているかをチェックし、インスタンスが存在しない場合はSingletonの親クラスであるtypeの\_\_call\_\_メソッドが呼び出され、SingletonClassがインスタンス化される(ここでtypeの\_\_call\_\_がSingletonClassをインスタンス化しているようだけど詳しいことはよく分からない。)。すでに\_instancesにインスタンスが存在する場合は、新たにインスタンスを生成することはない。一方メタクラスがデフォルトのtypeクラスであるRegularClassは、都度インスタンスが生成されている。

##参考
https://www.yunabe.jp/docs/python_metaclass.html
https://www.python-course.eu/python3_metaclasses.php
https://realpython.com/python-metaclasses/
https://teratail.com/questions/180387