为什么常规操作不基于其就地对应操作?

3 投票
2 回答
43 浏览
提问于 2025-04-12 15:07

在我看来,唯一的区别就是普通的操作需要多创建一个实例,而结果会保存在这个新实例里。因此,普通的实现应该调用另一个方法。

但是这些(就地)方法应该尝试在原地进行操作(修改自身)并返回结果(这个结果可以是自身,也可以不是)。如果某个特定的方法没有定义,或者该方法返回的是 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 个回答

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
1

就地操作通常会直接修改它们的操作对象。在你给的例子中,你使用了 A.copy() 来避免这种情况。

有些类型是不允许被修改的(比如元组就是不可变的)。所以它们不支持就地操作。

而且,对于刚接触编程的人,或者刚开始学习Python的人来说,就地操作并不是特别容易理解。这是语言中的一个不错的功能,但也仅此而已。

总体来说,常规操作是“正常”的操作方式。如果你在写一个新类并想要支持运算符,你可能首先会想到常规运算符,而不是就地运算符,或者根本不会想到。这样的话,如果只定义了常规操作,就地操作就无法使用。

不过,有时候性能确实是个问题,而就地操作可以让实现更高效。这就是为什么你可以定义就地操作,而不仅仅是调用常规操作。

撰写回答