なんのこっちゃって感じ。rest_frameworkのソース読んでて、

あー、これはこういう事に使うのかってピンと来たので残しておく。

DjangoのModelもそうなんだけど、シリアライザを作る時に

class CommentSerializer(serializers.Serializer):
    email = serializers.EmailField()
    content = serializers.CharField(max_length=200)
    created = serializers.DateTimeField()

こんな感じで、「クラス変数」にモデルの定義を入れてる。

で、クラス変数ってstaticだから、インスタンス化して使おうが、

結局全インスタンスで共有されてる変数。

でも、DjangoもRestFrameworkもちゃんとインスタンス変数になってる。

なんでかなーって不思議だったんですが、ソース読んでると、

metaclassの定義使って、クラス定義読み込まれる時に、クラス変数を潰して、

インスタンス変数作ってるんですね。

RestFrameworkのソースだと↓の部分。

class SerializerMetaclass(type):
    """
    This metaclass sets a dictionary named `_declared_fields` on the class.
    Any instances of `Field` included as attributes on either the class
    or on any of its superclasses will be include in the
    `_declared_fields` dictionary.
    """

    @classmethod
    def _get_declared_fields(cls, bases, attrs):
        fields = [(field_name, attrs.pop(field_name))
                  for field_name, obj in list(attrs.items())
                  if isinstance(obj, Field)]
        fields.sort(key=lambda x: x[1]._creation_counter)

        # If this class is subclassing another Serializer, add that Serializer's
        # fields.  Note that we loop over the bases in *reverse*. This is necessary
        # in order to maintain the correct order of fields.
        for base in reversed(bases):
            if hasattr(base, '_declared_fields'):
                fields = [
                    (field_name, obj) for field_name, obj
                    in base._declared_fields.items()
                    if field_name not in attrs
                ] + fields

        return OrderedDict(fields)

    def __new__(cls, name, bases, attrs):
        attrs['_declared_fields'] = cls._get_declared_fields(bases, attrs)
        return super().__new__(cls, name, bases, attrs)

で、Serializerの定義が↓

class Serializer(BaseSerializer, metaclass=SerializerMetaclass):

SerializerMetaclassの↓のところ

    @classmethod
    def _get_declared_fields(cls, bases, attrs):
        fields = [(field_name, attrs.pop(field_name))
                  for field_name, obj in list(attrs.items())
                  if isinstance(obj, Field)]
        fields.sort(key=lambda x: x[1]._creation_counter)

attrsにクラス定義でのクラス変数とか入ってて、これが最終的にクラス定義内に

展開されるっぽい。なので、attrs.popでクラス変数として消してる。

消すタイミングで、fieldsって変数に定義を退避してる感じ。

で、typeを継承してて、最後に「return super().__new__(cls, name, bases, attrs)」してる

という感じ。

なるほどー。ってなんか納得した。

pythonでメタプログラミングするならtype継承してクラス定義が作られてるって

知っとけみたいな話見てて「は?」って思ってたんだけど、

こう具体的なtypeの使い方見ると納得できますね。。。

で、↑のが何で便利なのかというと、MVCとMVVMとかで作ってる時って、

View層とのやり取りは専用のViewModel作ると思うんですよ。

バリデーションとか統一しやすいし。

で、ViewModelの定義する時のベースにこれがすごく使える。

一つ一つのフィールドのデータ型を個別に作れるので。

一つ賢くなりましたというお話。。。