LoginSignup
103
101

More than 3 years have passed since last update.

Pythonでメタプログラミング

Last updated at Posted at 2015-04-03

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を組み合せてもできるけど
前者でやった方がより拡張性が高く簡潔に書けそう。
あと、すべてクラスで完結できてるのでオブジェクト指向性が高い。

参考

103
101
3

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
103
101