329
\$\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

178 Answers 178

1
2 3 4 5 6
211
\$\begingroup\$

Use a=b=c=0 instead of a,b,c=0,0,0. (Note that this uses the same instance for each variable, so don't do this with objects like lists if you intend to mutate them independently)

Use a,b,c='123' instead of a,b,c='1','2','3'.

\$\endgroup\$
6
  • 4
    \$\begingroup\$ that's nice tip in general :) \$\endgroup\$
    – user18660
    Mar 20, 2014 at 20:11
  • 44
    \$\begingroup\$ Note that this will not necessarily work for defining mutable objects that you will be modifying in-place. a=b=[1] is actually different from a=[1];b=[1] \$\endgroup\$
    – isaacg
    Apr 23, 2014 at 9:02
  • 8
    \$\begingroup\$ The funny thing about the first tip is that it works in Java too. \$\endgroup\$
    – Justin
    Apr 24, 2014 at 7:46
  • 3
    \$\begingroup\$ @Justin Yes, but only with primitive types \$\endgroup\$
    – hyper-neutrino
    Sep 12, 2015 at 15:41
  • 27
    \$\begingroup\$ But NEVER use a=b=c=[] or any object instanciation since all the variables will point to the same instance. That's probably not what you want. \$\endgroup\$
    – PhE
    Aug 22, 2017 at 13:38
199
\$\begingroup\$

Conditionals can be lengthy. In some cases, you can replace a simple conditional with (a,b)[condition]. If condition is true, then b is returned.

Compare

if a<b:return a
else:return b

To this

return(b,a)[a<b]
\$\endgroup\$
9
  • 58
    \$\begingroup\$ These aren't exactly the same. The first one evaluates only the expression that is returned while the second one always evaluates them both. These ones do short-circuit: a if a<b else b and a<b and a or b \$\endgroup\$
    – marinus
    May 3, 2011 at 11:34
  • 7
    \$\begingroup\$ (lambda(): b, lambda(): a)[a < b]() make your own short-circuit with lambdas \$\endgroup\$
    – Ming-Tang
    May 7, 2011 at 23:33
  • 3
    \$\begingroup\$ @marinus, they are not equal: just consider P and A or B for any A that gives bool(A)=False. But (P and [A] or [B])[0] will do the job. See diveintopython.net/power_of_introspection/and_or.html for reference. \$\endgroup\$
    – kgadek
    Apr 24, 2013 at 11:01
  • 10
    \$\begingroup\$ Lambdas are way longer than a conditional expression. \$\endgroup\$ Mar 20, 2014 at 7:07
  • 28
    \$\begingroup\$ @user2357112 But they make you look so much cooler when you use them. :] \$\endgroup\$
    – Chase Ries
    Mar 20, 2014 at 9:06
157
\$\begingroup\$

A great thing I did once is:

if 3 > a > 1 < b < 5: foo()

instead of:

if a > 1 and b > 1 and 3 > a and 5 > b: foo()

Python’s comparison operators rock.


Using that everything is comparable in Python 2, you can also avoid the and operator this way. For example, if a, b, c and d are integers,

if a<b and c>d:foo()

can be shortened by one character to:

if a<b<[]>c>d:foo()

This uses that every list is larger than any integer.

If c and d are lists, this gets even better:

if a<b<c>d:foo()
\$\endgroup\$
8
  • 35
    \$\begingroup\$ Of course if this were actually golfed it'd be 3>a>1<b<5 \$\endgroup\$ Jan 28, 2011 at 0:35
  • 5
    \$\begingroup\$ Love the symmetry. Reminds me of the old Perl golf trick for finding the min of $a and $b: [$a => $b]->[$b <= $a] :) \$\endgroup\$ Feb 3, 2011 at 21:42
  • 9
    \$\begingroup\$ The + should be a *. An or would be + \$\endgroup\$ May 22, 2014 at 10:53
  • 2
    \$\begingroup\$ foo()if 3>a>1<b<5 \$\endgroup\$ Jun 10, 2016 at 7:31
  • 1
    \$\begingroup\$ @EriktheOutgolfer foo()if 3>a>1<b<5 doesn't work for me. \$\endgroup\$ Dec 24, 2019 at 8:52
132
\$\begingroup\$

If you're using a built-in function repeatedly, it might be more space-efficient to give it a new name, if using different arguments:

r=range
for x in r(10):
 for y in r(100):print x,y
\$\endgroup\$
9
  • 12
    \$\begingroup\$ Didn't actually save any bytes, though. \$\endgroup\$ Mar 20, 2014 at 7:06
  • 9
    \$\begingroup\$ r=range and the other two r's are 9 characters; using range twice is 10 characters. Not a huge saving in this example but all it would take is one more use of range to see a significant saving. \$\endgroup\$
    – Frank
    Aug 25, 2016 at 19:52
  • 26
    \$\begingroup\$ @Frank The additional newline is another character. \$\endgroup\$
    – L3viathan
    Nov 14, 2016 at 20:45
  • 9
    \$\begingroup\$ Indeed two repetitions is too little to save on a length five function name. You need: length 2: 6 reps, length 3: 4 reps, length 4 or 5: 3 reps, length >=6: 2 reps. AKA (length-1)*(reps-1)>4. \$\endgroup\$ Mar 18, 2017 at 16:26
  • 3
    \$\begingroup\$ Note this is applicable to all languages with first-class functions. \$\endgroup\$
    – bfontaine
    Jul 19, 2019 at 22:30
126
\$\begingroup\$

Use string substitution and exec to deal with long keywords like lambda that are repeated often in your code.

a=lambda b:lambda c:lambda d:lambda e:lambda f:0   # 48 bytes  (plain)
exec"a=`b:`c:`d:`e:`f:0".replace('`','lambda ')    # 47 bytes  (replace)
exec"a=%sb:%sc:%sd:%se:%sf:0"%(('lambda ',)*5)     # 46 bytes  (%)

The target string is very often 'lambda ', which is 7 bytes long. Suppose your code snippet contains n occurences of 'lambda ', and is s bytes long. Then:

  • The plain option is s bytes long.
  • The replace option is s - 6n + 29 bytes long.
  • The % option is s - 5n + 22 + len(str(n)) bytes long.

From a plot of bytes saved over plain for these three options, we can see that:

  • For n < 5 lambdas, you're better off not doing anything fancy at all.
  • For n = 5, writing exec"..."%(('lambda ',)*5) saves 2 bytes, and is your best option.
  • For n > 5, writing exec"...".replace('`','lambda ') is your best option.

For other cases, you can index the table below:

          1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 (occurences)
       +---------------------------------------------------------
     3 |  -  -  -  -  -  -  -  -  -  -  -  -  -  -  r  r  r  r  r  
     4 |  -  -  -  -  -  -  -  -  -  r  r  r  r  r  r  r  r  r  r  
     5 |  -  -  -  -  -  -  -  r  r  r  r  r  r  r  r  r  r  r  r  
     6 |  -  -  -  -  -  r  r  r  r  r  r  r  r  r  r  r  r  r  r  
     7 |  -  -  -  -  %  r  r  r  r  r  r  r  r  r  r  r  r  r  r  
     8 |  -  -  -  %  %  r  r  r  r  r  r  r  r  r  r  r  r  r  r  
     9 |  -  -  -  %  %  r  r  r  r  r  r  r  r  r  r  r  r  r  r  
    10 |  -  -  %  %  %  r  r  r  r  r  r  r  r  r  r  r  r  r  r  
    11 |  -  -  %  %  %  r  r  r  r  r  r  r  r  r  r  r  r  r  r  
    12 |  -  -  %  %  %  r  r  r  r  r  r  r  r  r  r  r  r  r  r   r = replace
    13 |  -  -  %  %  %  r  r  r  r  r  r  r  r  r  r  r  r  r  r   % = string %
    14 |  -  %  %  %  %  r  r  r  r  r  r  r  r  r  r  r  r  r  r   - = do nothing
    15 |  -  %  %  %  %  r  r  r  r  r  r  r  r  r  r  r  r  r  r  
  (length)

For example, if the string lambda x,y: (length 11) occurs 3 times in your code, you're better off writing exec"..."%(('lambda x,y:',)*3).

\$\endgroup\$
7
  • 8
    \$\begingroup\$ this should get more votes, it's a very useful tip. \$\endgroup\$
    – bigblind
    May 11, 2013 at 0:18
  • 13
    \$\begingroup\$ it's extremely rare that this works. the cost of replace is huge. \$\endgroup\$
    – boothby
    Oct 27, 2013 at 2:09
  • 7
    \$\begingroup\$ When it does work, though, it helps a lot. \$\endgroup\$ Apr 16, 2014 at 8:28
  • 1
    \$\begingroup\$ The same applies to import - you can do for s in ('module1','module2','etc'):exec"from %s import*"%s \$\endgroup\$
    – enedil
    Jul 17, 2017 at 10:35
  • 19
    \$\begingroup\$ If you use this often enough for .replace("R",'.replace("') to save bytes, many other replacements become cheaper. (It also makes your code entirely unreadable.) \$\endgroup\$
    – Hiatsu
    Aug 28, 2019 at 18:53
121
\$\begingroup\$

Sometimes your Python code requires you to have 2 levels of indentation. The obvious thing to do is use one and two spaces for each indentation level.

However, Python 2 considers the tab and space characters to be different indenting levels.

This means the first indentation level can be one space and the second can be one tab character.

For example:

if 1:
 if 1:
	pass
\$\endgroup\$
6
  • 1
    \$\begingroup\$ Cool, I never thought about this one! \$\endgroup\$ Jun 24, 2011 at 19:09
  • 143
    \$\begingroup\$ This fails in python3: you can no more mix spaces and tabs(a bad thing for codegolf, but a good thing in all other cases). \$\endgroup\$
    – Bakuriu
    Oct 19, 2013 at 15:41
  • 1
    \$\begingroup\$ In python 3.4 this seems to work fine. \$\endgroup\$ Aug 15, 2016 at 23:09
  • 10
    \$\begingroup\$ @trichoplax, In python 3.4.3 I get TabError: inconsistent use of tabs and spaces in indentation. \$\endgroup\$
    – ceilingcat
    Oct 3, 2016 at 5:16
  • 3
    \$\begingroup\$ Note that your editor may be changing the tab characters into space. This happened to me in VSCode. The trick there is to 1) enable whitespace rendering - "editor.renderWhitespace": "all" and 2) stop the editor from replacing it with whitespace - "editor.insertSpaces": false, "editor.detectIndentation": false. (VSCode version 1.46.1). If you want these to work only for the current golfing project, you can add a local settings.json file to the current workspace. \$\endgroup\$ Jun 22, 2020 at 10:11
105
\$\begingroup\$

Store lookup tables as magic numbers

Say you want to hardcode a Boolean lookup table, like which of the first twelve English numbers contain an n.

0: False
1: True
2: False
3: False
4: False
5: False
6: False
7: True
8: False
9: True
10:True
11:True
12:False

Then, you can implement this lookup table concisely as:

3714>>i&1

with the resulting 0 or 1 being equal to False to True.

The idea is that the magic number stores the table as a bitstring bin(3714) = 0b111010000010, with the n-th digit (from the end) corresponding the the nth table entry. We access the nth entry by bitshifting the number n spaces to the right and taking the last digit by &1.

This storage method is very efficient. Compare to the alternatives

n in[1,7,9,10,11]
'0111010000010'[n]>'0'

You can have your lookup table store multibit entries that can be extracted like

 340954054>>4*n&15

to extract the relevant four-bit block.

\$\endgroup\$
5
  • 1
    \$\begingroup\$ Could we have an example result for the four-bit block? Did you use a rule for n-bit block? \$\endgroup\$
    – jeromej
    Sep 14, 2015 at 8:23
  • 19
    \$\begingroup\$ Hex might sometimes be even smaller. \$\endgroup\$
    – Joonazan
    Feb 3, 2016 at 21:34
  • 7
    \$\begingroup\$ This is useful for a lot of languages. \$\endgroup\$
    – Cyoce
    Feb 11, 2016 at 3:50
  • 6
    \$\begingroup\$ @Joonazan Hex is smaller for numbers over 999 999. \$\endgroup\$ Jun 2, 2017 at 7:57
  • 1
    \$\begingroup\$ n in [...] might be smaller for sparse sets. \$\endgroup\$ Jun 9, 2020 at 0:53
103
\$\begingroup\$

Use extended slicing to select one string from many

>>> for x in 0,1,2:print"fbboaaorz"[x::3]
... 
foo
bar
baz

vs

>>> for x in 0,1,2:print["foo","bar","baz"][x]
... 
foo
bar
baz

In this Boolean two-string case, one can also write

b*"string"or"other_string"

for

["other_string","string"][b]

Unlike interleaving, this works for strings of any length, but can have operator precedence issues if b is instead an expression.

\$\endgroup\$
2
  • \$\begingroup\$ Note that the first example is exactly the same length as for x in ("foo","bar","baz"): print x \$\endgroup\$ Jun 2, 2017 at 7:21
  • 5
    \$\begingroup\$ @MateenUlhaq, That's just an example of how the different values of x are rendered. The golfed part is the "fbboaaorz"[x::3] vs ["foo","bar","baz"][x] How the x value is derived would be another part of your golf solution. \$\endgroup\$
    – gnibbler
    Jun 2, 2017 at 7:31
95
\$\begingroup\$

Collapse two numerical loops into one

Say you're iterating over the cells of an m*n grid. Instead of two nested for loops, one for the rows and one for the columns, it's usually shorter to write a single loop to iterate over the m*n cells of the grid. You can extract the row and column of the cell inside the loop.

Original code:

for i in range(m):
 for j in range(n):
  do_stuff(i,j)

Golfed code:

for k in range(m*n):
 do_stuff(k/n,k%n)

In effect, you're iterating over the Cartesian product of the two ranges, encoding the pair (i,j) as x=i*n+j. You've save a costly range call and a level of indentation inside the loop. The order of iteration is unchanged.

Use // instead of / in Python 3. If you refer to i and j many times, it may be shorter to assign their values i=k/n, j=k%n inside the loop.

\$\endgroup\$
6
  • 5
    \$\begingroup\$ This is awesome. I had never realised this was possible! \$\endgroup\$
    – minseong
    Apr 2, 2015 at 19:03
  • 1
    \$\begingroup\$ I saw this in the tips for JavaScript. It's a pretty useful trick in most languages. \$\endgroup\$
    – Cyoce
    Jan 12, 2016 at 6:59
  • 17
    \$\begingroup\$ For reference, to extend this to 3 loops: for i in range(m*n*o): do_stuff(i/n/o,i%(n*o)/o,i%o) \$\endgroup\$
    – mbomb007
    Oct 27, 2016 at 14:48
  • 6
    \$\begingroup\$ For n loops: repl.it/EHwa \$\endgroup\$
    – mbomb007
    Oct 27, 2016 at 16:33
  • 2
    \$\begingroup\$ In some cases, itertools.product can be much more concise than nested loops, especially when generating cartesian products. a1, a2, b1, b2 are examples of the cartesian product of 'ab' and '12' \$\endgroup\$
    – Aaron3468
    Mar 16, 2018 at 3:24
91
\$\begingroup\$

Use `n` to convert an integer to a string instead of using str(n):

>>> n=123
>>> `n`
'123'

Note: Only works in Python 2.

\$\endgroup\$
4
  • 2
    \$\begingroup\$ Attention: really works for integers, but not for strings, for example. \$\endgroup\$
    – Nakilon
    Jan 28, 2011 at 0:12
  • 55
    \$\begingroup\$ btw. `` is short for repr \$\endgroup\$
    – Alexandru
    Jan 28, 2011 at 2:14
  • 16
    \$\begingroup\$ Integers smaller than -2**31 or bigger than 2**31-1 (Longs) gets an 'L' tacked on at the end. \$\endgroup\$
    – hallvabo
    Jan 28, 2011 at 8:47
  • 8
    \$\begingroup\$ This can also be used to print floats to full precision \$\endgroup\$
    – gnibbler
    Feb 19, 2013 at 0:28
82
\$\begingroup\$

For integer n, you can write

  • n+1 as -~n
  • n-1 as ~-n

because the bit flip ~x equals -1-x. This uses the same number of characters, but can indirectly cut spaces or parens for operator precedence.

Compare:

while n-1:  #Same as while n!=1 
while~-n:

c/(n-1)
c/~-n

or f(n)+1
or-~f(n) 

(n-1)/10+(n-1)%10
~-n/10+~-n%10

The operators ~ and unary - are higher precedence than *, /, %, unlike binary +.

\$\endgroup\$
3
  • 23
    \$\begingroup\$ A variation on this trick I ran into today: -~-x saves one byte vs. (1-x). \$\endgroup\$
    – Lynn
    Jul 15, 2015 at 11:34
  • 11
    \$\begingroup\$ Another useful application is that a+b+1 can be more concisely written as a-~b. \$\endgroup\$
    – Strigoides
    Jan 22, 2017 at 11:51
  • 5
    \$\begingroup\$ And n-i-1 is just n+~i. \$\endgroup\$
    – ruohola
    Jun 9, 2019 at 22:23
76
\$\begingroup\$

Unless the following token starts with e or E. You can remove the space following a number.

For instance:

if i==4 and j==4:
    pass

Becomes:

if i==4and j==4:
    pass

Using this in complicated one line statements can save quite a few characters.

EDIT: as @marcog pointed out, 4or a will work, but not a or4 as this gets confused with a variable name.

\$\endgroup\$
7
  • 53
    \$\begingroup\$ if(i,j)==(4,4): is even shorter and in this special case if i==j==4: \$\endgroup\$
    – gnibbler
    Jan 28, 2011 at 0:14
  • 4
    \$\begingroup\$ Related: 4or a works, but not a or4 \$\endgroup\$
    – moinudin
    Jan 28, 2011 at 0:15
  • 24
    \$\begingroup\$ 0or also doesn't work (0o is a prefix for octal numbers). \$\endgroup\$
    – Nabb
    Feb 2, 2011 at 17:28
  • 8
    \$\begingroup\$ @Nabb Not that it matters anyways, since 0 or x is always gonna return x. Might as well cut out the 0 or. \$\endgroup\$ Apr 6, 2014 at 12:57
  • 11
    \$\begingroup\$ 0or is fine as part of a longer number though. 10 or x is equivalent to 10or x. \$\endgroup\$ Apr 20, 2014 at 22:39
73
\$\begingroup\$

A nice way to convert an iterable to list on Python 3:

imagine you have some iterable, like

i = (1,2,3,4)
i = range(4)
i = (x**2 for x in range(5))

But you need a list:

x=list(i)  #the default way
*x,=i      #using starred assignment -> 4 char fewer

It's very useful to make a list of chars out of a string

s=['a','b','c','d','e']
s=list('abcde')
*s,='abcde'
\$\endgroup\$
10
  • 2
    \$\begingroup\$ typing *s,='abcde' and then s crashes my interactive python3 with a segfault :( \$\endgroup\$
    – daniero
    Jul 6, 2015 at 18:41
  • 1
    \$\begingroup\$ My Python 3.5 works fine. \$\endgroup\$ Mar 10, 2016 at 16:19
  • \$\begingroup\$ for i = (x**2 for x in range(5)) I get this code returned <generator object <genexpr> at 0x03321690> \$\endgroup\$
    – george
    Jun 15, 2016 at 14:27
  • 12
    \$\begingroup\$ And if you're doing this in an expression, you can do [*'abcde']. \$\endgroup\$ Jun 5, 2017 at 0:58
  • 1
    \$\begingroup\$ @Jakob You might be using an older version of Python, because it only works as of version 3.5. \$\endgroup\$ Jun 30, 2018 at 2:10
67
\$\begingroup\$

Extended iterable unpacking ("Starred assignment", Python 3 only)

The best way to explain this is via an example:

>>> a,*b,c=range(5)
>>> a
0
>>> b
[1, 2, 3]
>>> c
4

We've already seen a use for this — turning an iterable into a list in Python 3:

a=list(range(10))
*a,=range(10)

Here are a few more uses.

Getting the last element from a list

a=L[-1]
*_,a=L

In some situations, this can also be used for getting the first element to save on parens:

a=(L+[1])[0]
a,*_=L+[1]

Assigning an empty list and other variables

a=1;b=2;c=[]
a,b,*c=1,2

Removing the first or last element of a non-empty list

_,*L=L
*L,_=L

These are shorter than the alternatives L=L[1:] and L.pop(). The result can also be saved to a different list.

Tips courtesy of @grc

\$\endgroup\$
3
  • \$\begingroup\$ Wow! I have written a=1;L=[] so many times. It's amazing that you can save chars on something so straightforward as this. \$\endgroup\$
    – xnor
    Dec 6, 2014 at 9:16
  • \$\begingroup\$ @xnor That one's thanks to grc. With only one other element it's not as good (a,*L=1,), but it still saves one char :) \$\endgroup\$
    – Sp3000
    Dec 6, 2014 at 9:20
  • \$\begingroup\$ don't forget you can also get both the first and last element of a list with a,*_,b=L \$\endgroup\$
    – Cyoce
    Mar 3, 2016 at 2:59
64
\$\begingroup\$

Instead of range(x), you can use the * operator on a list of anything, if you don't actually need to use the value of i:

for i in[1]*8:pass

as opposed to

for i in range(8):pass

If you need to do this more than twice, you could assign any iterable to a variable, and multiply that variable by the range you want:

r=1,
for i in r*8:pass
for i in r*1000:pass

Note: this is often longer than exec"pass;"*8, so this trick should only be used when that isn't an option.

\$\endgroup\$
8
  • 1
    \$\begingroup\$ @proudhaskeller I think the point of the line you removed was that "In addition to the obvious character savings you get because [1]*8 is shorter than range(8), you also get to save a space because for i in[... is legal while for i in range... is not". \$\endgroup\$ Aug 18, 2014 at 17:13
  • \$\begingroup\$ oh, right, i didn't understand that. fixed now \$\endgroup\$ Aug 18, 2014 at 17:30
  • 11
    \$\begingroup\$ exec"pass;"*8 is significantly shorter. \$\endgroup\$
    – DJMcMayhem
    May 19, 2016 at 19:23
  • 1
    \$\begingroup\$ if r=1, r*8 is 8, and you can't iterate through a number. I guess you meant r=[1] \$\endgroup\$
    – Miriam
    Mar 26, 2019 at 22:58
  • 3
    \$\begingroup\$ @ArtemisFowl, no it's ok as is, the comma after the 1 creates a tuple which is iterable. \$\endgroup\$
    – AndJM
    Jun 13, 2019 at 11:48
63
\$\begingroup\$

You can use the good old alien smiley face to reverse sequences:

[1, 2, 3, 4][::-1] # => [4, 3, 2, 1]
\$\endgroup\$
51
\$\begingroup\$

Use ~ to index from the back of a list

If L is a list, use L[~i] to get the i'th element from the back.

This is the i'th element of the reverse of L. The bit complement ~i equals -i-1, and so fixes the off-by-one error from L[-i].

\$\endgroup\$
1
  • 4
    \$\begingroup\$ Woah, that's so good I wanna use it in real code \$\endgroup\$ Aug 8, 2022 at 7:35
50
\$\begingroup\$

For ages it bothered me that I couldn't think of a short way to get the entire alphabet. If you use range enough that R=range is worth having in your program, then

[chr(i+97)for i in R(26)]

is shorter than the naive

'abcdefghijklmnopqrstuvwxyz'

, but otherwise it's longer by a single character. It haunted me that the clever one that required some knowledge of ascii values ended up being more verbose than just typing all the letters.

Until I saw this answer for My Daughter's Alphabet. I can't follow the edit history well enough to figure out if this genius was the work of the OP or if it was a suggestion by a commenter, but this is (I believe) the shortest way to create an iterable of the 26 letters in the Roman alphabet.

map(chr,range(97,123))

If case doesn't matter, you can strip off another character by using uppercase:

map(chr,range(65,91))

I use map way too much, I don't know how this never occurred to me.

\$\endgroup\$
6
  • 4
    \$\begingroup\$ Might use this in actual coding, I feel so stupid when hardcoding these things :') \$\endgroup\$ Jun 7, 2014 at 19:53
  • 51
    \$\begingroup\$ In actual coding, use string.lowercase -- that's what it's there for. \$\endgroup\$
    – Kevin S
    Aug 13, 2014 at 19:20
  • 2
    \$\begingroup\$ if you need both cases, the shortest way I know is filter(str.isalpha,map(chr,range(256))). It's just barely shorter than s=map(chr,range(256));s+=map(str.lower,s) \$\endgroup\$
    – quintopia
    Nov 18, 2015 at 3:40
  • \$\begingroup\$ @quintopia: Why 256 instead of 122 (ord('z'))? Aside from it being the same length... Also, if you need alphanumerics, replace str.isalpha in @quintopia's version with str.isalnum. (But if you only need one case, the whole 36-character string is no longer than filter(str.isalnum,map(chr,range(90))).) \$\endgroup\$ Dec 21, 2015 at 5:36
  • 3
    \$\begingroup\$ If you going to be unfair and use range as R, my version is shorter than your original one: '%c'*26%tuple(R(97,123)) (only 24 chars) if you spell range it is just as long as the alphabet -- uppercase version is shorter \$\endgroup\$
    – JBernardo
    Dec 21, 2017 at 20:26
47
\$\begingroup\$

set literals in Python2.7

You can write sets like this S={1,2,3} This also means you can check for membership using {e}&S instead of e in S which saves one character.

\$\endgroup\$
2
  • 9
    \$\begingroup\$ And this also saves the character in ifs as there is no spaces (if{e}&S:) \$\endgroup\$
    – Artyer
    Jun 4, 2017 at 18:43
  • 10
    \$\begingroup\$ Note that you can replace not in by {e}-S with that trick \$\endgroup\$
    – Kateba
    Nov 7, 2018 at 8:20
46
\$\begingroup\$

Choosing one of two numbers based on a condition

You already know to use the list selection [x,y][b] with a Boolean b for the ternary expression y if b else x. The variables x, y, and b can also be expressions, though note that both x and y are evaluated even when not selected.

Here's some potential optimizations when x and y are numbers.

  • [0,y][b] -> y*b
  • [1,y][b] -> y**b
  • [x,1][b] -> b or x
  • [x,x+1][b] -> x+b
  • [x,x-1][b] -> x-b
  • [1,-1][b] -> 1|-b
  • [x,~x][b] -> x^-b
  • [x,y][b] -> x+z*b (or y-z*b), where z=y-x.

You can also switch x and y if you can rewrite b to be its negation instead.

\$\endgroup\$
1
  • 2
    \$\begingroup\$ Bonus one: [-x,~x][b] -> -x-b \$\endgroup\$
    – TKirishima
    Jul 20, 2022 at 12:41
41
\$\begingroup\$

When you have two boolean values, a and b, if you want to find out if both a and b are true, use * instead of and:

if a and b: #7 chars

vs

if a*b: #3 chars

if either value is false, it will evaluate as 0 in that statement, and an integer value is only true if it is nonzero.

\$\endgroup\$
6
  • 14
    \$\begingroup\$ Or you could use &: a=b=False, a&b \$\endgroup\$ Apr 9, 2014 at 7:56
  • 4
    \$\begingroup\$ use + for or if you can guarantee a != -b \$\endgroup\$ Apr 23, 2015 at 1:18
  • 2
    \$\begingroup\$ | works in all situations. \$\endgroup\$ May 31, 2017 at 1:30
  • 1
    \$\begingroup\$ * instead of and/&& saves some bytes in many languages. \$\endgroup\$
    – wastl
    Apr 30, 2018 at 20:48
  • \$\begingroup\$ * unfortunately only works if what youre comparing are bool variables, otherwise you need parentheses, which cuts out any savings over using and, but I have found that >0< actually works as an and, and at least cuts out a byte or two of whitespace \$\endgroup\$
    – des54321
    Mar 8, 2022 at 23:45
39
\$\begingroup\$

Although python doesn't have switch statements, you can emulate them with dictionaries. For example, if you wanted a switch like this:

switch (a):
    case 1:
        runThisCode()
        break
    case 2:
        runThisOtherCode()
        break
    case 3:
        runThisOtherOtherCode()
        break

You could use if statements, or you could use this:

exec{1:"runThisCode()",2:"runThisOtherCode()",3:"runThisOtherOtherCode()"}[a]

or this:

{1:runThisCode,2:runThisOtherCode,3:runThisOtherOtherCode}[a]()

which is better if all code paths are functions with the same parameters.

To support a default value do this:

exec{1:"runThisCode()"}.get(a,"defaultCode()")

(or this:)

­­{1:runThisCode}.get(a,defaultCode)()

One other advantage of this is that if you do have redundancies, you could just add them after the end of the dictionary:

exec{'key1':'code','key2':'code'}[key]+';codeThatWillAlwaysExecute'

And if you just wanted to use a switch to return a value:

def getValue(key):
    if key=='blah':return 1
    if key=='foo':return 2
    if key=='bar':return 3
    return 4

You could just do this:

getValue=lambda key:{'blah':1,'foo':2,'bar',3}.get(key,4)
\$\endgroup\$
4
  • 3
    \$\begingroup\$ This is something i would deeply consider using in the wild. I do so miss my switch statements! +1 \$\endgroup\$
    – HalosGhost
    Nov 19, 2014 at 12:51
  • 2
    \$\begingroup\$ Although instead of using a dictionary with numbered keys in the first example, you should just use a list \$\endgroup\$
    – Cyoce
    Oct 13, 2016 at 19:25
  • 2
    \$\begingroup\$ If you have strings as keys, using dict(s1=v1,s2=v2,...,sn=vn) instead of {'s1':v1,'s2':v2,...,'sn':vn} saves 2*n-4 bytes and is better if n>=3 \$\endgroup\$
    – Kateba
    Oct 27, 2018 at 14:13
  • 2
    \$\begingroup\$ Note that the match statement was added in 3.10. python.org/dev/peps/pep-0636 \$\endgroup\$ Nov 3, 2021 at 15:43
37
\$\begingroup\$

PEP448 – Additional Unpacking Generalizations

With the release of Python 3.5, manipulation of lists, tuples, sets and dicts just got golfier.

Turning an iterable into a set/list

Compare the pairs:

set(T)
{*T}

list(T)
[*T]

tuple(T)
(*T,)

Much shorter! Note, however, that if you just want to convert something to a list and assign it to a variable, normal extended iterable unpacking is shorter:

L=[*T]
*L,=T

A similar syntax works for tuples:

T=*L,

which is like extended iterable unpacking, but with the asterisk and comma on the other side.

Joining lists/tuples

Unpacking is slightly shorter than concatenation if you need to append a list/tuple to both sides:

[1]+T+[2]
[1,*T,2]

(1,)+T+(2,)
(1,*T,2)

Printing the contents of multiple lists

This isn't limited to print, but it's definitely where most of the mileage will come from. PEP448 now allows for multiple unpacking, like so:

>>> T = (1, 2, 3)
>>> L = [4, 5, 6]
>>> print(*T,*L)
1 2 3 4 5 6

Updating multiple dictionary items

This probably won't happen very often, but the syntax can be used to save on updating dictionaries if you're updating at least three items:

d[0]=1;d[1]=3;d[2]=5
d={**d,0:1,1:3,2:5}

This basically negates any need for dict.update.

\$\endgroup\$
1
  • 8
    \$\begingroup\$ This looks worse than Perl, but it works... \$\endgroup\$
    – univalence
    Jun 25, 2016 at 5:52
36
\$\begingroup\$

loops up to 4 items may be better to supply a tuple instead of using range

for x in 0,1,2:

vs

for x in range(3):
\$\endgroup\$
36
\$\begingroup\$

Use += instead of append and extend

A.append(B)  

can be shortened to:

A+=B,

B, here creates a one-element tuple which can be used to extend A just like [B] in A+=[B].


A.extend(B)

can be shortened to:

A+=B
\$\endgroup\$
5
  • 5
    \$\begingroup\$ In many (but not all) cases, return 0 or return 1 is equivalent to return False or return True. \$\endgroup\$ Apr 16, 2014 at 8:30
  • 5
    \$\begingroup\$ (1) only works if you already know the number is negative, in which case you can save a further 2 characters by simply using a minus sign. -x rather than x*-1. --8.32 rather than -8.32*-1. Or just 8.32... \$\endgroup\$ Apr 20, 2014 at 22:56
  • \$\begingroup\$ Quoting the OP: Please post one tip per answer. \$\endgroup\$ Jun 23, 2014 at 13:32
  • \$\begingroup\$ Note that in A+=B B is a tuple. \$\endgroup\$ Jun 10, 2016 at 7:52
  • \$\begingroup\$ this can also be used with sets and dicts if you use |= in place of += \$\endgroup\$
    – des54321
    Mar 8, 2022 at 23:52
35
\$\begingroup\$

Ceil and Floor

If you ever want to get the rounded-up result for a division, much like you'd do with // for floor, you could use math.ceil(3/2) for 15 or the much shorter -(-3//2) for 8 bytes.

math.floor(n)   : 13 bytes+12 for import
n//1            : 4  bytes

math.ceil(n)    : 12 bytes+12 for import
-(-n//1)        : 8  bytes
\$\endgroup\$
3
  • 7
    \$\begingroup\$ This just saved me close to 20 bytes, thank you! \$\endgroup\$ Jan 13, 2016 at 19:27
  • 3
    \$\begingroup\$ sometimes you can get away with n//1+1 instead of ceil but it does mean ceil(n)=n+1 but it should work for all non integer values \$\endgroup\$
    – fejfo
    Dec 23, 2017 at 20:26
  • \$\begingroup\$ round(x) is (x+.5)//1, +1 byte but the latter starts with a (, and if x is a sum consisting of a constant it can be useful. \$\endgroup\$
    – DELETE_ME
    Apr 28, 2018 at 5:27
32
\$\begingroup\$

Change import * to import*


If you haven't heard, import* saves chars!

from math import*

is only 1 character longer than import math as m and you get to remove all instances of m.

Even one time use is a saver!

\$\endgroup\$
31
\$\begingroup\$

A one line function can be done with lambda:

def c(a):
  if a < 3: return a+10
  else: return a-5

can be converted to (note missing space 3and and 10or)

c=lambda a:a<3and a+10or a-5
\$\endgroup\$
4
  • 28
    \$\begingroup\$ or c=lambda a:a+[-5,10][a<3]. the and/or trick is more useful when you are depending on the shortcircuit behaviour \$\endgroup\$
    – gnibbler
    Feb 3, 2011 at 13:23
  • 4
    \$\begingroup\$ In your function, else: can be dropped as return stops the execution of the function, so everything that follows is only executed if the if condition failed, aka if the else condition is true. Thus else can safely be ommited. (Explained in details for the neophytes out there) \$\endgroup\$
    – jeromej
    Sep 14, 2015 at 8:15
  • \$\begingroup\$ c(-10) returns -15 while it should return 0 \$\endgroup\$
    – Anvit
    Dec 5, 2018 at 7:53
  • 2
    \$\begingroup\$ or c=lambda a:a-5+15*(a<3) \$\endgroup\$
    – JayXon
    Aug 11, 2019 at 8:59
30
\$\begingroup\$

Exploit Python 2 string representations

Python 2 lets you convert an object x to its string representation `x` at a cost of only 2 chars. Use this for tasks that are easier done on the object's string than the object itself.

Join characters

Given a list of characters l=['a','b','c'], one can produce ''.join(l) as `l`[2::5], which saves a byte.

The reason is that `l` is "['a', 'b', 'c']" (with spaces), so one can extract the letters with a list slice, starting that the second zero-indexed character a, and taking every fifth character from there. This doesn't work to join multi-character strings or escape characters represented like '\n'.

Concatenate digits

Similarly, given a non-empty list of digits like l=[0,3,5], one can concatenate them into a string '035' as `l`[1::3].

This saves doing something like map(str,l). Note that they must be single digits, and can't have floats like 1.0 mixed in. Also, this fails on the empty list, producing ].

Check for negatives

Now, for a non-string task. Suppose you have a list l of real numbers and want to test if it contains any negative numbers, producing a Boolean.

You can do

'-'in`l`

which checks for a negative sign in the string rep. This shorter than either of

any(x<0for x in l)
min(l+[0])<0   

For the second, min(l)<0 would fail on the empty list, so you have to hedge.

\$\endgroup\$
1
  • \$\begingroup\$ Concatenating single digits string slicing is also effective in Python 3, albeit less so: str(l)[2::5] is 12 bytes, versus 19 for ''.join(map(str,l)). An actual situation where this came up (where l was a generator statement, not a list) saved me just one byte... which is still worth it! \$\endgroup\$ Dec 21, 2015 at 2:08
30
\$\begingroup\$

Length tradeoff reference

I've think it would be useful to have a reference for the character count differences for some common alternative ways of doing things, so that I can know when to use which. I'll use _ to indicate an expression or piece of code.

Assign to a variable: +4

x=_;x
_

So, this breaks even if you

  • Use _ a second time: _ has length 5
  • Use _ a third time: _ has length 3

Assign variables separately: 0

x,y=a,b
x=a;y=b
  • -2 when a equals b for x=y=a

Expand lambda to function def: +7

lambda x:_
def f(x):return _
  • -2 for named functions
  • -1 if _ can touch on the left
  • -1 in Python 2 if can print rather than return
  • +1 for starred input *x

Generically, if you're def to save an expression to a variable used twice, this breaks even when the expression is length 12.

lambda x:g(123456789012,123456789012)
def f(x):s=123456789012;return g(s,s)

STDIN rather than function: +1

def f(x):_;print s
x=input();_;print s
  • -1 for line of code needed in _ if not single-line
  • +4 if raw_input needed in Python 2
  • -4 if input variable used only once
  • +1 if function must return rather than print in Python 2

Use exec rather than looping over range(n): +0

for i in range(n):_
i=0;exec"_;i+=1;"*n
  • +2 for Python 3 exec()
  • -4 if shifted range range(c,c+n) for single-char c
  • -5 when going backwards from n to 1 via range(n,0,-1)
  • -9 if index variable never used

Apply map manually in a loop: +0

for x in l:y=f(x);_
for y in map(f,l):_

Apply map manually in a list comprehension: +8

map(f,l)
[f(x)for x in l]
  • -12 when f must be written in the map as the lambda expression lambda x:f(x), causing overall 4 char loss.

Apply filter manually in a list comprehension: +11

filter(f,l)
[x for x in l if f(x)]
  • -1 if f(x) expression can touch on the left
  • -12 when f must be written in the filter as the lambda expression lambda x:f(x), causing overall 1 char loss.

Import versus import single-use: +4*

import _;_.f
from _ import*;f
  • Breaks even when _ has length 5
  • import _ as x;x.f is always worse except for multiple imports
  • __import__('_').f is also worse

Thanks to @Sp3000 for lots of suggestions and fixes.

\$\endgroup\$
1
  • 3
    \$\begingroup\$ The "exec" bit goes -9 if you don't need the index. \$\endgroup\$
    – T. Verron
    Oct 8, 2015 at 8:43
1
2 3 4 5 6

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.