334
\$\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\$
5
  • 3
    \$\begingroup\$ Use Python 2 for golfing not 3 \$\endgroup\$ Commented Aug 9, 2017 at 15:04
  • 10
    \$\begingroup\$ @Chris_Rands That simply does not universally hold, as there are cases in which Python 3 allows for shorter submissions. \$\endgroup\$ Commented Jul 15, 2018 at 14:36
  • 7
    \$\begingroup\$ @JonathanFrech Especially the new := operator in 3.8 \$\endgroup\$
    – MilkyWay90
    Commented Mar 17, 2019 at 17:31
  • 2
    \$\begingroup\$ Related: code golf - Tips for golfing with numpy, scipy, or pylab - Code Golf Stack Exchange \$\endgroup\$
    – user202729
    Commented Feb 2, 2021 at 11:54
  • \$\begingroup\$ I would love to upvote, but the vote count is exactly 333! +1 to this question \$\endgroup\$
    – Klumpy7
    Commented Oct 22 at 23:44

181 Answers 181

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

Long function names

Here's a trick to shorten long function names. The basic idea is to fetch the name by using dir(). This would return the name as a string, so we must follow it with an eval to make it usable. Below is an exaggerated example from the itertools module:

from itertools import*

combinations_with_replacement('ABC',2)
eval(dir()[7])('ABC',2)

In the minimal case, if the function name is 15 bytes or longer, there's a chance that it can still save bytes. This is assuming, that the name can be accessed by dir with a single-digit index:

???????????????()
eval(dir()[?])()
\$\endgroup\$
2
  • \$\begingroup\$ In your example, there are two unmatched parantheses. \$\endgroup\$
    – orthoplex
    Commented Aug 17, 2021 at 14:03
  • \$\begingroup\$ @orthoplex Oh oops, thanks for noticing. \$\endgroup\$ Commented Aug 17, 2021 at 18:29
6
\$\begingroup\$

Use the walrus operator := in list comprehension to store the previous value of the iterable

Instead of the convoluted

f=[s[i]for i in range(len(s))if s[i-1]!=s[i]]

You can use

e=""
f=[c for c in s if e!=(e:=c)]

Which saves 11 bytes in this example. Care needs to be taken that e is reassigned after being evaluted. It also needs to be initialised before the list, which is quite easy to do with default function arguments

\$\endgroup\$
5
\$\begingroup\$

Sometimes you need convert boolean expression into integer (0/1) Simple use this Boolean (in examples below c > 0) in ariphmetic

a=b+(c>0)
a+=c>0
a=sum(c>0 for c in b) # space in "0 for" may be omitted

And sometimes you need simple convert boolean to int (for example for printing or convert to binary string). In programm you may use some variants

1 if c>0 else 0
c>0and 1or 0
(0,1)[c>0]
int(c>0)

but shortest way is

+(c>0)
\$\endgroup\$
1
  • 9
    \$\begingroup\$ I find +c being shorter. E.g. let c be a boolean, then 4+c is 5 if c is True else 4. \$\endgroup\$
    – MyGGaN
    Commented Feb 25, 2014 at 23:41
5
\$\begingroup\$

Abusing try/except blocks can sometimes save characters, especially for exiting out of nested loops or list comprehensions. This:

for c in s:
 for i in l:
  q=ord(c)==i
  if q:print i,c;break
 if q:break

... can become this, saving 3 characters:

try:
 for c in s:
  for i in l:
   if ord(c)==i:print i,c;1/0
except:0

... which in this particular instance can be compressed even further using list comprehensions:

try:[1/(ord(c)-i)for c in s for i in l]
except:print i,c

For an example, see e.g. https://codegolf.stackexchange.com/a/36492/16766.

\$\endgroup\$
5
\$\begingroup\$

When using Python 3, for your final print statement, use exit to save one char (note: this prints to STDERR, so you might not be able to use this):

print('x')
exit('x')

exit even adds a trailing newline. There is one caveat, however: exit(some_integer) will not print.

\$\endgroup\$
4
  • \$\begingroup\$ Does this print to STDOUT or STDERR? This sounds like it would be the latter, but in most challenges only the former is allowed. \$\endgroup\$
    – Sp3000
    Commented Apr 19, 2015 at 4:24
  • \$\begingroup\$ @Sp3000 I don't know... how can I tell? \$\endgroup\$
    – Justin
    Commented Apr 19, 2015 at 5:18
  • \$\begingroup\$ Seems to be stderr \$\endgroup\$
    – Sp3000
    Commented Apr 19, 2015 at 5:32
  • 1
    \$\begingroup\$ exit(some_integer) is valid because exit code is a default output form. \$\endgroup\$ Commented Jun 23, 2020 at 15:20
5
\$\begingroup\$

Take multi-line input

Use list(iter(input,eof)) to take multi-line input. eof can be any string that you want to stop taking input on if you see it. An example would be eof = ''. The python 2 version is list(iter(raw_input,eof)), however you may want to use sys.stdin.readlines() instead if you have already imported sys.

\$\endgroup\$
6
  • \$\begingroup\$ list(iter(input,eof)) is shorter by one, you don't need that space. Also worth noting that this is only for Python 3 - Python 2 is 4 bytes longer because raw_input. \$\endgroup\$
    – user45941
    Commented Nov 30, 2015 at 3:15
  • \$\begingroup\$ Is shorter than what? \$\endgroup\$
    – J Atkin
    Commented Nov 30, 2015 at 3:33
  • \$\begingroup\$ Than what you have posted, because it has an extraneous space. \$\endgroup\$
    – user45941
    Commented Nov 30, 2015 at 3:34
  • \$\begingroup\$ Oh, I see. I assume the reader would remove the space though. I will edit it out now. \$\endgroup\$
    – J Atkin
    Commented Nov 30, 2015 at 3:38
  • \$\begingroup\$ read(0) returns an iterable of all lines of input on many systems (in Python 3.9, not sure when it was added). \$\endgroup\$ Commented May 16, 2021 at 19:24
5
\$\begingroup\$

Use slicing + assignment instead of mutator methods

l.insert(x,y) # before
l[x:x]=y,     # after

l.reverse()   # before
l[::-1]=l     # after

l.append(x)   # before
l[L:]=x,      # after (where L is any integer >= len(l))

l[:]=x        # set the contents of l to the contents of x

EDIT: thanks to @quintopia for pointing this out, these are statements, not expressions. The mutator methods are void functions, so they are expressions which evaluate to None. This means that things like [l.reverse() for x in L] and condition or l.reverse() are valid, whereas [l[::-1]=l for x in L] and condition or l[::-1]=l are not.

\$\endgroup\$
5
  • \$\begingroup\$ Note that this cannot always be done. For instance, when using or or and as conditionals, assignment is not allowed, but append and the like are. \$\endgroup\$
    – quintopia
    Commented Jan 13, 2016 at 5:34
  • \$\begingroup\$ l[:]=x is a bit unpythonic - same number of bytes, but I reckon l=x[:] is better practice. Also, [::-1] is listed as a tip here, L[:x]+=y, is better for the general case where x might be an expression (but same byte count if it's just x), and l+=x, is listed here for append. \$\endgroup\$
    – Sp3000
    Commented Feb 17, 2016 at 7:10
  • \$\begingroup\$ @Sp3000 l[:]=x and l=x[:] do different things. The former mutates the list itself, i.e. all references to that list, whereas the latter sets the variable l to a copy of x \$\endgroup\$
    – Cyoce
    Commented Feb 17, 2016 at 7:47
  • \$\begingroup\$ @Cyoce Oh, nevermind then - my eyes are going to have to get used to seeing that :P \$\endgroup\$
    – Sp3000
    Commented Feb 17, 2016 at 8:01
  • \$\begingroup\$ l+=[x] is shorter than l[L:]=x, \$\endgroup\$
    – TKirishima
    Commented Jun 28, 2022 at 14:19
5
\$\begingroup\$

Find the n'th number meeting a condition

Many sequence challenges ask you to find the n'th number in a sequence of increasing positive integers. When you have a expression p(i) that checks membership, you can do this with the recursive function:

f=lambda n,i=1:n and-~f(n-p(i),i+1)

Note that expression p(i) must give 0 or 1, not just Falsey or Truthy. The outputs are one-indexed, so say for the sequence of primes, it would give

f(1) = 2
f(2) = 3
...

For 0-indexed outputs, shift the base case

f=lambda n,i=1:n+1and-~f(n-p(i),i+1)

The recursive function f=lambda n,i=1:n and-~f(n-p(i),i+1) works by decrementing the required count n each time it gets a hit, and incrementing the output value each time for each value it checks. It might seem weird to redundantly track i, but it's longer to do:

f=lambda n,i=1:n and f(n-p(i),i+1)or~-i

Also compare the natural list strategy (zero-indexed here)

lambda n:[i for i in range(n*n)if p(i)][n]

(You might need a larger bound than n*n.)

\$\endgroup\$
5
\$\begingroup\$

Format() works on dates:

"We are in year: %s" % (date.strftime('%y'))

Becomes:

"We are in year: {:%y}".format(date)

Or even better with f-strings:

f"We are in year: {date:%y}"
\$\endgroup\$
5
\$\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\$
5
\$\begingroup\$

Formatting a Matrix

I've seen this code to get a character matrix (2D array) as a string.

'\n'.join(''.join(i)for i in M)

It's shorter to use a map instead:

'\n'.join(map(''.join,M))

If you're printing the result, it's shortest to use a for loop:

print('\n'.join(map(''.join,M)))
for i in M:print(*i,sep='')      # -5 bytes

If you're using Python 2, you can't use the print trick, but you can still use the for loop:

for i in M:print(''.join(i))     # -3 bytes
\$\endgroup\$
5
\$\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\$
1
  • 3
    \$\begingroup\$ a<b or-1 also works \$\endgroup\$ Commented Oct 17, 2021 at 18:00
5
\$\begingroup\$

Math built-ins in 3.8

In addition to the famous walrus operator, Python 3.8 introduces useful new math features.

Modular inverse

The modular-power built-in pow(base, exp, mod) can now compute the modular inverse using exp=-1. This requires that base and mod are relatively prime integers.

>>> pow(38, -1, 97)
23
>>> 23 * 38 % 97 == 1
True

exp may be any negative integer, which lets you compute modular powers of the modular inverse.


The math library has useful new functions for combinatorics and distances. Access them with import math. Or, write from math import* to import them without the math., which is worth it if you write more than one call.

Combinatorics

  • math.comb(n,k): The binomial coefficient n choose k, which equals n! / (k! * (n - k)!).
  • math.perm(n,k): The number of ordered choices of k elements from n, which is n! / (n - k)!. Calling just math.perm(n) gives n!, useful as a synonym for math.factorial(n).
  • math.prod(l): The product of the elements of a list or iterable l, analogous to sum.

Distances and square roots

  • math.dist(p,q): The Euclidean distance between two points p and q. The inputs p and q must be lists or iterables that are the same length.
  • math.hypot(*p): The Euclidean distance from a point p to zero. Now takes any number of arguments; before it took only two. For some reason, arguments still must be splatted like math.hypot(1,2,3) rather than math.hypot([1,2,3]).
  • math.isqrt(n): The integer square root, that is the floor of the square root of n. Requires than n is a non-negative integer. Usually n**.5//1 suffices instead, but this gives exact integer output rather than a float.
\$\endgroup\$
5
\$\begingroup\$

Using exec to remove repeated print

This is not quite often applicable, but can save some bytes, especially in ASCII art. Take the following code, which prints the flag where n=4.

# 43 bytes                  |  ***
n=input()                   |  **
while~-n:n-=1;print'*'*n    |  *
print'|'                    |  |

Notice that we repeat print twice. We can remove this using exec in the following code, saving 3 bytes.

# 40 bytes
n=input()
exec"'*'*n;n-=1;print"*n+"'|'"
\$\endgroup\$
5
\$\begingroup\$

Printing the elements of a list, with spaces

Suppose we have to print a list as a string with spaces, like square of numbers upto 10. Then,

Instead of,

print(' '.join(str(i**2)for i in range(11))) # 44 chars

We can do,

print(*(i**2for i in range(11))) # 32 chars
\$\endgroup\$
1
  • \$\begingroup\$ print(*[_*_ for _ in range(11)]) is same 32 bytes but less cluttered by not having 2f clumped up \$\endgroup\$ Commented May 28 at 7:34
5
\$\begingroup\$

Shorten while 1

It is possible to shorten a while 1: by replacing the 1 with an existing expression inside of the loop body. Common ways to make use of this in Python 3 include embedding

  • a print() statement:
while 1:print(x);...
# vs
while[print(x)]:...
  • a chained expression:
while 1:x>1==print(x);...
# vs
while[x>1==print(x)]:...
  • a walrus operator (Python 3.8 and above):
while 1:x=1;...
while x:=1:...

In general, any expression (one that returns a value), can be used as the while condition to save a byte or two. Often it's necessary to wrap the expression in [] to ensure it maintains a truthy value.

\$\endgroup\$
5
\$\begingroup\$

Special characters in string literals

When you write a quoted literal string in your code, perhaps to compress a big blob of data, certain characters must be replaced by a two-character escape sequence.

Null byte \0 (ASCII 0)

The null byte can't be present verbatim in Python code. Write it as \0.

Newline \n (ASCII 10)

An actual newline \n can't appear in a string literal because the Python lexer will read it and think the line has ended without closing the initial quote. But, a triple-quoted string like '''stuff''' or """stuff""" may contain newlines. This saves bytes with 5+ newlines, which can be useful in ASCII art.

Carriage return \r (ASCII 13)

A carriage return always needs escaping as \r. Though allowed in a triple-quoted string, a literal \r is misread as a newline \n.

Quotes " (ASCII 34) and ' (ASCII 39)

A single-quoted string can contain double quotes like '"', but must escape single-quotes like '\'', and vice-versa for double-quoted strings. Choose whichever option leads to less escaping.

A triple-quoted string is OK with unescaped quotes of the same type like '''it's'''. But, having quotes at the start and end or three-in-a-row can confuse the lexer into giving a SyntaxError.

Backslash \ (ASCII 92)

A backslash can be escaped as \\. But, Python allows just writing \ if it's followed by a character that can't make it an escape sequence. This is any character not in abfnrtvx0123456789"'\ or literal newline \n.

You can use a r-prefixed raw string like r'\n' to ignore most escape sequences, so r'\n' is just a backslash followed by a letter n and r'\\' is two backslashes. Quote characters still get escaped but don't consume the backslash, so r'\'' is backslash then single-quote. This can cause complications: r'\', r'\\\', and r'\\'' all give SyntaxError.


For reference, the special characters are ASCII values [0, 10, 13, 39, 34, 92]. When doing compression, you might tweak your method to avoid hitting these values.

Some weird characters can be written verbatim without escaping, such as:

  • bell \a (ASCII 7)
  • backspace \b (ASCII 8)
  • tab \t (ASCII 9)
  • vertical tab (ASCII 11)
  • form feed \f (ASCII 12)
  • DEL (ASCII 127)

StackExchange posts won't render these, but Python clients and TIO should handle them fine. Here they are for copy-pasting.

Characters with ASCII codes 128 and up can't be included in Python 2 code without a extra line declaring an encoding like this. Python 3, though, handles them fine, including multibyte Unicode characters.

\$\endgroup\$
5
\$\begingroup\$

List comprehension.

shortList = []
for x in range(10):
    shortList += [x * 2]

can be shortened into

shortList = [x*2 for x in range(10)]

Or even shorter:

shortList = range(0,20,2)
\$\endgroup\$
7
  • \$\begingroup\$ Can even be shortened (by a character) to [x**2for x in range(10)] \$\endgroup\$
    – TerryA
    Commented May 13, 2013 at 11:31
  • 1
    \$\begingroup\$ @Haidro I think you mean [x*2for x in range(10)] \$\endgroup\$
    – Timtech
    Commented Dec 8, 2013 at 17:58
  • \$\begingroup\$ @Timtech You are right :) \$\endgroup\$
    – TerryA
    Commented Dec 8, 2013 at 19:13
  • \$\begingroup\$ Note that the final trick works only in Python 2. In Python 3 you must use shortList = list(range(0, 20, 2)) \$\endgroup\$ Commented Dec 7, 2022 at 8:50
  • \$\begingroup\$ @UndoneStudios: *shortList,=range(0,20,2) or as an expression, [*range(0,20,2)] will make it a list with fewer characters than calling list() (constructor call adds six, [*...] adds three over just range(0,20,2), and *shortList,=range(0,20,2) only adds two). \$\endgroup\$ Commented Feb 10, 2023 at 20:55
4
\$\begingroup\$

Be aware of all, any and map:

if isdigit(a) and isdigit(b) and isdigit(c)
if all(map(isdigit,[a,b,c]))
\$\endgroup\$
2
  • 4
    \$\begingroup\$ filter(function, iterable) returns a list of all the elements of iterable` for which function is a True-y value and a non-empty list is True-y, so this can be shortened further to if filter(isdigit,[a,b,c]) \$\endgroup\$ Commented Apr 16, 2014 at 8:33
  • 3
    \$\begingroup\$ Over a year later, I'm reading this thread again and I'm embarrassed about my previous comment. if filter(isdigit,[a,b,c]) is not equivalent to the code in the answer; but it would be if @moose used isdigit(a) or... and if any(.... \$\endgroup\$ Commented Apr 21, 2015 at 13:36
4
\$\begingroup\$

If you need to import a lot of modules you can reassign __import__ to something shorter, this also has the advantage of being able to name imports anything you want.

i=__import__;s=i('string');x=i('itertools');
\$\endgroup\$
3
  • 4
    \$\begingroup\$ I've rarely found this to be actually useful. Generally import string,itertools, import string,itertools as M, from itertools import* and other variants tend to be shorter... \$\endgroup\$
    – Sp3000
    Commented Jul 25, 2015 at 9:05
  • 1
    \$\begingroup\$ s,i=map(__import__,['string','itertools']) is shorter than your example, but still longer than import string as s,itertools as i \$\endgroup\$ Commented Jul 12, 2017 at 14:44
  • 1
    \$\begingroup\$ @FelipeNardiBatista: The map approach does scale better if you are using a single space-separated string and splitting it. But to pull ahead of the alternatives, you'd need a lot of imports. As in s,i,c,p,f,b,d=map(__import__,'string itertools collections subprocess fractions bisect datetime'.split()). So probably not useful in most golfing scenarios. :-) \$\endgroup\$ Commented Feb 24, 2023 at 2:19
4
\$\begingroup\$

Alternatives to builtin string functions

str.capitalize for single words

Use str.title instead for single words. The difference between the two functions is that capitalize only capitalises the first word, while title capitalises all words:

>>> "the quick brown fox".capitalize()
'The quick brown fox'
>>> "the quick brown fox".title()
'The Quick Brown Fox'

str.index

str.find is almost always better, and even returns -1 if the substring is not present rather than throwing an exception.

str.startswith

See this tip by @xnor.

str.splitlines

str.split is shorter:

s.splitlines()
s.split('\n')

However, str.splitlines may be useful if you need to preserve trailing newlines, which can be done by passing 1 as the keepends argument.

\$\endgroup\$
1
  • \$\begingroup\$ splitlines also avoids a trailing blank line when the string ends in a newline. 'foo\nbar\n'.split('\n') produces ['foo', 'bar', ''] (len 3, with trailing empty string), while 'foo\nbar\n'.splitlines() produces ['foo', 'bar'] (len 2, no trailing empty string), which can save trouble in some cases. \$\endgroup\$ Commented Feb 24, 2023 at 2:23
4
\$\begingroup\$

Avoid the repeat argument of itertools.product

As @T.Verron points out, in most cases (e.g. ranges and lists), you can instead do

product(*[x]*n)

However, even if you have a generator which you can only use once, like a Python 3 map, the repeat argument is still unnecessary. In such a case you can use itertools.tee:

product(x,repeat=n)
product(*tee(x,n))

For n = 2 you don't even need to include n, since 2 is the default argument to tee.

\$\endgroup\$
3
  • \$\begingroup\$ product(*[L]*n) is even shorter. \$\endgroup\$
    – T. Verron
    Commented Oct 8, 2015 at 11:59
  • \$\begingroup\$ @T.Verron The initial use case I had in mind was something like a Python 3 map, for which that wouldn't work, but I'll add it as a note thanks :) \$\endgroup\$
    – Sp3000
    Commented Oct 8, 2015 at 12:05
  • 1
    \$\begingroup\$ Doesn't feersum have an open 500-point bounty for using itertools at all in a sufficiently old question? \$\endgroup\$
    – lirtosiast
    Commented Oct 8, 2015 at 13:17
4
\$\begingroup\$

Dictionary defaults as entries

Say you have an dictionary literal, which I'll denote {...}, and you want to get the value for a key k, with a default of d if k is missing.

You can save two bytes by prepending an entry rather than using get

{k:d,...}[k]
{...}.get(k,d)

Because later entries override earlier ones of the same key, the entry k:d gets overwritten if it appears in the dict, but remains if key k isn't present.

Note that this required writing k twice, which is fine for a variable, but poor when k is an expression.

\$\endgroup\$
4
\$\begingroup\$

Access list, while building it inside comprehension

In python version 2.4 (and <2.3 with some tweaks) it is possible to access list, from list comprehension. Source #1, Source #2 (Safari, Python Cookbook, 2nd edition)

Python creates secret name _[1] for list, while it is created and store it in locals. Also names _[2], _[3]... are used for nested lists.

So to access list, you may use locals()['_[1]'].

In earlier versions this is not enough. You'll need to use locals()['_[1]'].__self__

I couldn't find evidence, that somethins like that is possible in versions >2.4

Don't think, that it might be usefull often, but who knows! At least it helps with building one-liners.

Example:

# Remove duplicates from a list:
>>> L = [1,2,2,3,3,3]
>>> [x for x in L if x not in locals()['_[1]']]
[1,2,3]
\$\endgroup\$
1
  • 5
    \$\begingroup\$ This still exists in later Pythons. In Python 3, it's called '.0', and it's a generator. \$\endgroup\$
    – isaacg
    Commented Aug 18, 2017 at 6:42
4
\$\begingroup\$

If transforming from list to a tuple or set to a set, list or tuple is needed, as of Python 3.5 you can use the splat operator:

tuple(iterable) -> (*iterable,)  (-3 bytes)
set(iterable)   -> {*iterable}   (-2 bytes)
list(iterable)  -> [*iterable]   (-3 bytes)

If you're doing this as well as appending/prepending, you can do the following for an extra bonus:

iterable+[1]       -> *iterable,1          (-2 bytes, 3 for tuples)
iterable+iter2     -> *iterable,*iterable2 (+1 byte, 0 for tuples, though can combine types)
[1]+iterable+[1]   -> 1,*iterable,1        (-3 bytes, -4 for tuples)
iterable+[1]+iter2 -> *iterable,1,*iter2   (0 bytes, -1 for tuples)

Basically, ,* instead of , gives a +1 byte penalty and , instead of ,[] gives -2 bytes.

This shows [1,*iterable,1] is a golfier way of doing [1]+iterable+[1] by one byte, even when we're not doing any type conversion.

And just for fun, {*{}} is the same length as set() for challenges without letters.

\$\endgroup\$
1
  • 1
    \$\begingroup\$ The last one can also be useful if there is a letter preceding set() (e.g. ...and set() can become ...and{*{}}). Oh, and it can be replaced with {*()} or {*[]} if an empty dict feels unsettling. ;-) \$\endgroup\$ Commented Jun 29, 2018 at 22:37
4
\$\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
  • 1
    \$\begingroup\$ Alternative in Python 3 which can be shorter when combined with other assignments: *d,=x \$\endgroup\$
    – pxeger
    Commented Jan 2, 2022 at 0:58
4
\$\begingroup\$

Replace not with 1-

In python, the negation operator not wastes bytes, so we have to find a shorter way. Negation can be implemented as subtraction from 1 (obtained from my Keg experiece), which saves 1 byte. (Also, True and False can be alternatively represented as 1 and 0 internally, so this will not matter much.)

Compare this program:

lambda s:not(s[0]+s[-1]).isdigit()

With this program:

lambda s:1-(s[0]+s[-1]).isdigit()

Some straightforward tricks that might help:

and -> *
or -> +
\$\endgroup\$
1
4
\$\begingroup\$

dict.get as a first-class function

# all keys in the dict G with a truthy value
[k for k in G if G[k]]
filter(G.get,G)

# all keys in the dict G with a falsy value
[k for k in G if not G[k]]
G.keys()-filter(G.get,G)

{…,**d} or a|b to merge dicts in Python

# merge two dicts
a={…}
b={…}
# Python <3.9
merged={**a,**b} # the order lets you decide which overrides which
# Python ≥3.9
merged=a|b

# set a defaut value
G.setdefault(a,1)
G[a]=G.get(a,1)
# Python <3.9
G={a:1,**G} 
# Python ≥3.9
G={a:1}|G

Extract elements from a list using ::

# This is a very specific tip when you want to get both an element at
# the near beginning of a list and one somewhere near the end.
# For example, let's assume you want to take the elements at indices i=6 and j=37
# from a list L of length l=40 (it works only if i+j*2<l)

# ok
a=L[5];b=L[36]
# equivalent
a,b=L[5],L[36]
# 36 = 5 + 31
a,b=L[5::31]

Set literals

set() is 5 chars just to create an empty set. If you can have an initial element e, you can save 2 chars with {e}.

Generators instead of comprehension lists in function calls

# assuming the function iterates on its argument
f([x**2 for x in range(4)])
f(x**2 for x in range(4))
\$\endgroup\$
0
4
\$\begingroup\$

Access static methods on instances

If you're accessing a static method or class method, for example int.from_bytes or dict.fromkeys, you can also still access them on instances of that class - it doesn't have to be on the class itself. It's very often shorter to do this:

int.from_bytes(...
0 .from_bytes(...

dict.fromkeys(...
{}.fromkeys(...

Often you already have an instance of that class stored in a one-letter variable, so it can be even shorter:

x.from_bytes

It doesn't matter what value x is, and it isn't changed.

\$\endgroup\$
4
\$\begingroup\$

You can use list checking instead of using ors in a clause statement.

e.g. instead of using

if i % 3 == 0 or i % 5 == 0 or i % 7 == 0 ...:
# Golfed:
if i%3==0or i%5==0or i%7==0 ...:

use

if 0 in [i % 3, i % 5, i % 7, ...]:
# Golfed:
if 0in[i%3,i%5,i%7,...]:

This saves around 2-3 characters per condition, but can only be used in certain circumstances.

\$\endgroup\$
1
  • \$\begingroup\$ I used this tip here \$\endgroup\$ Commented Dec 7, 2022 at 7:47
1 2 3
4
5
7

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.