246
\$\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\$
  • 27
    \$\begingroup\$ Oh, I can see a whole set of questions like this one coming for each language... \$\endgroup\$ – R. Martinho Fernandes Jan 28 '11 at 4:26
  • 4
    \$\begingroup\$ @Marthinho I agree. Just started a C++ equivalent. I don't think its a bad thing though, as long as we don't see the same answers re-posted across many of these question types. \$\endgroup\$ – marcog Jan 28 '11 at 12:28
  • 49
    \$\begingroup\$ Love the question but I have to keep telling myself "this is ONLY for fun NOT for production code" \$\endgroup\$ – Greg Guida Dec 21 '11 at 0:08
  • 2
    \$\begingroup\$ Shouldn't this question be a community wiki post? \$\endgroup\$ – dorukayhan May 29 '16 at 15:35
  • 3
    \$\begingroup\$ @dorukayhan Nope; it's a valid code-golf tips question, asking for tips on shortening python code for CG'ing purposes. Such questions are perfectly valid for the site, and none of these tags explicitly says that the question should be CW'd, unlike SO, which required CG challenges to be CW'd. Also, writing a good answer, and finding such tips always deserves something, that is taken away if the question is community wiki (rep). \$\endgroup\$ – Erik the Outgolfer Sep 9 '16 at 14:48

142 Answers 142

19
\$\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 is 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)

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\$
  • 2
    \$\begingroup\$ The "exec" bit goes -9 if you don't need the index. \$\endgroup\$ – T. Verron Oct 8 '15 at 8:43
16
\$\begingroup\$

Translating chars in a string

I've seen this situation pop up a few times, so I thought a tip would be good.

Suppose you have a string s and you want to translate some chars of s to other chars (think ROT-13 like ciphers). For a more concrete example, suppose we want to swap just the as and bs in a string, e.g.

"abacus" -> "babcus"

The naïve way to do this would be:

lambda s:s.replace('a','T').replace('b','a').replace('T','b')

Note how we need to introduce a temporary 'T' to get the swapping right.

With eval, we can shorten this a bit:

lambda s:eval("s"+".replace('%s','%s')"*3%tuple("aTbaTb"))

For this particular example, iterating char-by-char gives a slightly better solution (feel free to try it!). But even so, the winner is str.translate, which takes a dictionary of from: to code points:

# Note: 97 is 'a' and 98 is 'b'
lambda s:s.translate({97:98,98:97})

In Python 2 this only works for Unicode strings, so unfortunately the code here is slightly longer:

lambda s:(u''+s).translate({97:98,98:97})

Some important points which make str.translate so useful are:

  • It's easily extendable.
  • Any char not specified is untouched by default, e.g. the "cus" in "abacus" above.
  • The to part of the dictionary can actually be a (Unicode) string as well, e.g. {97:"XYZ"} (u"XYZ" in Python 2) would turn abacus -> XYZbXYZcus. It can also be None, but that doesn't save any bytes compared to "" or u"".
\$\endgroup\$
  • 1
    \$\begingroup\$ "ab".translate({97:None}) is longer than "ab".translate({97:""}). \$\endgroup\$ – T. Verron Oct 8 '15 at 8:16
  • \$\begingroup\$ @T.Verron Thanks for pointing that out - I'm not sure why I added that there tbh... \$\endgroup\$ – Sp3000 Oct 8 '15 at 8:24
  • \$\begingroup\$ The second example you gave can be shortened: replace "aTbaTb" with "aTb"*2. Also, you neglected to mention maketrans here, which could significantly shorten translations involving many characters, e.g.: from string import*;t="abcdefghijklmABCDEFGHIJKLMZYXWVUTSRQPONzyxwvutsrqpon";lambda s:s.translate(maketrans(t,t[::-1])) \$\endgroup\$ – quintopia Aug 18 '17 at 4:05
16
\$\begingroup\$

Arithmetic tricks

Here are some arithmetic tricks which are either shorter or are more useful due to precedence rules.

Assumptions                  Version 1        Version 2
-------------------------------------------------------------------
n >= 0 float                 n==0             0**n
n >= 0 integer               n==0             1>>n
n >  0 integer               n!=1             1%n
n >  0 integer, Python 2     n==1             1/n
\$\endgroup\$
  • 3
    \$\begingroup\$ 1//n could still be useful in Python 3 for n==1 since they have different precedence. \$\endgroup\$ – mbomb007 Jun 16 '15 at 21:11
16
\$\begingroup\$

Multiple statements can be put on one line separated by ;. This can save a lot of whitespace from indentation.

while foo(a):
 print a;a*=2

Or even better:

while foo(a):print a;a*=2
\$\endgroup\$
  • 11
    \$\begingroup\$ you can save one more by putting this one all on one line \$\endgroup\$ – gnibbler Feb 3 '11 at 13:16
  • 9
    \$\begingroup\$ Not always possible if you have other compound statements like ifs and whiles inside the while. \$\endgroup\$ – JPvdMerwe Oct 29 '11 at 20:23
16
\$\begingroup\$

Common helper functions

These are some golfed implementations of number theoretic functions that come up in challenges a lot. Many of these are due to xnor, especially the “Wilson’s theorem prime machines” of the form lambda n,i=1,p=1. The coprime/totient functions are Dennis’s (explanation here).

It is instructive to study what exactly these are doing, so that you can adapt them to your needs or roll them into another recursive function. That often ends up being shorter than pasting these directly into your solution as-is!

All of these assume n is a positive integer. The ones marked with an asterisk produce the wrong result if n = 1. Furthermore, these snippets assume Python 2. For Python 3, you might need to replace / by // here and there.

# Function                                                   Output of f(360)
#========================================================================================
f=lambda n,i=2:n/i*[0]and[f(n,i+1),[i]+f(n/i)][n%i<1]      # [2, 2, 2, 3, 3, 5] (slow!)
f=lambda n,i=2:n/i*[0]and f(n,i+1)if n%i else[i]+f(n/i)    # [2, 2, 2, 3, 3, 5]
f=lambda n,i=2:n/i*[0]and(n%i and f(n,i+1)or[i]+f(n/i))    # [2, 2, 2, 3, 3, 5]
f=lambda n,i=2:n<2and{1}or n%i and f(n,i+1)or{i}|f(n/i)    # {1, 2, 3, 5}
f=lambda n,i=2:n<2and{i}or n%i and f(n,i+1)or{i}|f(n/i,i)  #*{2, 3, 5}
f=lambda n,i=2:n/i and[f(n,i+1),i+f(n/i)][n%i<1]           # 2+2+2+3+3+5 (slow!)
f=lambda n,i=2:n/i and f(n,i+1)if n%i else i+f(n/i)        # 2+2+2+3+3+5
f=lambda n,i=2:n/i and(n%i and f(n,i+1)or i+f(n/i))        # 2+2+2+3+3+5
f=lambda n,i=1,p=1:n*[0]and p%i*[i]+f(n-p%i,i+1,p*i*i)     # first n primes
f=lambda n,i=1,p=1:n*[0]and p%i*[i]+f(n-1,i+1,p*i*i)       # primes <= n
f=lambda n,i=1,p=1:n/i and p%i*i+f(n,i+1,p*i*i)            # sum of primes <= n
f=lambda n,i=1,p=1:n/i and p%i+f(n,i+1,p*i*i)              # count primes <= n
f=lambda n,i=1,p=1:n and-~f(n-p%i,i+1,p*i*i)               # nth prime
f=lambda n:all(n%m for m in range(2,n))                    #*is n prime? (not recursive)
f=lambda n:1>>n or n*f(n-1)                                # factorial
f=lambda n:sum(k/n*k%n>n-2for k in range(n*n))             # totient phi(n) (not recursive)
f=lambda n:[k/n for k in range(n*n)if k/n*k%n==1]          # coprimes up to n (not recursive)

Try it online!

Additions and byte saves are very welcome!

\$\endgroup\$
  • \$\begingroup\$ #7 has a redundant space after ). (Also it has a variant similar to #2.) \$\endgroup\$ – Ørjan Johansen Jan 4 '18 at 0:16
  • \$\begingroup\$ Um ... can f=lambda n:1>>n or n*f(n-1) be lambda n:n<2or n*f(n-1), or am I going crazy? \$\endgroup\$ – Zacharý Nov 26 '18 at 21:09
  • \$\begingroup\$ That looks like an acceptable alternative whenever it's acceptable that f(0) == f(1) == True rather than 1. \$\endgroup\$ – Lynn Nov 26 '18 at 23:08
  • \$\begingroup\$ Oh, totally forgot about that >_<. \$\endgroup\$ – Zacharý Nov 28 '18 at 13:01
15
\$\begingroup\$

Printing a string without a trailing newline in Python 3

Suppose you have a string s, and need to print it without a trailing newline. The canonical way of doing this would be

print(s,end='')

However, if we look at the documentation for print, we can see that print takes in a variable number of objects as its first parameter, with "variable number" including zero. This means that we can do

print(end=s)

instead, for a saving of 3 bytes.

Note that this only works when s is a string, since otherwise the conversion to string would be too expensive:

print(1,end='')
print(end=str(1))

Thanks to @Reticality for this tip.

\$\endgroup\$
  • \$\begingroup\$ Note for `` commenters: `` does not work in Python 3 (This is a spam prevention comment) \$\endgroup\$ – Erik the Outgolfer Jun 25 '16 at 13:56
13
\$\begingroup\$

Assignment expressions

Assignment expressions are a powerful language feature introduced in Python 3.8 (TIO), currently in alpha. Use := to assign a variable inline as part of expression.

>>> (n:=2, n+1)
(2, 3)

You can save an expression to a variable inside a lambda, where assignments are not ordinarily allowed. Compare:

lambda s:(t:=s.strip())+t[::-1]
lambda s:s.strip()+s.strip()[::-1]
def f(s):t=s.strip();return t+t[::-1]

An assignment expression can be used in a comprehension to iteratively update a value, storing the result after each step in a list or other collection. This example computes a running sum by updating the running total t.

>>> t=0
>>> l=[1,2,3]
>>> print([t:=t+x for x in l])
[1, 3, 6]
>>> t
6

This can be done in a lambda with the initial value as an optional argument:

>>> f=lambda l,t=0:[t:=t+x for x in l]
>>> f([1,2,3])
[1, 3, 6]

This function is reusable: each call with start t back at 0.

\$\endgroup\$
  • 4
    \$\begingroup\$ This changes everything! \$\endgroup\$ – Lynn Feb 23 at 19:56
  • \$\begingroup\$ What is Python infamous for? Oh... nevermind. \$\endgroup\$ – Erik the Outgolfer Mar 1 at 22:42
12
\$\begingroup\$

Replace a value in a list

To replace every entry of value a with b in a list L, use:

map({a:b}.get,L,L)

For example,

L=[1,2,3,1,2,3]
a=2
b=3
print map({a:b}.get,L,L)

[1, 3, 3, 1, 3, 3]  #Output

In Python 3, this returns a map object rather than a list. The list entries can be any hashable values (ints, floats, strings, tuples, etc).

Here's how this works. A dictionary's get method takes a key and default value, and returns the dictionary's entry for that key, using the default value is the key is not present. This method is mapped method over each entry in L both as the key and the default value, which results in

[{a:b}.get(x,x) for x in L]

If x is a, then the dictionary transforms it to b, and otherwise, it defaults to itself. You can perform multiple replacements at the same time using a larger dictionary.

Credit to twobit on Anarchy Golf for exposing me to this trick.

\$\endgroup\$
12
\$\begingroup\$

Safely get the first element

You can check if a possibly-empty list l starts with a value x by doing

l[:1]==[x]

This gives False on an empty list, while l[0]==x gives an out-of-bounds error. Strings works similarly

s[:1]=='a'

In general, you can safely check the n'th element as

l[n:n+1]==[a]

or as l[n:][:1]==[a] when n is a long expression.

\$\endgroup\$
12
\$\begingroup\$

Reading multi-line input

In Python 3, the built-in function open underwent some changes. In particular, its first argument

file is either a string or bytes object giving the pathname (absolute or relative to the current working directory) of the file to be opened or an integer file descriptor of the file to be wrapped.

(source)

That means

open(0).read()

suffices to read all input from STDIN.

Try it online on Ideone.

\$\endgroup\$
  • \$\begingroup\$ ... except on Windows :/ (OSError: [WinError 6] The handle is invalid) \$\endgroup\$ – Sp3000 Feb 17 '16 at 7:14
12
\$\begingroup\$

Use .center in ASCII art

In drawing a symmetrical ASCII art, you can center-justify each line in a fixed width of spaces. For example, "<|>".center(7) gives ' <|> '. This can be shorter than computing how many spaces are needed to center it.

You can also pad with a different character by doing "<|>".center(7,'_')

\$\endgroup\$
  • 1
    \$\begingroup\$ This is actually really cool! \$\endgroup\$ – Rɪᴋᴇʀ Nov 5 '16 at 0:41
  • \$\begingroup\$ I wish JS had this function... \$\endgroup\$ – ETHproductions Nov 5 '16 at 0:57
  • \$\begingroup\$ Would f"{'<|>':^7}" not be shorter in python3.6+ for non-variable widths? Even more so when providing the character to center by, f"{'<|>':-^7}" vs "<|>".center(7,"-") \$\endgroup\$ – Sam Rockett Sep 13 at 10:48
11
\$\begingroup\$

Use os.read to read all input:

import os
s=os.read(0,1e9)

Which is shorter than

import sys
s=sys.stdin.read()

Note that this has a limitation on input length, but it's so ridiculously large I'd say we're safe from the angry mob.

\$\endgroup\$
  • 1
    \$\begingroup\$ raw_input() is shorter. If you need to read once, just spelling it out is shorter than the import + os.read; if more than once, assign it to a single-character value. \$\endgroup\$ – Wooble Apr 30 '11 at 3:17
  • 3
    \$\begingroup\$ @Wooble: why use raw_input for golfing? just use input. \$\endgroup\$ – Lie Ryan Sep 1 '11 at 13:54
  • 1
    \$\begingroup\$ @Lie: good point; although it depends on the problem specification whether having input evaluated would work, you can just stipulate that you're using python 3 (although then your print functions require a bit more space...) \$\endgroup\$ – Wooble Sep 1 '11 at 15:20
  • 2
    \$\begingroup\$ This doesn't work in python2.7/3. The number of bytes to read must be an integer. \$\endgroup\$ – Bakuriu Oct 19 '13 at 15:46
  • \$\begingroup\$ raw_input isn't available in Python 3 - use int(input()) instead. \$\endgroup\$ – Hosch250 Jan 9 '14 at 4:31
11
\$\begingroup\$

If you're doing somewhat more complex golfing that require something from the standard library to be used a lot, import x as y can save some space:

import itertools as i
i.groupby(...) # same as itertools.groupby
\$\endgroup\$
  • 21
    \$\begingroup\$ You can also do from itertools import *. This uses up 2 more characters, but you hit equal immediately by typing groupby instead of i.groupby. If you use groupby twice, you just saved 2 characters! \$\endgroup\$ – Jonathan Sternberg Jan 28 '11 at 7:52
  • 5
    \$\begingroup\$ from blah import* (without the last whitespace) is even shorter. \$\endgroup\$ – hallvabo Jan 28 '11 at 8:42
  • \$\begingroup\$ import* also gets you all the rest of the itertools. I wish it were shorter to use product/combinations/permutations though \$\endgroup\$ – gnibbler Feb 3 '11 at 13:14
  • 2
    \$\begingroup\$ Yeah, the names in itertools are just way too descriptive, makes me sad. \$\endgroup\$ – Clueless Aug 23 '11 at 21:50
11
\$\begingroup\$

If you represent boolean values as numbers you can save characters. This is especially true for using -1 as True.

Bitty conditionals work (Truth table):

a  b   &  |  ^ 
0  0   0  0  0
0  -1  0 -1 -1
-1 0   0 -1 -1
-1 -1 -1 -1  0

And ~ works as not:

 a ~a
 0 -1
-1  0

Even though the - for initializing -1 costs one character, this can easily save characters overall.

Compare:

while~a:

to:

while not a:
\$\endgroup\$
10
\$\begingroup\$

Use extended slicing to select one of two strings

>>> for x in-2,2:print"WoolrlledH"[::x]
... 
Hello
World

vs

>>> for x in 0,1:print["Hello","World"][x]
... 
Hello
World
\$\endgroup\$
10
\$\begingroup\$

Just found out two new things. First, input() can parse tuples, like 1, 2, 3 is equivalent to the tuple (1, 2, 3).

And if you need to convert a value to float, just multiply by 1.. Yes, 1. is valid syntax (At least in 2.6).

\$\endgroup\$
  • 6
    \$\begingroup\$ I think it's worth noting that in Python 2, input(x) is basically the same thing as eval(raw_input(x)). Unsafe to use in practice, but good for code golfing. \$\endgroup\$ – C0deH4cker Jan 24 '14 at 2:33
  • 2
    \$\begingroup\$ Quoting the OP: Please post one tip per answer. \$\endgroup\$ – nyuszika7h Jun 23 '14 at 13:56
  • 1
    \$\begingroup\$ To further explicate the point of this answer: in a challenge where input format is flexible, for instance if reading in a bunch of numerical arguments, you can write input() only once. E.g. a,b,c=input() will read in three comma-separated arguments and assign them to a, b, and c \$\endgroup\$ – quintopia Feb 1 '16 at 4:17
10
\$\begingroup\$

You only need to indent nested control structures:

def baz(i):
 if i==0:i=1;print i;bar()
 while i:i+=foo(i-1)
\$\endgroup\$
10
\$\begingroup\$

You can use default arguments of a function to save some indentation, since

def f(a,l=[1,2,3]):
 return sum(a==i for i in l)

is one byte shorter than

def f(a):
 l=[1,2,3]
 return sum(a==i for i in l)
\$\endgroup\$
  • 7
    \$\begingroup\$ A small caution: if a list that is passed in as an optional argument is modified in the function (like with pop or l[0] =1), that list will be changed in the outer scope too. \$\endgroup\$ – xnor Aug 22 '14 at 0:14
  • 1
    \$\begingroup\$ Also, this is only needed if the function body contains nested block statements; otherwise, you can just put everything on one line to avoid indentation. \$\endgroup\$ – DLosc Sep 26 '14 at 1:55
10
\$\begingroup\$

None arguments in Python builtins

map (Python 2 only)

Mapping with None in place of a function assumes the identity function instead. This allows it to be used as an alternative to itertools.izip_longest for zipping lists to the length of the longest list:

>>> L = [[1, 2], [3, 4, 5, 6], [7]]
>>> map(None,*L)
[(1, 3, 7), (2, 4, None), (None, 5, None), (None, 6, None)]

For visualisation (with . representing None):

1 2                1 3 7
3 4 5 6      ->    2 4 .
7                  . 5 .
                   . 6 .

filter

filter with None also assumes the identity function, thus removing falsy elements.

>>> L = ["", 1, 0, [5], [], None, (), (4, 2)]
>>> filter(None, L)
[1, [5], (4, 2)]

This is a bit better than a list comprehension:

filter(None,L)
[x for x in L if x]

However, as @KSab notes, if all elements are of the same type then there may be shorter alternatives, e.g. filter(str,L) if all elements are strings.

\$\endgroup\$
  • 1
    \$\begingroup\$ I had no idea you could do this! In the case of the filter, something similar I have done in the past is filter(str,L) if L is all strings or filter(int,L) if all ints which in some cases could be shorter. \$\endgroup\$ – KSab May 30 '15 at 9:18
10
\$\begingroup\$

Make a mutable matrix

If you want to make a 3*4 grid of zeroes, the natural expression M=[[0]*4]*3 gives an unpleasant surprise if you modify an entry:

>>> M=[[0]*4]*3
>>> M
[[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]]
>>> M[0][0]=1
>>> M
[[1, 0, 0, 0], [1, 0, 0, 0], [1, 0, 0, 0]]

Since each row is a copy of the same list by reference, modifying one row modifies all of them, which is usually not the behavior you want.

In Python 2, avoid this with the hack (19 chars):

M=eval(`[[0]*4]*3`)

Doing eval(`_`) converts to the string representation, then re-evaluates it, converting the object to the code of how it's displayed. In effect, it's doing copy.deepcopy.

If you're OK getting a tuple of lists, you can do (18 chars):

M=eval('[0]*4,'*3)

to get ([0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]). This lets you do M[0][0]=1 but not M[0]=[1,2,3,4]. It also works in Python 3.

\$\endgroup\$
  • \$\begingroup\$ what about python3? \$\endgroup\$ – micsthepick Aug 5 '15 at 9:22
  • \$\begingroup\$ @micsthepick Good question. You can use str for backticks as M=eval(str([[0]*3]*4)). Or, M=[3*[0]for _ in[0]*4] which is the same length. Maybe there's better. \$\endgroup\$ – xnor Aug 5 '15 at 23:19
10
\$\begingroup\$

map can take multiple iterable arguments and apply the function in parallel.

Instead of

a=[1,4,2,6,4]
b=[2,3,1,8,2]
map(lambda x,y:...,zip(a,b))

you can write

map(lambda x,y:...,a,b)
\$\endgroup\$
  • \$\begingroup\$ I didn't know this and it's super useful! \$\endgroup\$ – undergroundmonorail Feb 3 '15 at 14:59
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\$
9
\$\begingroup\$

Adding vectors

Python doesn't have a built-in way to do vector (component-wise) addition except with libraries. Say a and b are two equal-length lists of numbers you want to add. Instead of the list comprehension

c=[a[i]+b[i]for i in range(len(a))]

you can use

c=map(sum,zip(a,b))

This produces an annoying map object in Python 3, but it's shorter even if you have to convert to a list.

\$\endgroup\$
  • \$\begingroup\$ map(int.__add__,a,b) is more readable with 1 char longer. \$\endgroup\$ – est Aug 22 '17 at 1:44
  • 3
    \$\begingroup\$ @est But this is code golf... \$\endgroup\$ – user202729 Apr 17 '18 at 16:17
9
\$\begingroup\$

Avoid startswith

The string method startswith is too long. There are shorter ways to check if a string s starts with a prefix t of unknown length.

t<=s<t+'~'     #Requires a char bigger than any in s,t
s.find(t)==0
s[:len(t)]==t    
s.startswith(t)

The second one is well-suited for the truth/falsity of the negation.

if s.find(t):
\$\endgroup\$
  • \$\begingroup\$ s[:len(t)]==t is shorter if t has a constant length less than 100000. \$\endgroup\$ – Erik the Outgolfer Jun 15 '17 at 18:48
9
\$\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\$
9
\$\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\$
  • 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 '15 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 '15 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 '16 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 '16 at 13:32
9
\$\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\$
  • \$\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 '16 at 20:05
  • 1
    \$\begingroup\$ cough ...linux ftw... cough \$\endgroup\$ – cat Mar 4 '16 at 3:08
  • \$\begingroup\$ If you get MinGW, you could just build from source \$\endgroup\$ – cat Mar 4 '16 at 3:09
  • 1
    \$\begingroup\$ It's shorter in trivial cases, too. Compare ' '+str(n) (10 bytes) and f' {n}' (7 bytes). \$\endgroup\$ – Evpok Sep 8 '16 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 '16 at 6:04
9
\$\begingroup\$

Negating Booleans

So you have a Boolean... a real Boolean, not one represented as an integer. You have a condition where it needs to be negated, and you can't just go back and negate it where you got it (e.g. != instead of ==), maybe because you use it once straight and once negated.

Well, who says your Booleans aren't longing to be integers deep in their little hearts?

>>> False < 1
True
>>> True < 1
False

8 bytes, not counting the colon:

if not C:

6 bytes:

if C<1:

EDIT: 5 bytes, thanks to user202729 in the comments:

if~-C:

This works because:

>>> -False
0
>>> -True
-1
>>> ~-False
-1
>>> ~-True
0
\$\endgroup\$
9
\$\begingroup\$

Binomial coefficient

The binomial coefficient \$\binom{n}{k} \ = \frac{n!}{k!(n-k)!}\$ can be expressed arithmetically as

((2**n+1)**n>>n*k)%2**n

Try it online!

This works for \$n,k \geq 0\$, except for \$n=k=0\$ it gives \$0\$ rather than \$1\$. More generally, it works to use

(b+1)**n/b**k%b

(TIO), where \$b\$ is any value strictly greater than the result. The first expression uses \$b=2^n\$, which exceeds \$\binom{n}{k}\$ except for \$n=k=0\$.


Why does this work? Let's look at an example with b=1000. Then, for n=6, we have

(b+1)**n = 1001 ** 6 = 1006015020015006001

Note how triples of digits encode the binomial coefficients in the n=6 row of Pascal's triangle:

1   6  15  20  15   6   1
1 006 015 020 015 006 001

This works because the binomial coefficients are the coefficients of the polynomial

$$ (b+1)^n = \sum_{k=0}^n\binom{n}{k}b^k$$

and so can be read off as digits in base b, as long no binomial coefficient exceeds b which would cause regrouping.

We can extract a given triple of digits, say for \$\binom{6}{2}=15\$, by floor-dividing by 1000000 to delete the last 6 digits leaving 1006015020015, then take %1000 to extract the last triplet 015. More generally, doing /b**k%b extracts the k-th digit from the end zero-indexed in base b, that is the digit with multiplier b**k.

\$\endgroup\$
  • 2
    \$\begingroup\$ Why is this a tip for golfing in Python? Many languages have built-ins for exponentiation. \$\endgroup\$ – Peter Taylor Aug 10 '18 at 12:46
8
\$\begingroup\$

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

\$\endgroup\$
  • 1
    \$\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\$ – Josh Caswell May 1 '11 at 3:10
  • 3
    \$\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 '11 at 7:17
  • 3
    \$\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\$ – Fraxtil Apr 20 '13 at 2:04
  • \$\begingroup\$ @Fraxtil id can be applied on mostly everything, such as 1 or []. \$\endgroup\$ – user202729 Jul 5 '18 at 9:41

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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