A friend of mine showed me the following Python code:
a[1:] == a[:-1]
Which returns True iff all the items in a
are identical.
I argued the code is hard to understand from first sight, and moreover - it is inefficient in memory usage, because two copies of a
will be created for the comparison.
I've used Python's dis
to see what happens behind the hood at a[1:]==a[:-1]
:
>>> def stanga_compare(a):
... return a[1:]==a[:-1]
...
>>> a=range(10)
>>> stanga_compare(a)
False
>>> a=[0 for i in range(10)]
>>> stanga_compare(a)
True
>>> dis.dis(stanga_compare)
2 0 LOAD_FAST 0 (a)
3 LOAD_CONST 1 (1)
6 SLICE+1
7 LOAD_FAST 0 (a)
10 LOAD_CONST 2 (-1)
13 SLICE+2
14 COMPARE_OP 2 (==)
17 RETURN_VALUE
It boils down to two slicing commands - SLICE+1
and SLICE+2
. The documentation is unclear as to whether these opcodes actually create a new copy of a
, or just a reference to it.
- Does the SLICE commands copy
a
? - Does the answer varies between Python implementations (Cython, Jython)?
Update
This snippet is clearly unreadable and confusing, and I'm not going to ever use it in actual code. My interest is purely technical - whether slicing copies the list, and whether the answer varies under different circumstances.
The documentation is unclear because slicing different objects does different things. In the case of a list, slicing does make a (shallow) copy1. Note that this is a feature of python lists independent of python implementation. In the case of other objects (like numpy arrays), it might not create a copy.
If you want a better way to check that all the elements in a list are the same, I would probably recommend:
From a performance standpoint, your friend's code might actually outperform this for small lists since list slicing is so optimized. But, this is IMHO much easier to tell what is going on and has the opportunity to "short circuit" as soon as it finds a non-match.
1The actual function to look at is
list_subscript
, but for most cases, it just callslist_slice