Pythonでのメタプログラミングを紹介。
メタプログラミングを理解する上でtype、__new__、__metaclass__を理解する
必要があるためブロックで分けて紹介してます
クラスの定義はtypeのインスタンスだった
まずPythonでのメタプログラミングを理解するには、
クラスの定義はtypeのインスタンスだったということについて理解しないければならない。
以下は通常クラスとtypeのクラスの定義で同様の挙動となります。
class Hoge(object):
def function1(self, args):
return args
def function2(self, args):
return args + 50
def func1(self, args):
return args
def func2(self, args):
return args + 50
# type(クラス名、親クラス、メンバー属性)になります
Hoge = type('Hoge', (object, ), {'function1': func1, 'function2': func2})
上記からわかるように、クラス定義は実はtypeのインスタンスだったということです。
type(Hoge) は type を返し、 isinstance(Hoge, type) は True を返します。
__init__と__new__について
Pythonでインスタンスの生成を行うと__init__が呼び出されインスタンスの初期化が行われると
理解してますが、__init__の前には暗黙的に__new__による処理が存在しています。
この__new__で上記のようなtypeのインスタンス化が自動的に行われクラスが定義されています。
__metaclass__について
上記で__new__でtypeのインスタンス化が行われていると説明しましたが、
このtypeのインスタンス化を別の定義で置き換えることができます。それが__metaclass__になります。
__metaclass__に関数やクラスを定義することでtypeのインスタンス化時にクラスを拡張することができます
class HogeMeta(type):
def __new__(mcs, name, bases, dictionary):
cls = type.__new__(mcs, name, bases, dictionary)
setattr(cls, 'member1', 10)
setattr(cls, 'member2', 20)
return cls
class Hoge(object):
__metaclass__ = HogeMeta
print Hoge().member1 -> 10
print Hoge().member2 -> 20
サンプル
複数のSkillクラス定義をMetaクラスで管理して、
skill_keyで任意のクラスを取得する場合。
class AttackSkill(object):
name = u'特大攻撃'
class HealSkill(object):
name = u'回復'
class PoisonSkill(object):
name = u'毒攻撃'
class SkillMeta(type):
def __new__(mcs, name, bases, dictionary):
cls = type.__new__(mcs, name, bases, dictionary)
skills = {'attack': AttackSkill,
'heal': HealSkill,
'poison': PoisonSkill}
cls.SKILLS = skills
return cls
class Skills(object):
__metaclass__ = SkillMeta
SKILLS = {}
@classmethod
def get(cls, skill_key):
return cls.SKILLS[skill_key]
Skill = Skills.get
print Skill('attack').name -> 特大攻撃
print Skill('heal').name -> 回復
まとめ
サンプルでのやり方は、Metaプログラミングを利用せず、関数やdictを組み合せてもできるけど
前者でやった方がより拡張性が高く簡潔に書けそう。
あと、すべてクラスで完結できてるのでオブジェクト指向性が高い。
参考