search
LoginSignup
0

More than 1 year has passed since last update.

posted at

メタクラスを用いてサブクラスの妥当性検証をする

はじめに

Twitterで一時期流行していた 100 Days Of Code なるものを先日知りました。本記事は、初学者である私が100日の学習を通してどの程度成長できるか記録を残すこと、アウトプットすることを目的とします。誤っている点、読みにくい点多々あると思います。ご指摘いただけると幸いです!

今回学習する教材

  • Effective Python

    • 8章構成
    • 本章216ページ
  • 第4章:メタクラスと属性

サブクラスをメタクラスで検証する

メタクラスとは

メタクラスはクラスより上の概念をぼんやりと意味する言葉であり、単純化すると、Python の class 文に割り込んで、クラスが定義されるたびに特別な振る舞いを与えるものです。
また、Python3 と Python2 で構文が違うようです。今回扱うのは Python3 です。

メタクラスは type からの継承で定義されます。
デフォルトでは、 __new__メソッドで関連するclass文の内容を受け取ります。

class Meta(type):
    def __new__(meta, name, bases, class_dict):
        print((meta, name, bases, class_dict))
        return type.__new__(meta, name, bases, class_dict)

メタクラスを指定するには、metaclass=メタクラス名 とします。

class MyClass(object, metaclass=Meta):
    one = 1

ex = MyClass()

メタクラスは、クラス名、それが継承している親クラス、class本体で定義されているすべてのクラス属性にアクセスできます。

ex = MyClass()
# (<class '__main__.Meta'>, 'MyClass', (<class 'object'>,), {'__module__': '__main__', '__qualname__': 'MyClass', 'one': 1})
print(one)
# 1
print(ex.__qualname__)
# __main__

サブクラスを検証

クラスが正しく定義されているかを検証することがメタクラスの使用法の一つです。
メタクラスは、新たなサブクラスが定義されるたびに妥当性検証(validation)コードを実行することで、正しく定義されているか確認することが可能です。
collections.abcなどによるクラスの妥当性検証は、基本的には__init__で実行されますが、メタクラスの場合は、それより前にエラーを発見できます。
例として、遠足のおやつの値段が300円以内か検証するコードを書きます。

class ValidateSnack(type):
    def __new__(meta, name, bases, class_dict):
        # 抽象Polygonクラスは妥当性検証しない
        if bases != (object,):
            if class_dict['sides'] >= 300:
                raise ValueError('おやつは300円まで!')
        return type.__new__(meta, name, bases, class_dict)

class Snack(object, metaclass=ValidateSnack):
    price = None # サブクラスで規定される


class Umaibo(Snack):
    price = 10

class Butamen(Snack):
    price = 70

class Cake(Snack):
    price = 400

上記のコードを実行すると、300円以上の駄菓子を定義しようとするタイミングで、妥当性検証コードがclass文本体の直後でエラーを発生させます。

Traceback (most recent call last):
  File "33_code.py", line 32, in <module>
    class Cake(Snack):
  File "33_code.py", line 19, in __new__
    raise ValueError('おやつは300円まで!')
ValueError: おやつは300円まで!

他にも、以下のようなことをしたいときにメタクラスを用いるようです。

  • プログラムで型を自動登録する
  • クラスが定義された後で、プロパティを修正したり、注釈を加える

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
What you can do with signing up
0