Help us understand the problem. What is going on with this article?

[Python]サブクラスのメソッドに特定の処理を強制する方法

サブクラスに特定の処理を強制したい

Pythonでとあるクラスを継承したサブクラスを設計するとき、継承先には必ずこんな処理をして欲しい...なんてことがあります。

例えば__init__が呼ばれた時に必ずsuper().init()を呼んで欲しい...などの場合です。

__init__に限らず「親クラスのメソッドをオーバーライドしたいけど、親クラスのメソッドもそのまま呼び出したい!」なんて処理はよくよく現れるものですが、親クラスのメソッドを呼び出す処理を書き忘れてしまってエラーを吐かせるのはいやですし、そもそも親クラスのメソッドを呼び出す仕様そのものが面倒くさかったりしますね。

今回は子クラスのメソッドfが呼ばれるとき、その処理の後で必ず親クラスのメソッドfが呼ばれるような実装を実現してみます。

やり方

「子クラスの関数f」を「子クラスの関数fと親クラスの関数fを実行する関数new_f」に書き換える
ってことをやってくれるメタクラスをかく

メタクラスってなんや?って話はまた今度違うところでします。とりあえずはクラスの定義のされ方をいじるクラスだと思っておいてください。

よくわからない人でも下記コードをコピーペーストしてdecorateメソッドを書き換えるだけでなんでもできると信じています。

前提

Python: 3.7.2 (Python3なら多分OK、Python2でもmetaclassの指定方法が違うだけなはず)

コード

class ParentCaller(type):
    def __new__(cls, name, base, attr):
        # ParentクラスのfがParentクラスのfを呼ぶのを防ぐ
        if name != "Parent":
            # クラス生成時、関数fは自動的にデコレートされる仕様にする
            attr["f"] = cls.decorate(attr["f"])
        return super().__new__(cls, name, base, attr)

    @classmethod
    def decorate(cls, f):
        # 関数fを受け取ると、fとParent.fをセットで実行するnew_fを返す
        def new_f(self, x):
            f(self, x)
            Parent.f(self, x)
        return new_f


class Parent(metaclass=ParentCaller):
    def f(self, x):
        print(f"parent's f says: {x}")


class Child(Parent):
    def f(self, x):
        print(f"child's f says: {x}")


child = Child()
child.f("Hello World!")

#  child's f says: Hello World!
#  parent's f says: Hello World!

まとめ

Tensorflowのように人に継承して使ってもらうライブラリを書くときにはなるべく「ここでhogehogeを実行してね!」っていうお約束を避けたいものです。そんな時はメタクラスを書いて相手の知らないところで勝手にhogehogeを呼び出してしまいましょう。

集団での開発ではメタプログラミングって読みにくくて嫌われちゃうらしいですけどね...。個人で開発する分には工数を最小にしてくれるエッセンスなのではないかと思っています。

Yosemat1
強化学習を研究する修士学生。強化学習が大好きです。強化学習以外はAIじゃない! 極めて高いレスポンス速度を誇ります。強化学習を一緒に勉強する仲間を求めているのでお気軽に、本当にお気軽に絡んでみてください! コメントやTwitterでぜひ!
https://twitter.com/home
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away