我刚刚进行了一个有趣的测试:
~$ 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不优化这个呢?
The __add__
方法可以自由返回不同类型的对象,而__iadd__
如果使用就地语义,应该返回self
。他们不需要在这里返回相同类型的对象,所以sum()
不应依赖于特殊语义__iadd__
.
您可以使用functools.reduce()功能 https://docs.python.org/3/library/functools.html#functools.reduce自己实现您想要的功能:
from functools import reduce
sum_with_inplace_semantics = reduce(Foo.__iadd__, a, Foo())
Demo:
>>> from functools import reduce
>>> class Foo(object):
... def __add__(self, other):
... global add_calls
... add_calls += 1
... return Foo()
... def __iadd__(self, other):
... global iadd_calls
... iadd_calls += 1
... return self
...
>>> a = [Foo() for _ in range(6)]
>>> result = Foo()
>>> add_calls = iadd_calls = 0
>>> reduce(Foo.__iadd__, a, result) is result
True
>>> add_calls, iadd_calls
(0, 6)
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)