238
\$\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\$
  • 26
    \$\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
  • 48
    \$\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

138 Answers 138

1
\$\begingroup\$

If you have multidimensional array of numbers and for instance need to count all numbers greater than n.

First flatten the array, then apply filter function to match condition:

l=[[1,[8,4,7,1],3],[5,[7],3,9],[7,3,9,[[[8]]]]]
n=5
flatten=lambda l: sum(map(flatten,l),[]) if isinstance(l,list) else [l]
len(filter(lambda x:x>n,flatten(l)))
\$\endgroup\$
1
\$\begingroup\$

You can generate pseudo random numbers using hash.

hash('V~')%10000

Will print 2014.

\$\endgroup\$
  • 1
    \$\begingroup\$ Prints 9454 for me. \$\endgroup\$ – nyuszika7h Jun 23 '14 at 13:07
  • 1
    \$\begingroup\$ Python 2.7.2 always returns 2014, but Python 3.4.0 returns more a more random number per session, like 6321, 3744, and 5566. \$\endgroup\$ – Cees Timmerman Aug 13 '14 at 8:34
  • \$\begingroup\$ Pretty similar to this. \$\endgroup\$ – user202729 Jul 5 '18 at 9:42
1
\$\begingroup\$

To find the all the indexes of a certain element in a list l, use

filter(lambda x:l[x]==element,range(len(l)))

To find the next index after a certain index:

l[:index].index(element)

To find the nth index:

list(filter(lambda x:l[x]==element,range(len(l))))[n]
\$\endgroup\$
1
\$\begingroup\$

Convert modules to lists

This will work for CPython (probably both 2 and 3), and lets you maybe shave a few bytes if you need to use a lot of different functions and classes with long names from the same module, but you aren't using any of them often enough to rename individually. You'll have to do some research first to figure out which magic numbers give you which functions. Example (rot13):

d=sorted
e=".__dict__.values()"
b=d(eval("__builtins__"+e))
s=d(eval("str"+e))
t=b[12]('string').maketrans
r=''.join(map(chr,range(65,91)))
w=r[13:]+r[:13]
l=t(r+s[4](r),w+s[4](w))
print s[28](b[53](),l)

Translated back to plain python, this is the same as:

t=__import__('string').maketrans
r=''.join(map(chr,range(65,91)))
w=r[13:]+r[:13]
l=t(r+r.lower(),w+w.lower())
print raw_input().translate(l)

which is obviously much shorter, but it should be clear how this methodology would eventually save bytes on much longer, more complicated programs that use more modules.

\$\endgroup\$
1
\$\begingroup\$

Use a list if you have multiple choices based on int

Say for example you have some output that will be 1, 0, or -1 and you need a different output for each case. You could do something like this:

print('0'if x==0else('1'if x>0else'-1'))

However, the better way is to use x as an index to a list like so:

print(['0','1','-1'][x])

which is 16 bytes shorter.

\$\endgroup\$
  • \$\begingroup\$ for numbers that won't be used in concatenation: print([0,1,-1][x]) \$\endgroup\$ – Felipe Nardi Batista Jul 12 '17 at 14:00
1
\$\begingroup\$

You can assign to a list inside of a for loop.

For example:

L=[1, 2, 3, 4, 6]
queue = [None]*len(L)
for e, queue[e] in enumerate(L):
    print("adding", queue[e], "to processing queue")

This can also be helpful if you need to switch the object you're assigning to.

class Foo:
    def __init__(self):
        self.x = None
a = Foo()
b = Foo()
for q, (lambda x: a if x%2==0 else b)(q).x in enumerate(range(10)):
    print(a.x, b.x)
\$\endgroup\$
1
\$\begingroup\$

Checking the length of a list

Empty      : `a==[]` but just checking if it's non empty and swapping the if and the else can be shorter 
Non-Empty  : `a` (assuming it is in a situation where it will be interpreted as a boolean)
len(a)>i   : `a>a[:i]` if the list is non-empty
\$\endgroup\$
  • 1
    \$\begingroup\$ Additionally because [] is falsy, if want to check if a list is not empty, you can simply do if a:. \$\endgroup\$ – Backerupper Dec 22 '17 at 21:08
  • 1
    \$\begingroup\$ The second one doesn't seem shorter? Although it can save a following space. But I think 1==len(a) also works for that. \$\endgroup\$ – Ørjan Johansen Dec 22 '17 at 21:44
  • \$\begingroup\$ The second one gives an error on the empty list. Assuming nonempty, a<a[:2] is shorter. \$\endgroup\$ – xnor Dec 23 '17 at 5:25
1
\$\begingroup\$

Fuse list comprehensions

You can write

[f(x)for x in l]+[g(x)for x in l]

As

sum([[f(x),g(x)]for x in l],[])

It gets even better with more comprehensions or if you have to take out more values

If you need to expand a list you can even turn l+[f(x)for x in l]+[g(x)for x in l] into sum([[f(x),g(x)]for x in l],l)

\$\endgroup\$
  • \$\begingroup\$ I don't think the two snippets return the same result. For example, [i for i in range(10)]+[-i for i in range(10)] returns [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, -1, -2, -3, -4, -5, -6, -7, -8, -9], while sum([[i,-i]for i in range(10)],[]) returns [0, 0, 1, -1, 2, -2, 3, -3, 4, -4, 5, -5, 6, -6, 7, -7, 8, -8, 9, -9]. \$\endgroup\$ – Erik the Outgolfer Jun 29 '18 at 22:29
1
\$\begingroup\$

Difference of two parallel expressions

Say you have a challenge to find the difference of some characteristic on two inputs. Your solution has the form lambda a,b:e(b)-e(a), where e is some long expression you've written. Repeating e twice is wasteful, so what do you do?

Here are templates sorted by length. Assume that e stands for a long expression, not one that's already defined as a function. Also assume inputs can be taken in either order.

31 bytes*

lambda*l:eval('e(%s)-'*2%l+'0')

*Requires that e only mentions its variable once. Assumes -e(x) negates the whole expression, otherwise requires parens like -(e(x)) for two more bytes.

34 bytes

f=lambda a,*b:e(a)-(b>()and f(*b))

36 bytes

lambda a,b:d(b)-d(a)
d=lambda x:e(x)

36 bytes

a,b=[e(x)for x in input()]
print b-a

37 bytes

r=0
for x in input():r=e(x)-r
print r

39 bytes

lambda*l:int.__sub__(*[e(x)for x in l])
\$\endgroup\$
1
\$\begingroup\$

Large hard coded numbers can be represented in larger bases, but there is a trade off. Higher bases only become worthwhile after a certain cutoff.

The only three bases you're likely to need to worry about are 10, 16, and 36. These are the cutoffs:

1000000000000 (13 bytes)                            -> 0xe8d4a51000 (12 bytes)
0x10000000000000000000000000000000000000 (40 bytes) -> int("9gmd8o3gbbaz3m2ydgtgwn9qo6xog",36) (39 bytes)
\$\endgroup\$
1
\$\begingroup\$

Shorter way to copy/clone a list

(not deep clone. For deep clone see this answer)

(credit to this answer)

a=x[:]
b=[*x]
c=x*1

Try it online!

\$\endgroup\$
1
\$\begingroup\$
  1. from lib import func as F

  2. from lib import*;F=func

  3. import lib;F=lib.func

#2 is better than #1 except in rare cases where something in lib clobbers another name that's important to you.

#3 uses lib twice, winning with short library names.

\$\endgroup\$
  • 1
    \$\begingroup\$ from lib import*;F=func is shorter than import lib;F=lib.func for six letter names; it is always shorter than from lib import func as F. \$\endgroup\$ – Dennis Dec 13 '18 at 4:03
  • \$\begingroup\$ @Dennis the import* trick is covered in a couple of tips farther up the list from here \$\endgroup\$ – Sparr Dec 13 '18 at 21:16
  • \$\begingroup\$ That doesn't prevent you from comparing import lib;F=lib.func to from lib import*;F=func in this answer. \$\endgroup\$ – Dennis Dec 13 '18 at 21:44
  • \$\begingroup\$ @Dennis you have a zillion rep on this site; you could edit it if you thought it would make the answer better \$\endgroup\$ – Sparr Dec 14 '18 at 5:02
0
\$\begingroup\$

1 or 0 can act as boolean operators in Python:

func = lambda x:1 if x//2==x/2 else 0
while 1:
    if func(n):
         print('Hello')
    else:
         exit()

Which is 10 characters shorter than:

func = lambda x:True if x//2==x/2 else False
while True:
    if func(n):
         print('Hello')
    else:
         exit()
\$\endgroup\$
  • 1
    \$\begingroup\$ Isn't this pointless in func? You could just have func= lambda x:x//2-x/2 and reverse the consequences of the if (the subtraction will give 0 if they are the same, +/-1 if they are different). It might be better to point out that any python type that has a definition for __bool__ or __non_zero__ could be used instead of a boolean. \$\endgroup\$ – FryAmTheEggman Oct 23 '14 at 20:24
  • 2
    \$\begingroup\$ Here's a tip for golfing everywhere: don't return booleans with a conditional! return True if condition else False can always be simplified to return condition, or if the condition isn't a boolean and you need a boolean, use return bool(condition) or return condition!=0 if it's a number. \$\endgroup\$ – Cyoce Feb 17 '16 at 8:02
  • \$\begingroup\$ @Cyoce This deserves to have many votes. \$\endgroup\$ – Erik the Outgolfer Jun 25 '16 at 6:43
  • \$\begingroup\$ Also, the while True is unnecessary. It could be shortened to while func(n):print('Hello')\nexit() where \n is the new line character \$\endgroup\$ – Cyoce Jul 7 '16 at 2:38
0
\$\begingroup\$

Not read all the answers but you can instead of

if x==3:
    print "yes"
else:
    print "no"

use

print "yes" if x==3 else "no"
\$\endgroup\$
  • 4
    \$\begingroup\$ We already have this in shorter form. \$\endgroup\$ – xnor Apr 23 '15 at 8:14
  • \$\begingroup\$ this is specific to print function as asked in the question I guess \$\endgroup\$ – user3340707 Apr 23 '15 at 8:16
  • 5
    \$\begingroup\$ print"yneos"[x!=3::2] based on this answer \$\endgroup\$ – Jakube Apr 23 '15 at 8:28
  • \$\begingroup\$ @xnor That isn't always applicable, for instance if using recursion. \$\endgroup\$ – Ogaday Feb 16 '16 at 12:25
0
\$\begingroup\$

Slicing can assign:

Converting BGR pixels array to RGB:

l = len(pixels)
for idx in range(0, l - 2, 3):
    pixels[idx + 2], pixels[idx] = pixels[idx], pixels[idx + 2]

Becomes:

l = len(pixels)
pixels[2:l:3], pixels[0:l:3] = pixels[0:l:3], pixels[2:l:3]

It's not just shorter. It's 5 times faster. Except on pypy, where the first version is 2x times faster :)

\$\endgroup\$
  • \$\begingroup\$ You don't really need to get the length, pixels[2::3] should work too \$\endgroup\$ – wastl Jun 2 '18 at 19:26
0
\$\begingroup\$

Tricks with dicts:

# create a dict from iterable
d = {k: None for k in iterable}
# merge 2 dicts
d.merge(d1)
d.merge(d2)
# access a key, if it doesn't exist set a default value and return it
if 'foo' not in d:
    res = d['foo'] = 'bar'
    res = 'bar
else:
    res = d['foo']

Becomes:

d = {**dict.fromkeys(iterable), **d1, **d2}
res = d.setdefault('foo', 'bar')

Or if you need repeated access:

import collections as c
d = c.ChainMap(dict.fromkeys(iterable), d1, d2, {'foo': 'bar'})
res = d['foo']
\$\endgroup\$
0
\$\begingroup\$

In Python,

True == 1   # true
False == 0  # true

So,

(a<b)*2-1

returns 1 if b is larger than a. If not, returns -1.

More golfing,

-(a>b)|1

returns exactly same value as mentioned above.

Useful when modify iterator index by comparable values.

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

Shortening a%b==a if b has a constant sign

For two expressions a and b, where each one results in an int (or long in Python 2) or float, you can replace these:

a%b==a

a==a%b

with these, if b is positive:

0<=a<b

b>a>=0

or these, if b is negative:

b<a<=0

0>=a>b

I'm presenting two expressions for each case because sometimes you may want to use one over the other to eliminate a space to separate expression b from an adjacent token. They both have the same precedence, so you're not usually going to need to surround the second expression with () if you don't need to do so to the first one.

This is useful if expression a is more than 1 byte long or b is negative, because it removes one occurrence of a from the expression. If \$a,b\$ are the lengths of expressions a and b respectively, and \$l\$ is the length of the original expression, the resulting expression will be \$l-a+1\$ bytes long. Note that this method is always going to be shorter than assigning expression a to a separate variable.

Example

For example,

(a+b)%c==a+b

can be replaced with

0<=a+b<c

for a total saving of 4 bytes.

Proof

Let's define the operator \$x\mathbin\%y\$ for \$x,y\in\mathbb Q\$.

Every rational number \$a\$ can be represented as \$a=bq+r\$, where \$q\in\mathbb Z,0\le r<|b|\$. Therefore, we can define an operator \$a\mathbin\%b\$, where the result has the same sign as \$b\$:

$$a=bq+r,q\in\mathbb Z,0\le r<|b|\\a\mathbin\%b=\begin{cases}\begin{align}r\quad b>0\\-r\quad b<0\end{align}\end{cases}$$

This represents the % operator in Python, which calculates the remainder of the division of two numbers. a % b is the same as abs(a) % b, and the result has the same sign as the divisor, b. For the \$a\mathbin\%b\$ operator, this equality holds:

$$(a\pm b)\mathbin\%b=a\mathbin\%b$$

Proof:

$$a=bq+r\leftrightarrow a\pm b=bq+r\pm b=(bq\pm b)+r=b(q\pm1)+r$$

Moreover, for \$b>0\$, we have:

$$a\mathbin\%b=a\leftrightarrow r=a\leftrightarrow0\le a<b$$

Proof for \$r=a\leftarrow0\le a<b\$:

$$0\le a<b\leftrightarrow0\le bq+r<b\leftrightarrow bq=0\leftrightarrow a=r$$

Similarly, for \$b<0\$, we have \$b<a\le0\$.

Therefore, \$a\mathbin\%b=a\leftrightarrow\begin{cases}\begin{align}0\le a<b\quad b>0\\b<a\le0\quad b<0\end{align}\end{cases}\$, or, equivalently, \$(0\le a<b)\lor(b<a\le0)\$.

\$\endgroup\$

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.