330
\$\begingroup\$

What general tips do you have for golfing in Python? I'm looking for ideas which can be applied to code-golf problems and which are also at least somewhat specific to Python (e.g. "remove comments" is not an answer).

Please post one tip per answer.

\$\endgroup\$
4

179 Answers 179

11
\$\begingroup\$

use os.urandom() as a random source instead of random.randint()

\$\endgroup\$
5
  • 2
    \$\begingroup\$ Doesn't this require use of ord() to get a number instead of character? len("ord(os.urandom(1))") -> 18 and len("random.randint()") -> 16 \$\endgroup\$
    – jscs
    May 1, 2011 at 3:10
  • 4
    \$\begingroup\$ @Josh, don't forget you need to import random vs import os. randint() needs 3 parameters anyway. If you need a list of random numbers, you can use map(ord,os.urandom(N)) Also, sometimes, you actually need a random char instead of a number \$\endgroup\$
    – gnibbler
    May 1, 2011 at 7:17
  • 6
    \$\begingroup\$ Late to the party, but if you only need a few random numbers, try using id(id), substituting the inner id with any 3-letter builtin if you need more than one. 'abc'[id(id)%3] is 11 characters shorter than 'abc'[random.randrange(3)], not even counting the import statement. \$\endgroup\$
    – ashastral
    Apr 20, 2013 at 2:04
  • \$\begingroup\$ @Fraxtil id can be applied on mostly everything, such as 1 or []. \$\endgroup\$
    – DELETE_ME
    Jul 5, 2018 at 9:41
  • \$\begingroup\$ If you want to generate random bytes, of course. \$\endgroup\$
    – Joao-3
    Jan 5, 2023 at 13:35
11
\$\begingroup\$

Iterating over indices in a list

Sometimes, you need to iterate over the indices of a list l in order to do something for each element that depends on its index. The obvious way is a clunky expression:

# 38 chars
for i in range(len(l)):DoStuff(i,l[i])

The Pythonic solution is to use enumerate:

# 36 chars
for i,x in enumerate(l):DoStuff(i,x)

But that nine-letter method is just too long for golfing.

Instead, just manually track the index yourself while iterating over the list.

# 32 chars
i=0
for x in l:DoStuff(i,x);i+=1

Here's some alternatives that are longer but might be situationally better

# 36 chars
# Consumes list
i=0
while l:DoStuff(i,l.pop(0));i+=1

# 36 chars
i=0
while l[i:]:DoStuff(i,l[i]);i+=1
\$\endgroup\$
11
\$\begingroup\$

Leak variables to save on assignment

Combining with this tip, suppose you have a situation like

for _ in[0]*x:doSomething()
a="blah"

You can instead do:

for a in["blah"]*x:doSomething()

to skip out on a variable assignment. However, be aware that

exec"doSomething();"*x;a="blah"

in Python 2 is just shorter, so this only really saves in cases like assigning a char (via "c"*x) or in Python 3.

However, where things get fun is with Python 2 list comprehensions, where this idea still works due to a quirk with list comprehension scope:

[doSomething()for a in["blah"]*x]

(Credits to @xnor for expanding the former, and @Lembik for teaching me about the latter)

\$\endgroup\$
2
  • \$\begingroup\$ Does the last one not work in Python 3? \$\endgroup\$
    – xnor
    Jun 26, 2015 at 17:29
  • 1
    \$\begingroup\$ @xnor in Python 3 exec requires parentheses, so it would be one character longer than the for... format. \$\endgroup\$ Jul 14, 2017 at 7:10
11
\$\begingroup\$

Use complex numbers to find the distance between two points

Say you have two 2-element tuples which represent points in the Euclidean plane, e.g. x=(0, 0) and y=(3, 4), and you want to find the distance between them. The naïve way to do this is

d=((x[0]-y[0])**2+(x[1]-y[1])**2)**.5

Using complex numbers, this becomes:

c=complex;d=abs(c(*x)-c(*y))

If you have access to each coordinate individually, say a=0, b=0, c=3, d=4, then

abs(a-c+(b-d)*1j)

can be used instead.

\$\endgroup\$
4
  • 1
    \$\begingroup\$ Only 3 chars longer is d=abs(x[0]-y[0]+(x[1]-y[1])*1j). Can this be made shorter than what you have? I'm just wondering if 1j could be useful in converting a tuple to a complex number. \$\endgroup\$
    – mbomb007
    Jun 16, 2015 at 21:42
  • \$\begingroup\$ @mbomb007 It depends on what you have I guess. If you need to process the individual numbers in the two tuples anyway, then *1j is usually shorter. \$\endgroup\$
    – Sp3000
    Jun 17, 2015 at 7:25
  • \$\begingroup\$ In this case, it's the list subscription that's expensive. Consider, instead of x=(0,0);y=(3,4);c=complex;d=abs(c(*x)-c(*y)), different assignments: a,b=0,0;c,d=3,4;d=((a-c)**2+(b-d)**2)**.5. Yes, writing x=0,0;y=3,4 does indeed make the complex option shorter, but using different assignments makes it even shorter: x=0,0;y=3,4;c=complex;d=abs(c(*x)-c(*y). Finally, consider @mbomb007's approach with different assignments: a,b=0,0;x,y=3,4;d=abs(a-x+(b-y)*1j): with or without the assignment, it's shorter than all of the alternatives I've found. \$\endgroup\$
    – Ogaday
    Feb 16, 2016 at 13:11
  • 1
    \$\begingroup\$ @Ogaday This tip's been long overdue for the 1j part, so I've added that in. The general point was basically that ((a-b)**2+(c-d)**2)**.5 is rarely ever needed. \$\endgroup\$
    – Sp3000
    Feb 16, 2016 at 13:32
11
\$\begingroup\$

Use f-strings

Python 3.6 introduces a new string literal that is vastly more byte-efficient at variable interpolation than using % or .format() in non-trivial cases. For example, you can write:

l='Python';b=40;print(f'{l}, {b} bytes')

instead of

l='Python';b=43;print('%s, %d bytes'%(l,b))
\$\endgroup\$
6
  • \$\begingroup\$ At the time of writing, getting a Python 3.6 pre-release to run on Windows is a more difficult matter... ^^; \$\endgroup\$
    – Lynn
    Mar 2, 2016 at 20:05
  • 1
    \$\begingroup\$ cough ...linux ftw... cough \$\endgroup\$
    – cat
    Mar 4, 2016 at 3:08
  • \$\begingroup\$ If you get MinGW, you could just build from source \$\endgroup\$
    – cat
    Mar 4, 2016 at 3:09
  • 2
    \$\begingroup\$ It's shorter in trivial cases, too. Compare ' '+str(n) (10 bytes) and f' {n}' (7 bytes). \$\endgroup\$
    – Evpok
    Sep 8, 2016 at 18:29
  • \$\begingroup\$ The better comparison would be with ' %d'%n I think, where this example doesn't save any bytes. You do need more args for this to help \$\endgroup\$
    – Sp3000
    Sep 9, 2016 at 6:04
11
\$\begingroup\$

Abuse == short circuiting

If you have:

  • A function with a side effect (such as print);
  • That you only want to run if some condition is (or is not) met.

Then you might be able to use == over or to save a byte.

Here's printing all numbers n under 100 that have f(n) less than 2:

# Naive
for n in range(100):f(n)<2and print(n)
# Invert condition
for n in range(100):f(n)>1or print(n)
# Use ==
for n in range(100):f(n)<2==print(n)
\$\endgroup\$
11
\$\begingroup\$

Combine assignments of reused values with unused for-loop variables

If you need to loop a number of times but you don't care about the iteration variable, you can co-opt the loop to assign a variable.

r=reused;for _ in"_"*n:stuff
r=reused;exec("r;"*n)                          # [note 1]
r=reused;exec"r;"*n                            # [note 1]; Python 2 only
for r in[reused]*n:r

lambda args:((r:=reused)for _ in"_"*n)         # generally needs parentheses
lambda args,r=reused:(r for _ in"_"*n)         # only works with constants
lambda args:(r for r in[reused]*n)

This is generally a more versatile approach for assignment than the := operator or using default arguments of functions, because it supports assigning to attributes .x, subscripts [x], and unpacking with * or ,.

(stuff+(a[0]:=value)for _ in"_"*n)                   # syntax error
(stuff+a[0]for a[0]in[value]*n)                      # works, and shorter!

(stuff+a+b for*a,b in[value]*n)                      # works!

The only pitfall is that scope inside comprehensions is sometimes quite confusing, because the body of the comprehension is compiled as a separate implicit function.

Taken from @xnor's use of it here.

[note 1]: and longer if backslashes/quotes/newlines/... need to be escaped inside the string


This is a bot account operated by pxeger. I'm posting this to get enough reputation to use chat.

\$\endgroup\$
1
  • 10
    \$\begingroup\$ howdy there stranger! welcome to code gol...oh wait nevermind. \$\endgroup\$
    – lyxal
    Jul 20, 2021 at 11:44
10
\$\begingroup\$

Avoid list.insert

Instead of list.insert, appending to a slice is shorter:

L.insert(i,x)
L[:i]+=x,

For example:

>>> L = [1, 2, 3, 4]
>>> L[:-2]+=5,
>>> L
[1, 2, 5, 3, 4]
>>> L[:0]+=6,
>>> L
[6, 1, 2, 5, 3, 4]
\$\endgroup\$
10
\$\begingroup\$

Store 8-bit numbers compactly as a bytes object in Python 3

In Python 3, a bytes object is written as a string literal preceded by a b, like b"golf". It acts much like a tuple of the ord values of its characters.

>>> l=b"golf"
>>> list(l)
[103, 111, 108, 102]
>>> l[2]
108
>>> 108 in l
True
>>> max(l)
111
>>> for x in l:print(x)
103
111
108
102

Python 2 also has bytes objects but they act as strings, so this only works in Python 3.

This gives a shorter way to express an explicit list of numbers between 0 to 255. Use this to hardcode data. It uses one byte per number, plus three bytes overhead for b"". For example, the list of the first 9 primes [2,3,5,7,11,13,17,19,23] compresses to 14 bytes rather than 24. (An extra byte is used for a workaround explained below for character 13.)

In many cases, your bytes object will contain non-printable characters such as b"\x01x02\x03" for [1, 2, 3]. These are written with hex escape characters, but you may use them a single characters in your code (unless the challenge says otherwise) even though SE will not display them. But, characters like the carriage return b"\x0D" will break your code, so you need to use the two-char escape sequence "\r".

\$\endgroup\$
0
10
\$\begingroup\$

Use powers of the imaginary unit to calculate sines and cosines.

For example, given an angle d in degrees, you can calculate the sine and cosine as follows:

p=1j**(d/90.)
s=p.real
c=p.imag

This can also be used for related functions such as the side length of a unit n-gon:

l=abs(1-1j**(4./n))
\$\endgroup\$
1
  • 3
    \$\begingroup\$ Does... Does this also become a math golfing tip? o0 \$\endgroup\$ Oct 31, 2017 at 1:14
10
\$\begingroup\$

0in instead of not all

(or, under DeMorgan's Law, any(not ...))

not all(...)
~-all(...)  # shorter than `not`, and with more forgiving precedence
0in(...)
not all map(f,a)
0in map(f,a)  # the reduction is more significant here because you can omit the parentheses

This only works if the falsey values in the ... sequence are actually False (or 0/0.0/etc.) (not []/""/{} etc.).

1in instead of any

This one isn't shorter with a comprehension:

any(f(x)for x in a)
1in(f(x)for x in a)

But it sometimes saves bytes with other kinds of expression by letting you omit the parentheses:

any(map(f,a))
1in map(f,a)

This has a similar truthiness-related caveat to the above, though.


Notes

These might sometimes have less favourable precedence, because they use the in operator. However, if you're combining this with a comparison you may be able to make additional use of this tip about comparison condition chaining.

You can also use these if you want the entire for-comprehension in any/all to always be fully evaluated:

any([... for x in a])
1in[... for x in a]

This one's rare, but if you need to evaluate and discard an extra expression for every item in a comprehension, you could use a dictionary here at no extra cost:

1in[(condition,side_effect)[0]for x in a]
1in{condition:side_effect for x in a}

because dict's in checks only the keys.

\$\endgroup\$
9
\$\begingroup\$

Abuse the fact that in case of an expression yielding True boolean operators return the first value that decides about the outcome of the expression instead of a boolean:

>>> False or 5
5

is pretty straightforward. For a more complex example:

>>> i = i or j and "a" or ""

i's value remains unchanged if it already had a value set, becomes "a" if j has a value or in any other case becomes an empty string (which can usually be omitted, as i most likely already was an empty string).

\$\endgroup\$
1
  • 4
    \$\begingroup\$ Shorter: i=i or j and"a"or"" \$\endgroup\$
    – Luna
    Aug 18, 2014 at 16:46
9
\$\begingroup\$

Check if a number is a power of 2

Check whether a positive integer n is a perfect power of 2, that is one of 1, 2, 4, 8, 16, ..., with any of these expressions:

n&~-n<1
n&-n==n
n^n-1>=n
2**n%n<1

The third expression also works for n==0 giving False. The last is easy to modify to checking for, say, powers of 3.

\$\endgroup\$
2
  • \$\begingroup\$ -n^n<-n is also an option, even better if you're detecting a negative power of 2 \$\endgroup\$
    – Jo King
    Jul 27, 2023 at 6:26
  • \$\begingroup\$ oh, and as a modification of the second expression -n&n^n gives 0 for powers of 2, including zero. \$\endgroup\$
    – Jo King
    Jul 27, 2023 at 6:34
8
\$\begingroup\$

Lets play with some list tricks

a=[5,5,5,5,5,5,5]

can be written as:

a=[5]*7

It can be expanded in this way. Lets, say we need to do something like

for i in range(0,100,3):a[i]=5

Now using the slicing trick we can simply do:

a[0:100:3]=[5]*(1+99//3)
\$\endgroup\$
2
  • 6
    \$\begingroup\$ last example, of course, must be written a[:100:3]=[5]*34 \$\endgroup\$
    – AMK
    Dec 16, 2013 at 18:57
  • 5
    \$\begingroup\$ @AMK yap i know ;) But i kept that way cause people may get hard time guessing how i got 34. it just from the formula (1+(100-1)//3) where 100 is number of elements and 3 is the step. Thanks for pointing it :D \$\endgroup\$
    – Wasi
    Dec 16, 2013 at 19:03
8
\$\begingroup\$

If you are doing something small in a for loop whose only purpose is to invoke a side effect (pop, print in Python 3, append), it might be possible to translate it to a list-comprehension. For example, from Keith Randall's answer here, in the middle of a function, hence the indent:

  if d>list('XXXXXXXXX'):
   for z in D:d.pop()
   c=['X']

Can be converted to:

  if d>list('XXXXXXXXX'):
   [d.pop()for z in D]
   c=['X']

Which then allows this golf:

  if d>list('XXXXXXXXX'):[d.pop()for z in D];c=['X']

An if within a for works just as well:

for i in range(10):
 if is_prime(i):d.pop()

can be written as

[d.pop()for i in range(10)if is_prime(i)]
\$\endgroup\$
1
  • \$\begingroup\$ [d.pop()for i in range(10)if is_prime(i)] is shorter as for i in range(10):notprime(i)or d.pop(), so it's shorter with a False condition instead of a True one if the condition can be squished into the or. Obviously, if you need the popped values the comprehension is shorter, but this post is about side effects. \$\endgroup\$ Jul 4, 2021 at 22:11
8
\$\begingroup\$

Use map for side effects

Usually you use map to transform a collection

>> map(ord,"abc")
[97, 98, 99]

But you can also use it to repeatedly act on object by a built-in method that modifies it.

>> L=[1,2,3,4,5]
>> map(L.remove,[4,2])
[None, None]
>> L
[1, 3, 5]

Be aware that the calls are done in order, so earlier ones might mess up later ones.

>> L=[1,2,3,4,5]
>> map(L.pop,[0,1])
[1, 3]
>> L
[2, 4, 5]

Here, we intended to extract the first two elements of L, but after extracting the first, the next second element is the original third one. We could sort the indices in descending order to avoid this.

An advantage of the evaluation-as-action is that it can be done inside of a lambda. Be careful in Python 3 though, where map objects are not evaluated immediately. You might need an expression like [*map(...)] or *map(...), to force evaluation.

\$\endgroup\$
2
8
\$\begingroup\$

Logical short-circuiting in recursive functions

A detailed guide

I had worked with short-circuiting and/or's for a while without really grasping how they work, just using b and x or y just as a template. I hope this detailed explanation will help you understand them and use them more flexibly.


Recursive named lambda functions are often shorter than programs that loop. For evaluation to terminate, there must be control flow to prevent a recursive call for the base case. Python has a ternary condition operator that fits the bill.

f=lambda x:base_value if is_base_case else recursive_value

Note that list selection won't work because Python evaluates both options. Also, regular if _: isn't an option because we're in a lambda.


Python has another option to short-circuit, the logical operator keywords and and or. The idea is that

True or b == True
False and b == False

so Python can skip evaluate b in these cases because the result is known. Think of the evaluation of a or b as "Evaluate a. If it's True, output a. Otherwise, evaluate and output b." So, it's equivalent to write

a or b
a if a else b

It's the same for a or b except we stop if a is False.

a and b
a if (not a) else b

You might wonder why we didn't just write False if (not a) else b. The reason is that this works for non-Boolean a. Such values are first converted to a Boolean. The number 0, None, and the empty list/tuple/set become False, and are so called "Falsey". The rest are "Truthy".

So, a or b and a and b always manages to produce either a or b, while forming a correct Boolean equation.

(0 or 0) == 0
(0 or 3) == 3
(2 or 0) == 2
(2 or 3) == 2
(0 and 0) == 0
(0 and 3) == 0
(2 and 0) == 0
(2 and 3) == 3
('' or 3) == 3
([] and [1]) == []
([0] or [1]) == [0]

Now that we understand Boolean short-circuiting, let's use it in recursive functions.

f=lambda x:base_value if is_base_case else recursive_value

The simplest and most common situation is when the base is something like f("") = "", sending a Falsey value to itself. Here, it suffices to do x and with the argument.

For example, this function doubles each character in a string, f("abc") == "aabbcc".

f=lambda s:s and s[0]*2+f(s[1:])

Or, this recursively sums the cubes of numbers 1 through n, so f(3)==36.

f=lambda n:n and n**3+f(n-1)

Another common situation is for your function to take non-negative numbers to lists, with a base case of 0 giving the empty list. We need to transform the number to a list while preserving Truthiness. One way is n*[5], where the list can be anything nonempty. This seems silly, but it works.

So, the following returns the list [1..n].

f=lambda n:n*[5]and f(n-1)+[n]  

Note that negative n will also give the empty list, which works here, but not always. For strings, it's similar with any non-empty string. If you've previously defined such a value, you can save chars by using it.

More generally, when your base value is an empty list, you can use the arithmetic values True == 1 and False == 0 to do:

[5]*(is_not_base_case)and ...

TODO: Truthy base value


TODO: and/or

\$\endgroup\$
1
  • \$\begingroup\$ An easy way to explain how and and or work might be: x or y=if x:return x;return y x and y:if x:return y;return x \$\endgroup\$
    – fejfo
    Jan 11, 2018 at 19:47
7
\$\begingroup\$

Cut out newlines wherever you can.

At the top-level, it doesn't matter.

a=1
b=9

Takes the same amount of characters as:

a=1;b=9

In the first case you have a newline instead of a ;. But in function bodies, you save however deep the nesting level is:

def f():
 a=1;b=9

Actually in this case, you can have them all on one line:

def f():a=1;b=9

If you have an if or a for, you can likewise have everything on one line:

if h:a=1;b=9
for g in(1,2):a=1;b=9

But if you have a nesting of control structures (e.g. if in a for, or if in a def), then you need the newline:

if h:for g in(1,2):a=1;b=9 #ERROR

if h:
 for g in(1,2):a=1;b=9 # SAUL GOODMAN
\$\endgroup\$
3
  • \$\begingroup\$ You can use a,b=1,9 \$\endgroup\$
    – Oliver Ni
    Jan 4, 2015 at 1:24
  • \$\begingroup\$ It's too bad you can't have multiple :s on one line :( \$\endgroup\$ May 28, 2017 at 5:35
  • \$\begingroup\$ @OliverNi a,b=1,9 does not save any bytes \$\endgroup\$ Jul 12, 2017 at 14:40
7
\$\begingroup\$

Use eval to iterate

Say you want to apply f composed k times to the number 1, then print the result.

This can be done via an exec loop,

n=1
exec("n=f(n);"*k)
print(n)

which runs code like n=1;n=f(n);n=f(n);n=f(n);n=f(n);n=f(n);print(n).

But, it's one character shorter to use eval

print(eval("f("*k+'1'+")"*k))

which evaluates code like f(f(f(f(f(1))))) and prints the result.

This does not save chars in Python 2 though, where exec doesn't need parens but eval still does. It does still help though when f(n) is an expression in which n appears only once as the first or last character, letting you use only one string multiplication.

\$\endgroup\$
7
\$\begingroup\$

One trick I have encountered concerns returning or printing Yes/No answers:

 print 'YNeos'[x::2]

x is the condition and can take value 0 or 1.

I found this rather brilliant.

\$\endgroup\$
1
  • 5
    \$\begingroup\$ Welcome to PPCG! It seems that this is mostly a special case of this tip though. \$\endgroup\$ Apr 14, 2016 at 16:26
7
\$\begingroup\$

A condition like

s = ''
if c:
    s = 'a'

can be written as

s = c*'a'

and there is possibly a need for parenthesis for condition.

This can also be combined with other conditions as (multiple ifs)

s = c1*'a' + c2*'b'

or (multiple elifs)

s = c1*'a' or c2*'b'

For example FizzBuzz problem's solution will be

for i in range(n):
    print((i%3<1)*"Fizz"+(i%5<1)*"Buzz" or i)
\$\endgroup\$
7
\$\begingroup\$

Use Splat (*) to pass a bunch of single character strings into a function

For example:

a.replace("a","b")
a.replace(*"ab")    -2 bytes

some_function("a","b","c")
some_function(*"abc")       -5 bytes

In fact, if you have n single-character strings, you will save 3(n - 1) - 1 or 3n - 4 bytes by doing this (because each time, you remove the "," for each one and add a constant *).

\$\endgroup\$
7
\$\begingroup\$

Trig without imports

You can compute cos and sin without needing to import math by using complex arithmetic. For an angle of d degrees, its cosine is

(1j**(d/90)).real

and its sine is

(1j**(d/90)).imag

Here, 1j is how Python writes the imaginary unit \$i\$. If your angle is r radians, you'll need to use 1j**(r/(pi/2)), using a decimal approximation of pi/2 if the challenge allows it.

If you're curious, this all works because of Euler's formula:

$$i^x = (e^{i \pi /2})^x = e^{i \pi /2 \cdot x} = \cos(\pi/2 \cdot x) + i \sin(\pi /2 \cdot x)$$

\$\endgroup\$
7
\$\begingroup\$

!= can be replaced with -
here is a example

n=int(input())
if n!=69:
 print("thanks for being mature")

instead of using != you can use -
after that it should look like this

n=int(input())
if n-69:
 print("thanks for being mature")
\$\endgroup\$
7
\$\begingroup\$

Multiple if statements in comprehensions

If you need to keep multiple conditions inside comprehension, you can replace and with if to save a byte each time.

Works in Python 2 and 3.

[a for a in 'abc'if cond1()and cond2()or cond3()and cond4()and cond5()]
[a for a in 'abc'if cond1()if cond2()or cond3()if cond4()if cond5()]

Try it online!

\$\endgroup\$
6
\$\begingroup\$

Build a string instead of joining

To concatenate strings or characters, it can be shorter to repeatedly append to the empty string than to join.

23 chars

s=""
for x in l:s+=f(x)

25 chars

s="".join(f(x)for x in l)

Assume here that f(x) stands for some expression in x, so you can't just map.

But, the join may be shorter if the result doesn't need saving to a variable or if the for takes newlines or indentation.

\$\endgroup\$
6
\$\begingroup\$

String keys to dicts

For a dictionary with string keys which also happen to be valid Python variable names, you can get a saving if there's at least three items by using dict's keyword arguments:

{'a':1,'e':4,'i':9}
dict(a=1,e=4,i=9)

The more string keys you have, the more quote characters you'll save, so this is particularly beneficial for large dictionaries (e.g. for a kolmogorov challenge).

\$\endgroup\$
6
\$\begingroup\$

When squaring single letter variables, it is shorter to times it by itself

>>> x=30
>>> x*x
900

Is one byte shorter than

>>> x=30
>>> x**2
900
\$\endgroup\$
6
\$\begingroup\$

When mapping a function on a list in Python 3, instead of doing [f(x)for x in l] or list(map(f,l)), do [*map(f,l)].

It works for all other functions returning generators too (like filter).

The best solution is still switching to Python 2 though

\$\endgroup\$
6
\$\begingroup\$

Iterate over adjacent pairs

It's common to want to iterate over adjacent pairs of items in a list or string, i.e.

"golf" -> [('g','o'), ('o','l'), ('l','f')]

There's a few methods, and which is shortest depends on specifics.

Shift and zip

## 47 bytes
l=input()
for x,y in zip(l,l[1:]):do_stuff(x,y)

Create a list of adjacent pairs, by removing the first element and zipping the original with the result. This is most useful in a list comprehension like

sum(abs(x-y)for x,y in zip(l,l[1:]))

You can also use map with a two-input function, though note that the original list is no longer truncated.

## Python 2
map(cmp,l[:-1],l[1:])

Keep the previous

## 41 bytes, Python 3
x,*l=input()
for y in l:do_stuff(x,y);x=y

Iterate over the elements of the list, remembering the element from a previous loop. This works best with Python 3's ability to unpack to input into the initial and remaining elements.

If there's an initial value of x that serves as a null operation in do_stuff(x,y), you can iterate over the whole list.

## 39 bytes
x=''
for y in input():do_stuff(x,y);x=y

Truncate from the front

## 46 bytes
l=input()
while l[1:]:do_stuff(*l[:2]);l=l[1:]

Keep shortening the list and act on the first two elements. This works best when your operation is better-expressed on a length-two list or string than on two values.


I've written these all as loops, but they also lend to a recursive functions. You can also adjust to get cyclic pairs by putting the first element at the end of the list, or as the initial previous-value.


The Python 3.8 "walrus" assignment expressions allow a short expression to give pairs, though with an extra initial element.

>>> p=''
>>> [(p,p:=c)for c in"golf"]
[('', 'g'), ('g', 'o'), ('o', 'l'), ('l', 'f')]
\$\endgroup\$
2
  • \$\begingroup\$ Also worth mentioning since I just used it, if you want the "initial value of x that serves as a null operation" at the end of your list as well so the final item is processed with it you can do x=''(\n)for y in*input(),x:do_stuff(x,y);x=y \$\endgroup\$ Jul 4, 2021 at 22:30
  • \$\begingroup\$ As of 3.10, if you already have itertools stuff imported for some other reason with from itertools import*, you can use pairwise for this, replacing for x,y in zip(l,l[1:]): with for x,y in pairwise(l): to save a character. Also, on your "Truncate from the front" solution, you can shave a byte by using unpacking and a single indexing operation in place of two uses of slicing: while l[1:]:a,*l=l;do_stuff(a,l[0]) \$\endgroup\$ Feb 24, 2023 at 1:49

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.