什么是python相当于类型重载?

时间:2017-01-17 05:53:59

标签: python overloading

据我所知,有两种类型的重载,一种基于参数的数量,一种基于参数类型

虽然已经涵盖了基于参数数量的重载here,但我似乎无法通过参数类型找到关于函数重载的指南。

因此,由于type()的类型检查似乎一般不受欢迎,我该如何以pythonic方式执行此操作?

这似乎不像我期望的那么优雅......

def overloaded_func(arg):
    try:
        do_list_action(arg)
    except:
        try:
            do_dict_action(arg)
        except:
            pass

3 个答案:

答案 0 :(得分:4)

您可以使用functools.singledispatch。它将根据第一个参数的类型进行调度,如果类型与任何已注册的函数不匹配,则使用默认实现:

from functools import singledispatch

@singledispatch
def overloaded_func(arg):
    print('Default impl', arg)

@overloaded_func.register(list)
def do_list_action(lst):
    print('List action', lst)

@overloaded_func.register(dict)
def do_list_action(dct):
    print('Dict action', dct)

overloaded_func(['foobar'])
overloaded_func({'foo':'bar'})
overloaded_func('foobar')

输出:

List action ['foobar']
Dict action {'foo': 'bar'}
Default impl foobar

答案 1 :(得分:1)

使用index

isinstance

或者,您可以考虑检查其他方式,例如是否存在def overloaded_func(arg): if isinstance(arg, list): do_list_action(arg) elif isinstance(arg, dict): do_dict_action(arg) else: do_default_action(arg) __getitem__等。这取决于您重置的原因的详细信息。与我们分享。

答案 2 :(得分:1)

使用type()进行类型检查(或者,更好的是,isinstance())在Python中一般并不赞同。当你只能检查行为时,使用类型作为行为的代理,不赞成的是。换句话说,当您需要一个对象具有某些特定功能时,而在某些其他语言中,您必须显式检查其类型是否支持该功能,在Python中您只需假设该对象执行您需要它执行的操作,并且信任如果情况并非如此,则会引发例外情况。但是,如果您在不同类型的功能之间进行选择,其中任何功能都可以为您完成工作,那就是另一个故事。

例如,假设您有一些代码可以使用列表或dicts来实现整数索引数组。

class Array:
    def __init__(self, keys_and_values):
        self.store = ... # either a list or a dict

如果只有少数非常大的索引分配了值,则可能使用dict,否则使用列表。如果要访问索引处的元素(如果有),则只需编写self.store[index]

    def __getitem__(self, index):
        return self.store[index]

您不必先查看它是列表还是字典,因为您想要的行为 - 能够被整数索引 - 以任何一种方式存在。

但是如果你想在索引处设置元素,如果它是一个列表,你需要先将它扩展到适当的长度。现在,正确的鸭子打字可能会建议你这样做:

    def __setitem__(self, index, value):
        if index >= len(self.store):
            try:
                self.store.extend([None] * (index - len(self.store) + 1))
            except AttributeError:
                pass
        self.store[index] = value

但我认为大多数Python程序员会说isinstance()在这种情况下更好。 (不,真的。没关系。)

    def __setitem__(self, index, value):
        if isinstance(self.store, list) and index >= len(self.store):
            self.store.extend([None] * (index - len(self.store) + 1))
        self.store[index] = value

当你只有几种类型需要测试时,我通常会推荐这条路线。

如果您要测试更多类型,使用调度程序模式更为实际,这是一种功能性方法。您可以构建类型到处理该类型的函数的映射,并根据所获得的对象的类型选择要调用的类型。在这个例子中,这将是这样的:

    def __setitem__dict(self, index, value):
        self.store[index] = value
    def __setitem__list(self, index, value):
        if index >= len(self.store):
            self.store.extend([None] * (index - len(self.store) + 1))
        self.store[index] = value
    __setitem__dispatch = {list: __setitem__list, dict: __setitem__dict}
    def __setitem__(self, index, value):
        self.__setitem__dispatch[type(self.store)](index, value)

在这个简单的例子中做到这一点非常愚蠢,但在更复杂的场景中它可以派上用场。一般的模式是

dispatch = {list: handle_list, dict: handle_dict, ...}
def function(arg):
    return dispatch[type(arg)](arg)

它甚至允许您稍后为新类型动态添加处理程序。这基本上是functools.singledispatch所做的(如another answer提到的那样)。这种方式看起来很复杂,因为它隐藏了dispatch字典作为原始函数本身的属性。

一般来说,不可能说是否使用鸭子打字,类型检查,调度或其他东西,因为它有些主观,取决于你的情况细节:代码有多么不同你需要处理不同的类型?你要处理多少种类型?您需要能够轻松处理新类型吗?等等。你还没有在问题中提供足够的信息,以允许其他人告诉你哪种方式最好,但它们都有其用途。