12

次のような長い if-elif チェーンがあります。

class MyClass:
    def my_func(self, item, value):
        if item == "this":
            self.do_this(value)
        elif item == "that":
            self.do_that(value)
        # and so on

私はそれを読むのが難しいと思うので、辞書を使うのが好きです:

class MyClass:
    def my_func(self, item, value):
        do_map = {
            "this" : self.do_this,
            "that" : self.do_that,
            # and so on
        }

        if item in do_map:
            do_map[item](value)

関数が呼び出されるたびにマップを再作成するのはばかげています。すべてのインスタンスに対してディクショナリが一度だけ作成されるように、このクラスをリファクタリングするにはどうすればよいですか? どうにかdo_mapしてクラス メンバーになり、それでもインスタンス メソッドにマップできますか?

4

2 に答える 2

16

たくさんのオプションがあります!

__init__メソッドでマップを初期化できます。

def __init__(self):
    self.do_map = {"this": self.do_this, "that": self.do_that}

selfインスタンスでルックアップされたおかげで、メソッドは にバインドされます。

または、string-and-getattr アプローチを使用することもできます。これにより、メソッドが確実にバインドされます。

class Foo(object):
    do_map = {"this": "do_this", "that": "do_that"}

    def my_func(self, item, value):
        if item in self.do_map:
            getattr(self, self.do_map[item])(value)

または、__get__記述子プロトコル メソッドを使用して、クラス レベルの辞書内の関数を手動でインスタンスにバインドできます。

class Foo(object):
    def do_this(self, value):
        ...

    def do_that(self, value):
        ...

    # at class creation time, the above functions are 'local' names
    # so can be assigned to a dictionary, but remain unbound
    do_map = {"this": do_this, "that": do_that}

    def my_func(self, item, value):
        if item in self.do_map:
            # __get__ binds a function into a method
            method = self.do_map[item].__get__(self, type(self))
            method(value)

これはself.method_name、ボンネットの下で行うことです。method_nameクラス階層で属性を検索し、それをメソッド オブジェクトにバインドします。

または、self手動で渡すこともできます。

class Foo(object):
    def do_this(self, value):
        ...

    def do_that(self, value):
        ...

    # at class creation time, the above functions are 'local' names
    # so can be assigned to a dictionary, but remain unbound
    do_map = {"this": do_this, "that": do_that}

    def my_func(self, item, value):
        if item in self.do_map:
            # unbound functions still accept self manually
            self.do_map[item](self, value)

何を選択するかは、各オプション (開発者の時間は重要です!) にどれだけ慣れているか、ルックアップを実行する必要がある頻度 (インスタンスごとに 1 回か 2 回、またはインスタンスごとにこれらのディスパッチが頻繁に行われるか?) によって異なります。事前にマッピングをキャッシュする__init__メソッド)、およびこれがどの程度動的である必要があるかについて (これをたくさんサブクラス化しますか? 次に、メソッドでマッピングを非表示にしないでください。それは役に立ちません)。

于 2012-10-16T19:14:21.120 に答える
1

少しキャッシングした私の(テストされていない)テイク:

class Something(object):
    def __init__(self):
        self.__do_map = {}

    def my_func(self, item, value):
        self.__do_map.setdefault(item, getattr(self, 'do_{}'.format(item)))(value)

OTOH、バインドされていないメソッドを取得してから、最初のインスタンスとして明示的に self を渡すことができます...

class Something(object):
    _do_methods = {}
    def __init__(self:
        pass

    def my_func(self, item, value):
        ubf = Something._do_methods.setdefault(item, getattr(Something, 'do_{}'.format(item)))
        ubf(self, value)

    def do_test(self, value):
        print 'test is', value
于 2012-10-16T19:35:37.680 に答える