我刚做了一个有趣的测试:
~$ python3 # I also conducted this on python 2.7.6, with the same result
Python 3.4.0 (default, Apr 11 2014, 13:05:11)
[GCC 4.8.2] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> class Foo(object):
... def __add__(self, other):
... global add_calls
... add_calls += 1
... return Foo()
... def __iadd__(self, other):
... return self
...
>>> add_calls = 0
>>> a = list(map(lambda x:Foo(), range(6)))
>>> a[0] + a[1] + a[2]
<__main__.Foo object at 0x7fb588e6c400>
>>> add_calls
2
>>> add_calls = 0
>>> sum(a, Foo())
<__main__.Foo object at 0x7fb588e6c4a8>
>>> add_calls
6
显然,__iadd__
方法比__add__
方法更有效,不需要分配新类。如果我添加的对象足够复杂,这会产生不必要的新对象,可能会在我的代码中造成巨大的瓶颈。
我希望在a[0] + a[1] + a[2]
中,第一个操作会调用__add__
,第二个操作会在新创建的对象上调用__iadd__
。
为什么没有python优化这个?
答案 0 :(得分:4)
>>> from PIL import Image
>>> import glob, os
>>> size = 128, 128
>>> pic = glob.glob("cherngloong1.jpg")
>>> im = Image.open(pic[0])
>>> im
<PIL.JpegImagePlugin.JpegImageFile image mode=RGB size=2048x1365 at 0x100A63BD8>
>>> im.thumbnail(size, Image.ANTIALIAS)
>>> im.save("cherngloong_thumbnail", "PNG")
>>> im.save("cherngloong_thumbnail1", "JPEG")
方法可以自由地返回不同类型的对象,而__add__
如果使用就地语义则返回__iadd__
。它们不需要在此返回相同类型的对象,因此self
不应该依赖sum()
的特殊语义。
您可以使用functools.reduce()
function自行实现所需的功能:
__iadd__
演示:
from functools import reduce
sum_with_inplace_semantics = reduce(Foo.__iadd__, a, Foo())
答案 1 :(得分:0)
Martjin's answer提供了一个很好的解决方法,但我觉得有必要总结一下评论中散布的各种答案:
sum
函数主要用于不可变类型。执行除第一个就地之外的所有添加将对具有__iadd__
方法的对象创建性能改进,但检查__iadd__
方法会导致更典型情况下的性能损失。 Special cases aren't special enough to break the rules
我还声明__add__
可能只应在a + b + c
中调用一次,其中a + b
创建一个临时变量,然后在返回之前调用tmp.__iadd__(c)
。但是,这会违反最不意外的原则。
答案 2 :(得分:0)
既然你正在写你的班级,你知道它__add__
也可以返回同一个对象,不是吗?
因此,您可以使用+
运算符和内置sum
运行currying优化代码:
>>> class Foo(object):
... def __add__(self, other):
... global add_calls
... add_calls += 1
... return self
(请注意将代码传递给期望“+”成为新对象的第三方函数)