【发布时间】:2021-02-04 15:15:28
【问题描述】:
例如,假设我们有一个实现矩阵的 Matrix 类。
我们希望能够使用“+”运算符来添加 2 个矩阵,或者将 1 个矩阵与 1 个数字相加(这实际上将被定义为将数字添加到矩阵的每个元素中)。
在 C++ 中,我们可以做到(用 '...' 跳过细节):
class Matrix
{
...
Matrix operator + (Matrix A, Matrix B)
{
... // process in some way
}
Matrix operator + (Matrix A, double x)
{
... // process in some other way
}
}
但据我所知,在 Python 中,多个定义会覆盖之前的定义。
class Matrix:
...
def __add__(A, B):
... # process in some way
def __add__(A, x):
... # process in some other way
这不起作用:只有最后一个方法定义处于活动状态。
所以我能想到的第一个解决方案是只定义一个方法并解析参数(根据它们的类型、数量或关键字)。
例如,通过类型检查,我们可以做类似的事情(我们称之为技术#1):
class Matrix:
...
def __add__(A, X):
if isinstance(X, Matrix)
... # process in some way
elif isinstance(X, (int, float, complex))
... # process in some other way
else:
raise TypeError("unsupported type for right operand")
但我经常读到 type-checking 不是“pythonic”,还有什么?
此外,在这种情况下,总是有两个参数,但更一般地说,如果我们希望能够处理不同数量的参数?
为了清楚起见,假设我们有一个名为“mymethod”的方法,我们希望能够调用,比如:
mymethod(type1_arg1)
mymethod(type2_arg1)
mymethod(type3_arg1, type4_arg2)
我们也假设:
- 每个版本都处理同一个对象,所以它必须是一个唯一的方法,
- 每个处理都不同,参数类型很重要。
----------------------------------- - - - - - - 编辑 - - - - - - - - - - - - - - - - - - - --------------------
感谢大家对此话题的关注。
@thegreatemu 在下面提出了一个很好的类型检查替代方案(我们称之为技术#2)。它使用 duck-typing 范例和 exception 处理。但据我所知,它仅在所有情况下的参数数量相同时才有效。
根据我从此处和链接主题的答案中了解到的情况,当我们想要处理不同数量的参数时,我们可以使用关键字参数。这是一个例子(我们称之为技术#3)。
class Duration:
"""Durations in H:M:S format."""
def __init__(hours, mins=None, secs=None):
"""Works with decimal hours or H:M:S format."""
if mins and secs: # direct H:M:S
... # checks
self.hours = hours
self.mins = mins
self.secs = secs
else: # decimal
... # checks
... # convert decimal hours to H:M:S
或者我们也可以使用可变长度参数 *args(或**kwargs)。这是带有 *args 和 长度检查 的替代版本(我们称之为技术 #4)。
class Duration:
"""Durations in H:M:S format."""
def __init__(*args): # direct H:M:S
"""Works with decimal hours or H:M:S format."""
if len(args) == 3 # direct H:M:S format
... # checks
self.hours = args[0]
self.mins = args[1]
self.secs = args[2]
elif len(args) == 1: # decimal
... # checks
... # convert decimal hours to H:M:S
else:
raise TypeError("expected 1 or 3 arguments")
在这种情况下,技术#3 和#4 之间的最佳选择是什么?可能是口味问题。
但正如我在原帖中含糊提到的,也可以使用装饰器。特别是,链接的主题提到了重载模块,它提供了一个类似于 C++ 重载的 API,带有 @overload 装饰器。见PEP3124。
它看起来确实很方便(这就是技巧 #5)!
【问题讨论】:
-
您使用默认参数执行最后一部分。
def mymethod(self, arg1, arg2=defaultValue): -
我使用
isinstance并且实际上更喜欢它,因为它明确表明根据传入的对象类型有不同的功能。关于你的第二个问题,使用默认值或传入*args或**kwargs。 -
“使用装饰器”作为解决方案的描述相当模糊。它基本上(几乎完全)等同于“使用功能”。无论如何,不,Python 没有方法重载。
-
@juanpa.arrivillaga 确实,我在阅读教程时遇到了不同的技术,但我添加了似乎对我的问题最友好的技术。
标签: python python-3.x operator-overloading overloading