Python:为啥在函数中使用重载而不是 *args(尤其是当参数的类型不影响函数的运行方式时)
Posted
技术标签:
【中文标题】Python:为啥在函数中使用重载而不是 *args(尤其是当参数的类型不影响函数的运行方式时)【英文标题】:Python: why use overloading instead of *args in a function (especially when the type of arguments do not affect how the function runs)Python:为什么在函数中使用重载而不是 *args(尤其是当参数的类型不影响函数的运行方式时) 【发布时间】:2020-11-22 06:15:06 【问题描述】:编辑:
我正在查看内置 zip()
函数的类型注释。
我了解重载(在类型检查的上下文中)可以根据给定参数的类型修改函数的行为。
@overload
def zip(__iter1: Iterable[_T1]) -> List[Tuple[_T1]]: ...
@overload
def zip(__iter1: Iterable[_T1],
__iter2: Iterable[_T2]) -> List[Tuple[_T1, _T2]]: ...
@overload
def zip(__iter1: Iterable[_T1], __iter2: Iterable[_T2],
__iter3: Iterable[_T3]) -> List[Tuple[_T1, _T2, _T3]]: ...
@overload
def zip(__iter1: Iterable[_T1], __iter2: Iterable[_T2], __iter3: Iterable[_T3],
__iter4: Iterable[_T4]) -> List[Tuple[_T1, _T2, _T3, _T4]]: ...
@overload
def zip(__iter1: Iterable[_T1], __iter2: Iterable[_T2], __iter3: Iterable[_T3],
__iter4: Iterable[_T4], __iter5: Iterable[_T5]) -> List[Tuple[_T1, _T2, _T3, _T4, _T5]]: ...
@overload
def zip(__iter1: Iterable[Any], __iter2: Iterable[Any], __iter3: Iterable[Any],
__iter4: Iterable[Any], __iter5: Iterable[Any], __iter6: Iterable[Any],
*iterables: Iterable[Any]) -> List[Tuple[Any, ...]]: ...
但是,在此示例中,类型是相同的 (Any)。那么重载有什么作用呢?
为什么不直接使用最后一个函数,它接受任意数量的参数。为什么要创建前五个,它本质上是说:如果有一个论点就这样做,如果有两个就这样做,如果有 3 个...... 这似乎违反了 DRY 原则。
@overload
def zip(__iter1: Iterable[Any], __iter2: Iterable[Any], __iter3: Iterable[Any],
__iter4: Iterable[Any], __iter5: Iterable[Any], __iter6: Iterable[Any],
*iterables: Iterable[Any]) -> List[Tuple[Any, ...]]: ...
【问题讨论】:
【参考方案1】:overload
装饰器用于静态类型分析,而不是用于实现。事实上,问题中显示的代码只是类型注释 - zip
是内置的,它没有在 Python 中实现。
各种重载的目的是保留参数的数量和类型。例如,它声明zip
超过三个可迭代对象会产生具有与可迭代元素类型匹配的三个元素的元组。带有可变参数 *args
和 Any
的最终重载仅适用于未指定的情况。
【讨论】:
【参考方案2】:这是一种优化。可以处理任意数量参数的版本需要使用额外的循环级别。固定数量参数的版本可以硬编码访问每个参数,效率更高。
zip()
的绝大多数用法只有 2-3 个参数,因此即使这种优化可能很小,但加起来非常有益。
【讨论】:
以上是关于Python:为啥在函数中使用重载而不是 *args(尤其是当参数的类型不影响函数的运行方式时)的主要内容,如果未能解决你的问题,请参考以下文章
为啥编译器更喜欢 char 的 int 重载而不是 varargs char 重载?