为什么常规操作不基于其就地对应操作?
在我看来,唯一的区别就是普通的操作需要多创建一个实例,而结果会保存在这个新实例里。因此,普通的实现应该调用另一个方法。
但是:这些(就地)方法应该尝试在原地进行操作(修改自身)并返回结果(这个结果可以是自身,也可以不是)。如果某个特定的方法没有定义,或者该方法返回的是 NotImplemented,那么增强赋值就会退回到普通的方法。
在这里,我明白标准的方法和我的理解正好相反:__iadd__
依赖于 __add__
,编辑 意思是 __add__
会在最后的情况下被调用。
那么为什么 __add__
是实现加法的默认方式,而不是 __iadd__
,尽管后者应该需要更少的处理?结束编辑
一点背景:这个问题是在实现一个多项式类时出现的,目的是为了学习。我写了:
class A:
...
def __iadd__(self, other):
"processing resulting in modification of attributes of self"
return self
def __add__(self, other):
res = self.copy() # A.copy() being implemented as well
res += other
return res
2 个回答
你可能误解了文档的意思,把操作符和方法搞混了。其实不是 __iadd__
调用 __add__
,而是 +=
这个操作符(或者说处理这个操作符的部分)在调用它。这个操作符首先会尝试调用 __iadd__
,如果这个方法不存在或者返回 NotImplemented
,那么它才会尝试调用 __add__
。这就是文档的意思。
而且让 __iadd__
去调用 __add__
也没有意义,因为那样会创建一个新的对象,而这并不是我们需要的。
当一个类型确实实现了 __iadd__
,那么它很可能也会实现 __add__
,而且很有可能 __add__
在做了一个副本之后会调用 __iadd__
,就像你所做的那样。或者这两个方法都不互相调用,而是都调用某个“更新”方法,比如 OrderedDict.__ior__ 和 OrderedDict.__or__
就是这样做的:
def __ior__(self, other):
self.update(other)
return self
def __or__(self, other):
if not isinstance(other, dict):
return NotImplemented
new = self.__class__(self)
new.update(other)
return new
就地操作通常会直接修改它们的操作对象。在你给的例子中,你使用了 A.copy()
来避免这种情况。
有些类型是不允许被修改的(比如元组就是不可变的)。所以它们不支持就地操作。
而且,对于刚接触编程的人,或者刚开始学习Python的人来说,就地操作并不是特别容易理解。这是语言中的一个不错的功能,但也仅此而已。
总体来说,常规操作是“正常”的操作方式。如果你在写一个新类并想要支持运算符,你可能首先会想到常规运算符,而不是就地运算符,或者根本不会想到。这样的话,如果只定义了常规操作,就地操作就无法使用。
不过,有时候性能确实是个问题,而就地操作可以让实现更高效。这就是为什么你可以定义就地操作,而不仅仅是调用常规操作。