Why is it necessary to return anything from these methods if the documentation specifies that, if implemented, they should only be doing stuff in-place anyway? Why don’t the augmented assignment operators simply not perform the redundant assignment in the case where __iadd__ is implemented?
一个原因是强制他们是言论而不是表达.
更大的原因是作业并不总是多余的.在左侧只是一个变量的情况下,确定,在突变该对象之后,将该对象重新绑定到已经绑定到的名称上通常不是必需的.
但是左侧是更复杂的分配目标的情况呢?记住,you can assign—and augmented-assign—to subscriptions, slicings, and attribute references,像[1] = 2或a.b – = 2.在这种情况下,你实际上是在一个对象上调用__setitem__或__setattr__,而不仅仅是绑定一个变量.
此外,值得注意的是,“冗余分配”不是一项昂贵的操作.这不是C,其中任何分配可以最终在该值上调用自定义赋值运算符. (它可能最终在一个对象上调用一个定制的setter操作符,该值是一个元素,子元素或属性,并且可能是昂贵的…但是在这种情况下,这并不是多余的,如上所述)
最后一个原因直接关系到你的第二个问题:你几乎总是想从__ispam__返回自己,但总是不总是.如果__iadd__没有返回自我,这个任务显然是必要的.
Under what circumstances would it ever make sense to return something other than self from an augmented assignment method?
你已经在这里撇除了一个重要的相关位置:
These methods should attempt to do the operation in-place (modifying self)
任何情况下,他们不能就地执行操作,但可以做其他事情,返回除自我以外的东西可能是合理的.
想象一下,使用写时复制实现的对象,如果它是唯一的副本,就会在原位进行变异,否则换一个新的副本.你不能通过不执行__iadd__和letting =回到__add__来实现;你只能通过实现一个__iadd__来实现,这个__iadd__可以使得并返回一个副本,而不是突变和返回自己. (您可能会出于性能原因,但也可以想象,您将拥有一个具有两个不同接口的对象;“高级”界面看起来是不可变的,并且在写入时复制,而“低级”接口暴露实际共享.)
所以,需要的第一个原因是处理非就地案例.
但还有其他原因吗?当然.
一个原因只是为了包装其他语言或库,这是一个重要的功能.
例如,在Objective C中,很多方法返回一个self,通常但并不总是与接收方法调用相同的对象. “不总是”是ObjC如何处理类集群的事情.在Python中,有更好的方法来做同样的事情(甚至在运行时改变你的类通常会更好),但是在ObjC中,它是完全正常和习惯的. (它仅用于Apple当前框架中的init方法,但它是标准库的一个惯例,NSMutableFoo添加的mutator方法总是返回void,就像常规中,像python这样的mutator方法总是返回None,而不是Python的一部分语言.)所以,如果你想在Python中包装ObjC运行时,你会如何处理?
您可以在一切之前放置一个额外的代理层,因此您的包装对象可以更改其包装的ObjC对象.但这意味着很多复杂的委托代码(特别是如果您想使ObjC反射通过包装器进行Python备份)和内存管理代码以及性能命中.
相反,你可以只有一个通用的薄包装.如果您返回一个不同于您开始使用的ObjC对象,则返回包装器周围的东西,而不是围绕您开始的包装.简单的代码,内存管理是自动的,没有性能成本.只要你的包装器的用户总是做a = b而不是.__ iadd __(b),他们将看不到任何区别.
我意识到“在一个不同的ObjC框架库中编写一个PyObjC风格的包装器比苹果基金会不完全是每天的用例…但是你已经知道这是一个你每天都不使用的功能,所以还有什么你会期待吗?
一个懒惰的网络对象代理可能会做一些类似的事情 – 从一个小的昵称对象开始,交换一个完整的代理对象,第一次尝试做一些事情.你可以想到其他这样的例子.你可能永远不会写任何他们…但如果你不得不,你可以.