-3

What do you prefer concerning performance (memory consumption, speed) / readability, if you need to switch variable value between 0 and 1 in a cycle:

x=get(x) 
for i in range (start, end):
    if x==0:
        x=1
    else:
        x=0

or

x=get(x)
for i in range (start, end):
    x = ((x + 1) % 2)

How the python compiler interprets it?

Petr Krampl
  • 816
  • 9
  • 11

2 Answers2

4

There are a multitude of options beyond the if statement and expression you used.

  • Use subtraction:

    x = 1 - x
    
  • Use ^ (XOR):

    for i in range (start, end):
        x ^= 1
    

    This toggles between 0 and 1:

    >>> x = 1
    >>> x ^ 1
    0
    >>> x ^= 1
    >>> x ^ 1
    1
    
  • You could use itertools.cycle():

    from itertools import cycle
    
    x_values = cycle((0, 1))
    
    for i in range (start, end):
        x = next(x_values)
    

    cycle() is especially useful for swapping between more than one value, especially when there is no easy mathematical or logical relationship between them:

    >>> from itertools import cycle
    >>> x_values = cycle((1, 'spam', 3.14))
    >>> next(x_values)
    1
    >>> next(x_values)
    'spam'
    >>> next(x_values)
    3.14
    >>> next(x_values)
    1
    

But to figure out which works best for you, pick a metric and stick to that. For performance, use the timeit module for comparative trials.

Using x - 1 is by far faster than cycle() or your expression, for example:

>>> from timeit import timeit
>>> timeit('x = 1 - x', 'x = 0')
0.044335126876831055
>>> timeit('x ^= 1', 'x = 0')
0.05133986473083496
>>> timeit('x = ((x + 1) % 2)', 'x = 0')
0.11392998695373535
>>> timeit('x = next(x_values)', 'from itertools import cycle; x_values = cycle((0, 1))')
0.1254570484161377

The Python compiler interprets code pretty straightforward, mostly. Your if version would not be optimized, for example. Use the dis.dis() function to see a disassembly of the compiler output:

>>> import dis
>>> def foo():
...     if x==0:
...         x=1
...     else:
...         x=0
... 
>>> dis.dis(foo)
  2           0 LOAD_FAST                0 (x)
              3 LOAD_CONST               1 (0)
              6 COMPARE_OP               2 (==)
              9 POP_JUMP_IF_FALSE       21

  3          12 LOAD_CONST               2 (1)
             15 STORE_FAST               0 (x)
             18 JUMP_FORWARD             6 (to 27)

  5     >>   21 LOAD_CONST               1 (0)
             24 STORE_FAST               0 (x)
        >>   27 LOAD_CONST               0 (None)
             30 RETURN_VALUE        

As you can see the full branching structure is still there. Using an expression leads to very different bytecode:

>>> def bar():
...     x = ((x + 1) % 2)
... 
>>> dis.dis(bar)
  2           0 LOAD_FAST                0 (x)
              3 LOAD_CONST               1 (1)
              6 BINARY_ADD          
              7 LOAD_CONST               2 (2)
             10 BINARY_MODULO       
             11 STORE_FAST               0 (x)
             14 LOAD_CONST               0 (None)
             17 RETURN_VALUE        
Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
0

You can use xor operator ^ to toggle between 0 and 1:

x ^= 1
mvp
  • 111,019
  • 13
  • 122
  • 148