LoginSignup
4
6

More than 5 years have passed since last update.

Pythonのクラスのインスタンスメソッドを動的に追加する

Last updated at Posted at 2018-07-09

ある抽象クラスの継承先で、メンバ変数のリスト内の文字列に応じてインスタンスメソッドを作るという設計をしました。
新規追加メソッド作成のロジック自体は自明なので、文字列の数分いちいちメソッドを書き下すのは冗長だし時間がかかる。
そこで外からインスタンスメソッドを代入するという方法を思いついた。
その思考錯誤が結構面白かった。

失敗例



# ある変数のゲッターを作る
def make_getter(variable_name):
    def f(self):
        return getattr(self, variable_name)

    return f

class Parent(object):
    def __init__(self, variable_list):
        # ゲッターを代入する
        for variable in variable_list:
            setattr(self, 'get_'+variable, make_getter(variable))


class Child(Parent):

    def __init__(self):
        variable_list = ['hoge', 'fuga', 'piyo']
        super().__init__(variable_list)

まず上記の例だと、Childのインスタンスではchild.get_hoge()を呼ぶと素っ頓狂なエラーが返ってくる。
TypeError: f() missing 1 required positional argument: 'self'

なにをいってるんだ。Pythonのインスタンスメソッドの第一引数には暗黙的に自身が指定されるはずだろう。
と思うものの、素っ頓狂なことをしているのはPythonのインタプリタではなくてやっぱり自分でした。

インスタンスメソッドとして認識されるのは、インスタンスの持つ関数ではなくて、その型であるクラスに登録された関数のみ。
イニシャライズの途中にselfに関数を代入したからといって、インスタンスメソッドにならないということですね。いわゆるstaticmethodを追加するならこのやり方でよいということになります。

成功例

このため、下記ケースではchild.get_hoge()を想定通り実行することができる。



# ある変数のゲッターを作る
def make_getter(variable_name):
    def f(self):
        return getattr(self, variable_name)

    return f

class Parent(object):
    def __init__(self, variable_list):
        # ゲッターを代入する
        for variable in variable_list:
            setattr(self.__class__, 'get_'+variable, make_getter(variable))


class Child(Parent):

    def __init__(self):
        variable_list = ['hoge', 'fuga', 'piyo']
        super().__init__(variable_list)

インスタンス化最中のselfオブジェクトの型クラスにsetattrして改造するということをしています。
まあ邪道実装極まりないのですが、また一つPythonに詳しくなれたのでよかったです。

4
6
6

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
6