在Python中,a += 1比a = a + 1更快吗?

6

我怀疑在C语言中a+=1a=a+1更快。 - kennytm
它们完全做同样的事情,为什么会有区别呢? - Esailija
1
@Esailija:只有当a没有实现自己的__iadd__时,它们才会执行相同的操作。 - kennytm
1
@Esailija,你以前见过这个吗?https://dev59.com/F3RA5IYBdhLWcg3w1BqW - Hanfei Sun
4个回答

19

对于这两个语句,Python 的工作几乎没有任何区别:

>>> import dis
>>> def inplace_add():
...     a = 0
...     a += 1
... 
>>> def add_and_assign():
...     a = 0
...     a = a + 1
... 
>>> dis.dis(inplace_add)
  2           0 LOAD_CONST               1 (0)
              3 STORE_FAST               0 (a)

  3           6 LOAD_FAST                0 (a)
              9 LOAD_CONST               2 (1)
             12 INPLACE_ADD         
             13 STORE_FAST               0 (a)
             16 LOAD_CONST               0 (None)
             19 RETURN_VALUE        
>>> dis.dis(add_and_assign)
  2           0 LOAD_CONST               1 (0)
              3 STORE_FAST               0 (a)

  3           6 LOAD_FAST                0 (a)
              9 LOAD_CONST               2 (1)
             12 BINARY_ADD          
             13 STORE_FAST               0 (a)
             16 LOAD_CONST               0 (None)
             19 RETURN_VALUE        

区别在于INPLACE_ADDBINARY_ADD的使用。

最终的时间非常接近,无法确定哪个更快:

>>> import timeit
>>> timeit.timeit('inplace_add', 'from __main__ import inplace_add', number=10000000)
0.32667088508605957
>>> timeit.timeit('add_and_assign', 'from __main__ import add_and_assign', number=10000000)
0.34172606468200684

因此,在Python中,这种差异是微不足道的。不要担心它。


很好的答案(我不知道dis模块,所以谢谢你)。有一个考虑因素是执行速度可能会受到在给定时间运行的其他进程的影响。因此,我的方法是在一个函数中运行两次一百万次,并在cProfile中多次比较执行速度(见下文)。我同意差异微不足道。 - Aaron Newton
我不太明白为什么在 a 是一个不可变对象(整数)时,使用 a += 1 会调用原地操作。据我所知,整数在 Python 中是不可变对象。原地操作只对可变对象才有意义。请问有人可以解释一下吗?谢谢。 - Lion Lai
1
@LionLai:就地操作是一种“语句”,这意味着它是语言的一部分,并且适用于任何变量,无论其类型如何,只要它支持“常规”操作(此处为+)。整数保持不可变,但解释器需要执行的字节码步骤更少。这是因为整数支持+加法操作。 - Martijn Pieters

6

不行

>>> bar = timeit.Timer("a += 1", "a = 0")
>>> bar.timeit(number=1000000)
0.064391136169433594
>>> bar = timeit.Timer("a = a + 1", "a = 0")
>>> bar.timeit(number=1000000)
0.064393997192382812
>>>

4

没错,但差异微不足道。

>>> timeit.Timer('x += 1', 'x = 0').timeit(10**8)
5.7387330532073975
>>> timeit.Timer('x = x + 1', 'x = 0').timeit(10**8)
6.04801607131958
>>> timeit.Timer('x += 1', 'x = 0').timeit(10**8)
5.790481090545654
>>> timeit.Timer('x = x + 1', 'x = 0').timeit(10**8)
6.083467960357666

1
我采用了略微不同的方法,使用 cProfile 模块:
$ python -m cProfile test.py 
     4 function calls in 0.397 seconds

Ordered by: standard name

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
        1    0.000    0.000    0.397    0.397 test.py:2(<module>)
        1    0.205    0.205    0.205    0.205 test.py:2(add1)
        1    0.192    0.192    0.192    0.192 test.py:6(add2)
        1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}




aaron@zebrafish:~/pyad$ cat test.py 
def add1(a):
    for x in xrange(10 ** 6):
        a += 1

def add2(a):
    for x in xrange(10 ** 6):
        a = a + 1

add1(0)
add2(0)

经过大约20次运行,我得出结论,使用a = a + 1的add2速度略快,但并非在所有情况下都是如此(也许可以尝试更多循环)。这可能不是最好的启发式方法,但我认为更多次数和更大的数字重复应该能够显示性能差异。
编辑 - 10 ** 9调用的结果:
    1  216.119  216.119  216.119  216.119 test.py:2(add1)
    1  195.364  195.364  195.364  195.364 test.py:6(add2)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接