使用类字典将Python中的实例方法映射到类上

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转换为类成员,但仍然映射到实例方法?

2个回答

19
你有很多选项!
你可以在__init__方法中初始化地图:
def __init__(self):
    self.do_map = {"this": self.do_this, "that": self.do_that}

现在这些方法已经绑定到 self 上,因为它们是通过实例查找得到的。

或者,您可以使用字符串和 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__方法中绑定方法以提前缓存映射),以及它需要多么动态化(你经常子类化它吗?那么不要将映射隐藏在方法中,那并没有什么帮助)。


@MartijnPieters,你是如何在if item in do_map:中访问do_map的?难道不应该是self.do_map吗? - TheRealFakeNews
@TheRealFakeNews:是的,那是我8年前犯的一个愚蠢的错误。我已经更新了答案,以便更详细地讲解各种选项。 - Martijn Pieters

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)

另一方面,您可以获取未绑定的方法,然后将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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接