LoginSignup
4
5

More than 3 years have passed since last update.

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

Last updated at Posted at 2020-09-15

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

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を呼び出してしまいましょう。

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

4
5
0

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
4
5