0

Reviewing some of my code and I realized I had written what is essentially:'

if (any(predicate for predicate in list_of_predicates)):
    # do something

I had expected this syntax error since it was missing '()' or '[]'. So I tried it in ipython:

Without bracketing:

In [33]: timeit.repeat('any(True for x in xrange(10))', repeat=10)
Out[33]: 
[0.502741813659668,
 0.49950194358825684,
 0.6626348495483398,
 0.5485308170318604,
 0.5268769264221191,
 0.6033108234405518,
 0.4647831916809082,
 0.45836901664733887,
 0.46139097213745117,
 0.4977281093597412]

Generator comprehension:

In [34]: timeit.repeat('any((True for x in xrange(10)))', repeat=10)
Out[34]: 
[0.7183680534362793,
 0.6293261051177979,
 0.5045809745788574,
 0.4723200798034668,
 0.4649538993835449,
 0.5164840221405029,
 0.5919051170349121,
 0.5790350437164307,
 0.594775915145874,
 0.5718569755554199]

Ramping up:

In [52]: reg = timeit.repeat('any(True for x in xrange(10))', repeat=100)

In [53]: comp = timeit.repeat('any((True for x in xrange(10)))', repeat=100)

In [55]: avg(reg)
Out[55]: 0.5245428466796875

In [56]: avg(comp)
Out[56]: 0.5283565306663514

In [57]: stddev(reg)
Out[57]: 0.05609485659272963

In [58]: stddev(comp)
Out[58]: 0.058506353663056954

In [59]: reg[50]
Out[59]: 0.46748805046081543

In [60]: comp[50]
Out[60]: 0.5147180557250977

There seems to be a marginal (possibly noise) performance advantage to not having the parentheses - ramping up the test it appears more like noise. Is there a fundamental difference between how these are processed?

kporter
  • 2,684
  • 17
  • 26

1 Answers1

2

These expressions are equivalent. The performance difference is noise.

From the original genexp PEP:

if a function call has a single positional argument, it can be a generator expression without extra parentheses, but in all other cases you have to parenthesize it.

And viewing the disassembly, you can see that they compile to the same bytecode:

>>> def f():
...     any(True for x in xrange(10))
...
>>> def g():
...     any((True for x in xrange(10)))
...
>>> dis.dis(f)
  2           0 LOAD_GLOBAL              0 (any)
              3 LOAD_CONST               1 (<code object <genexpr> at 0000000002
B46A30, file "<stdin>", line 2>)
              6 MAKE_FUNCTION            0
              9 LOAD_GLOBAL              1 (xrange)
             12 LOAD_CONST               2 (10)
             15 CALL_FUNCTION            1
             18 GET_ITER
             19 CALL_FUNCTION            1
             22 CALL_FUNCTION            1
             25 POP_TOP
             26 LOAD_CONST               0 (None)
             29 RETURN_VALUE
>>> dis.dis(g)
  2           0 LOAD_GLOBAL              0 (any)
              3 LOAD_CONST               1 (<code object <genexpr> at 0000000002
BE0DB0, file "<stdin>", line 2>)
              6 MAKE_FUNCTION            0
              9 LOAD_GLOBAL              1 (xrange)
             12 LOAD_CONST               2 (10)
             15 CALL_FUNCTION            1
             18 GET_ITER
             19 CALL_FUNCTION            1
             22 CALL_FUNCTION            1
             25 POP_TOP
             26 LOAD_CONST               0 (None)
             29 RETURN_VALUE
>>> f.__code__.co_consts
(None, <code object <genexpr> at 0000000002B46A30, file "<stdin>", line 2>, 10)
>>> dis.dis(f.__code__.co_consts[1])  # the genexp's code object in f
  2           0 LOAD_FAST                0 (.0)
        >>    3 FOR_ITER                11 (to 17)
              6 STORE_FAST               1 (x)
              9 LOAD_GLOBAL              0 (True)
             12 YIELD_VALUE
             13 POP_TOP
             14 JUMP_ABSOLUTE            3
        >>   17 LOAD_CONST               0 (None)
             20 RETURN_VALUE
>>> dis.dis(g.__code__.co_consts[1])  # the genexp's code object in g
  2           0 LOAD_FAST                0 (.0)
        >>    3 FOR_ITER                11 (to 17)
              6 STORE_FAST               1 (x)
              9 LOAD_GLOBAL              0 (True)
             12 YIELD_VALUE
             13 POP_TOP
             14 JUMP_ABSOLUTE            3
        >>   17 LOAD_CONST               0 (None)
             20 RETURN_VALUE
user2357112
  • 260,549
  • 28
  • 431
  • 505