『Effective Python』の続き。メタクラスとディスクリプタの華麗なる関係。 www.effectivepython.com
メタクラスの応用例、そしてChapter 4の最後を飾るのはクラス属性の注釈、またはメタクラスとディスクリプタの合わせ技の話である。
データベースのレコードをPythonのクラスで表したいという状況を考える。
レコードの要素をField
ディスクリプタで、レコードをCustomer
という具合である。
class Field(object): def __init__(self, name): self.name = name self.internal_name = '_' + self.name def __get__(self, instance, instance_type): if instance is None: return self return getattr(instance, self.internal_name, '') def __set__(self, instance, value): setattr(instance, self.internal_name, value) class Customer(object): # Class attributes first_name = Field('first_name') last_name = Field('last_name') prefix = Field('prefix') suffix = Field('suffix')
これでも上手く動作するが、Customer.first_name
にField('first_name')
を渡していてfirst_name
を二重に登録するはめになる。
コレは面倒だ、ということでメタクラスの出番である。
class Meta(type): def __new__(meta, name, bases, class_dict): for key, value in class_dict.items(): if isinstance(value, Field): value.name = key value.internal_name = '_' + key cls = type.__new__(meta, name, bases, class_dict) return cls class DatabaseRow(object, metaclass=Meta): pass
これを使えば、Field
に引数first_name
を渡すことなくやりたいことを実現できる。
これでChapter 4が終わった。Chapter 3が1ヶ月掛かったが、これは10日で終わった。 夏季休暇で十分な時間が取れたからであろうか。 学部生か院生の頃か忘れてしまったが、恩師は僕に対して「本をゆっくり読めるのは学生時代だけである」とおっしゃった。 今、その意味をかみ締めながら日々を過ごしている。