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