gpt4 book ai didi

python - 为什么从`__iadd__`返回除“self”之外的任何东西?

转载 作者:太空狗 更新时间:2023-10-29 17:08:30 25 4
gpt4 key购买 nike

python的documentation on the methods related to the in-place operators类似于+=*=(或者,正如它所说的,增加的算术赋值)有以下说法:
这些方法应该尝试就地执行操作(修改self)并返回结果(可能是self,但不一定是self)。如果未定义特定的方法,则增强的赋值将返回到普通方法。
我有两个密切相关的问题:
如果文档指定,如果实现了这些方法,那么它们应该只在适当的地方进行工作,那么为什么需要从这些方法返回任何东西呢?为什么在实现了__iadd__的情况下,增广的赋值操作符不简单地执行冗余赋值?
在什么情况下,从增广的赋值方法返回self之外的内容是有意义的?
一些实验表明,python的不可变类型不实现__iadd__(与引用的文档一致):

>>> x = 5
>>> x.__iadd__
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'int' object has no attribute '__iadd__'

当然,其可变类型的 __iadd__方法是就地操作并返回 self
>>> list1 = []
>>> list2 = list1
>>> list1 += [1,2,3]
>>> list1 is list2
True

因此,我不知道从 self返回 __iadd__之外的内容的功能是什么。在任何情况下都这样做似乎是错误的。

最佳答案

如果文档指定,如果实现了这些方法,那么它们应该只在适当的地方进行工作,那么为什么需要从这些方法返回任何东西呢?为什么在实现了__iadd__的情况下,增广的赋值操作符不简单地执行冗余赋值?
一个原因是强迫它们成为语句而不是表达式。
一个更大的原因是任务并不总是多余的。在左侧只是一个变量的情况下,当然,在改变对象之后,通常不需要将该对象重新绑定到它已经绑定到的名称。
但是,如果左手边是一个更复杂的任务目标,那该怎么办呢?记住you can assign—and augmented-assign—to subscriptions, slicings, and attribute references,如a[1] += 2a.b -= 2。在这种情况下,您实际上是在对象上调用__setitem____setattr__,而不仅仅是绑定一个变量。
另外,值得注意的是,“冗余分配”并不是一个昂贵的操作。这不是C++,任何赋值都可以在值上调用自定义赋值操作符。(最后可能会在值是元素、子片或属性的对象上调用自定义setter运算符,这可能很昂贵……但在这种情况下,它不是多余的,如上所述。)
最后一个原因直接关系到第二个问题:你几乎总是想从self返回__ispam__,但几乎总是不总是。如果__iadd__ever没有返回self,那么分配显然是必要的。
在什么情况下,从增广的赋值方法返回除self以外的东西是有意义的?
你在这里略读了一个重要的相关内容:
这些方法应尝试就地执行操作(修改自身)
在任何情况下,如果他们不能在适当的位置执行操作,但可以执行其他操作,则可能会合理地返回self以外的操作。
假设一个对象使用了一个copy-on-write实现,如果它是唯一的副本,那么它会在适当的位置发生变化,但是会生成一个新的副本。不能通过不实现__iadd__并让+=返回到__add__来实现;只能通过实现可能生成并返回副本而不是突变并返回__iadd__来实现。(出于性能原因,您可能会这样做,但也可以想象,您拥有一个具有两个不同接口的对象;“高级别”接口看起来是不可变的,“写时复制”,“低级别”接口公开实际共享。)
所以,它需要的第一个原因是处理非就地案例。
但还有其他原因吗?当然.
其中一个原因是包装其他语言或库,而这是一个重要的特性。
例如,在目标C中,许多方法返回一个self通常是但不总是收到方法调用的同一个对象。objc处理类集群之类的事情的方式“并不总是”。在Python中,有更好的方法来做同样的事情(即使在运行时更改类通常也更好),但是在objc中,这是完全正常和惯用的。(它只用于苹果当前框架中的self方法,但它是标准库的一个约定,由init添加的mutator方法始终返回NSMutableFoo,就像在python中mutator方法(如void始终返回list.sort)的约定,而不是语言的一部分。)所以,如果你想用python总结objc运行时,您将如何处理它?
您可以在所有内容前面放置一个额外的代理层,这样您的包装器对象就可以更改它包装的对象objc。但这意味着大量复杂的委托代码(特别是如果您想让objc反射通过包装器回到python中工作的话)和内存管理代码,并且性能会受到影响。
相反,您可以只拥有一个通用的瘦包装器。如果返回一个与开始时不同的objc对象,则返回围绕该对象的包装器,而不是围绕您开始时的对象的包装器。琐碎的代码,内存管理是自动的,没有性能成本。只要包装器的用户总是执行None而不是a += b操作,他们就不会看到任何差异。
我意识到“在一个不同的Objc框架库中编写一个PyObjc风格的包装,而不是苹果的基础”并不是一个日常的用例,但是你已经知道这是一个你每天都不用的特性,那么你还能期待什么呢?
一个懒惰的网络对象代理可能会做一些类似的事情,从一个很小的名字对象开始,当你第一次尝试对它做一些事情时,把它换成一个完整的代理对象。你可能会想到其他这样的例子。你可能永远也不会写它们……但是如果你不得不写,你可以。

关于python - 为什么从`__iadd__`返回除“self”之外的任何东西?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20849870/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com