Pythonのクラスアトリビュートとメタクラスの話

# 問題
```python
class Base:
    __BASE_PARAMS__ = ["base"]


class A(Base):
    __PARAMS__ = ["a"]

    
class B(Base):
    __PARAMS__ = ["b"]
```

上記のように`Base`クラスがあり、`A`クラスと`B`クラスは`Base`クラスを継承している。
`A`クラスと`B`クラスに、親クラスの`__BASE_PARAMS__`と自身の`__PARAMS__`を統合した`__ALL_PARAMS__`というクラスアトリビュートを追加したい。つまり、以下のような結果を得たいとする。

```python
A.__ALL_PARAMS__
# ['base', 'a']

B.__ALL_PARAMS__
# ['base', 'b']
```

普通に実装したら、以下のようになるだろう。

```python
class Base:
    __BASE_PARAMS__ = ["base"]


class A(Base):
    __PARAMS__ = ["a"]
    __ALL_PARAMS__ = Base.__BASE_PARAMS__ + __PARAMS__

    
class B(Base):
    __PARAMS__ = ["b"]
    __ALL_PARAMS__ = Base.__BASE_PARAMS__ + __PARAMS__


A.__ALL_PARAMS__
# ['base', 'a']

B.__ALL_PARAMS__
# ['base', 'b']
```
しかし、`Base`クラスを継承するクラスは多数あり、すべての継承先のクラスに処理とは無関係な`__ALL_PARAMS__ = Base.__BASE_PARAMS__ + __PARAMS__`というクラスアトリビュートを書くとなると面倒である。

継承先のクラスに無意味な記述をさせずに、簡潔にこの問題を解決する方法はないのだろうか？
皆さんも考えてみてほしい。

# メタプログラミング
メタプログラミングとは、プログラムでプログラムを定義する技法である。

ここではメタプログラミンの基本的な話はしない。メタプログラミングの事が知りたい場合は以下の記事などを参考にしてほしい。

[Pythonでメタプログラミング](http://qiita.com/fujimisakari/items/0d4786dd9ddeed4eb702)
[\_\_new\_\_と\_\_init\_\_とメタクラスと](http://qiita.com/FGtatsuro/items/49f907a809e53b874b18)
[pythonのメタクラスで、ORMのメソッドに比較演算子を文字列として渡して、where句を組み立てる。](http://qiita.com/mitsuru793/items/aa45f837dbe09b0874e5)

さて、メタクラスを使用すると、先程の問題を以下のように解決することができる。

```python:Python3用メタクラス
class MetaClass(type):    
    def __init__(cls, name, bases, attribute):
        super(MetaClass, cls).__init__(name, bases, attribute)
        cls.__ALL_PARAMS__ = cls.__BASE_PARAMS__ + getattr(cls, "__PARAMS__", [])


class Base(metaclass=MetaClass):
    __BASE_PARAMS__ = ["base"]


class A(Base):
    __PARAMS__ = ["a"]

    
class B(Base):
    __PARAMS__ = ["b"]


A.__ALL_PARAMS__
# ['base', 'a']

B.__ALL_PARAMS__
# ['base', 'b']
```

Python2の場合は特殊なアトリビュート`__metaclass__`にメタクラスを代入する。

```python:Python2用
class Base(object):
    __metaclass__ = MetaClass
```

## メタプログラミングいろいろ
メタプログラミングを使えば言語を拡張するような事もできる。
JavaにあるFinalクラスはPythonには存在しないが、メタクラスを使えば同じような機能を定義することができる。

```python:final_metaclass.py
class FinalMetaClass(type):
    def __init__(cls, name, bases, namespace):
        super(FinalMetaClass, cls).__init__(name, bases, namespace)
        for _class in bases:
            if isinstance(_class, FinalMetaClass):
                raise TypeError()


class A:
    pass


class B(A, metaclass=FinalMetaClass):
    pass


# Error!!
class C(B):
    pass
```

## \_\_init\_\_か\_\_new\_\_か？

typeを継承したメタクラスで、`__init__`と`__new__`のどちらを使えば良いのだろうか？
基本的に好みの問題で、どちらでもよい。
ただし`__new__`の方が`__slots__`を書き換える事ができるなど自由度が高い。

## 新しい特殊メソッド　\_\_prepare\_\_

Python3から特殊メソッド`__prepare__`が追加されている。
通常、`__dict__`は順序が保証されていないdict型である（Python3.6ではどうなったのだろうか）、`__prepare__`でこれを制御することができる。

以下、[Pythonドキュメント](http://docs.python.jp/3/reference/datamodel.html)からの抜粋である。

```python
import collections

class OrderedClass(type):

    @classmethod
    def __prepare__(metacls, name, bases, **kwds):
        return collections.OrderedDict()

    def __new__(metacls, name, bases, namespace, **kwds):
        cls = type.__new__(metacls, name, bases, dict(namespace))
        cls.members = tuple(namespace)
        return cls

class A(metaclass=OrderedClass):
    def one(self): pass
    def two(self): pass
    def three(self): pass
    def four(self): pass
    
A.members
# ('__module__', '__qualname__', 'one', 'two', 'three', 'four')
```

クラスメンバの列挙が定義順になっている事が確認できる。

## メタクラスの継承
```python
class MetaA(type):
    def __new__(mcls, *args, **kwargs):
        cls = type.__new__(mcls, *args, **kwargs)
        cls.MetaA = "Yes"
        return cls


class MetaB(type):
    def __new__(mcls, *args, **kwargs):
        cls = type.__new__(mcls, *args, **kwargs)
        cls.MetaB = "Yes"
        return cls        


class A(metaclass=MetaA):
    pass


class B(A, metaclass=MetaB):
    pass
```

`B`クラスを定義しようとすると、以下のエラーが出る。

```
TypeError: metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases
```

`metaclass`に渡せるのは`type`のサブクラスだけではない。
特定に引数に対応したCallableなものであれば何でも良い、この場合は関数を渡してダイヤモンド継承を解消すればよい。

```python
class MetaA(type):
    def __new__(mcls, *args, **kwargs):
        cls = type.__new__(mcls, *args, **kwargs)
        cls.MetaA = "Yes"
        return cls


class A(metaclass=MetaA):
    pass


def MetaB(mcls, *args, **kwargs):
    cls = type(mcls, *args, **kwargs)
    cls.MetaB = "Yes"
    return cls
    

class B(A, metaclass=MetaAB):
    pass

B.MetaA, B.MetaB
# ('Yes', 'Yes')
```

## メタプログラミングの使い所
メタプログラミングは、言語仕様を変えることができるほど強力なものである。
それゆえに、メタプログラミングの多用はチームプログラミングにおいて混乱の元となりえる。

基本的には設計がしっかりしていれば、メタプログラミングの出番は殆どないはずである。
例外的に継承先のクラスアトリビュートを生成する場合、メタクラスを使うとスッキリと書ける場合が多い。

# 参考
http://python-3-patterns-idioms-test.readthedocs.io/en/latest/Metaprogramming.html