类型重载的 python 等价物是什么?
What would be the python equivalent of type-wise overloading?
据我所知,有两种类型的重载,一种基于参数的数量,一种基于参数的类型
虽然已经介绍了基于参数数量的重载 here,但我似乎找不到关于按参数类型重载函数的指南。
因此,由于使用 type()
进行类型检查似乎通常不受欢迎,我该如何以 Pythonic 方式执行此操作?
这似乎没有我预期的那么优雅...
def overloaded_func(arg):
try:
do_list_action(arg)
except:
try:
do_dict_action(arg)
except:
pass
使用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__
或 __iter__
等。这取决于您为何重载的详细信息,您尚未分享和我们。
您可以使用 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
使用 type()
(或者更好的是 isinstance()
)进行类型检查 通常 在 Python 中不被反对。令人不悦的是 使用类型作为行为的代理 当您仅检查行为就可以逃脱时。换句话说,当您需要一个对象具有某些特定功能时,而在某些其他语言中您必须显式检查其类型是否支持该功能,在 Python 中您只是假设该对象执行您需要它的操作做,并相信如果不是这种情况会引发异常。但是,如果您在不同类型的功能之间进行选择,其中任何一种都可以为您完成工作,那就是另一回事了。
例如,假设您有一些代码可以使用列表或字典来实现 integer-indexed 数组。
class Array:
def __init__(self, keys_and_values):
self.store = ... # either a list or a 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
所做的(如 所述)。这种方式看起来很复杂,因为它将 dispatch
字典隐藏为原始函数本身的属性。
不可能笼统地说是使用 duck typing、类型检查、调度还是其他方法,因为它有些主观并且取决于您的具体情况:处理不同类型所需的代码有何不同?您要处理多少种类型?您需要能够轻松处理新类型吗?等等。您没有在问题中提供足够的信息,无法让其他人告诉您哪种方式最好,但它们都有自己的用途。
据我所知,有两种类型的重载,一种基于参数的数量,一种基于参数的类型
虽然已经介绍了基于参数数量的重载 here,但我似乎找不到关于按参数类型重载函数的指南。
因此,由于使用 type()
进行类型检查似乎通常不受欢迎,我该如何以 Pythonic 方式执行此操作?
这似乎没有我预期的那么优雅...
def overloaded_func(arg):
try:
do_list_action(arg)
except:
try:
do_dict_action(arg)
except:
pass
使用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__
或 __iter__
等。这取决于您为何重载的详细信息,您尚未分享和我们。
您可以使用 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
使用 type()
(或者更好的是 isinstance()
)进行类型检查 通常 在 Python 中不被反对。令人不悦的是 使用类型作为行为的代理 当您仅检查行为就可以逃脱时。换句话说,当您需要一个对象具有某些特定功能时,而在某些其他语言中您必须显式检查其类型是否支持该功能,在 Python 中您只是假设该对象执行您需要它的操作做,并相信如果不是这种情况会引发异常。但是,如果您在不同类型的功能之间进行选择,其中任何一种都可以为您完成工作,那就是另一回事了。
例如,假设您有一些代码可以使用列表或字典来实现 integer-indexed 数组。
class Array:
def __init__(self, keys_and_values):
self.store = ... # either a list or a 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
所做的(如 dispatch
字典隐藏为原始函数本身的属性。
不可能笼统地说是使用 duck typing、类型检查、调度还是其他方法,因为它有些主观并且取决于您的具体情况:处理不同类型所需的代码有何不同?您要处理多少种类型?您需要能够轻松处理新类型吗?等等。您没有在问题中提供足够的信息,无法让其他人告诉您哪种方式最好,但它们都有自己的用途。