据我所知,有两种类型的重载,一种基于参数的数量,一种基于参数类型
虽然已经涵盖了基于参数数量的重载here,但我似乎无法通过参数类型找到关于函数重载的指南。
因此,由于type()
的类型检查似乎一般不受欢迎,我该如何以pythonic方式执行此操作?
这似乎不像我期望的那么优雅......
def overloaded_func(arg):
try:
do_list_action(arg)
except:
try:
do_dict_action(arg)
except:
pass
答案 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
字典作为原始函数本身的属性。
一般来说,不可能说是否使用鸭子打字,类型检查,调度或其他东西,因为它有些主观,取决于你的情况细节:代码有多么不同你需要处理不同的类型?你要处理多少种类型?您需要能够轻松处理新类型吗?等等。你还没有在问题中提供足够的信息,以允许其他人告诉你哪种方式最好,但它们都有其用途。