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)

你选择什么取决于你对每个选项的感觉有多舒服(开发人员的时间很重要!),你需要多久进行一次查找(每个实例一次或两次,或者这些调度是否在每个实例中完成了很多?然后也许将绑定方法放入预先缓存映射的__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 回答