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

177 Answers 177

4
\$\begingroup\$

Instead of not in, use in with ^1 or 1-

Often, it helps to replace a not in b with (a in b)^1. By themselves,it doesn't save any bytes, but it often means that you can remove whitespace:

For example, if a not in b: can just be if(a in b)^1: to save a byte.

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

Long numbers to 1e_

I'm not sure if this has been mentioned before (likely), so either I'm going crazy or I'm blind.

Long numbers like 10000 can be written as 1e4 for short. Do note that using 1e_ gives a decimal, not a integer, so 1e4 in fact gives 10000.0, so be wary when doing stuff like:

for x in range(1,1e4):print(x%5)

which gives an error since when using range you have to use integers.

\$\endgroup\$
1
  • 2
    \$\begingroup\$ I think the reason it gives an error is because of the fact that range requires ints and not floats, and it's not because of the modulo. \$\endgroup\$ Nov 4, 2022 at 4:10
3
\$\begingroup\$

Sometimes you can use Python's exec-statement combined with string repetition, to shorten loops. Unfortunately, you can't often use this, but when you can you can get rid of a lot of long loop constructs. Additionally, because exec is a statement you can't use it in lambdas, but eval() might work (but eval() is quite restricted in what you can do with it) although it's 2 characters longer.

Here is an example of this technique in use: GCJ 2010 1B-A Codegolf Python Solution

\$\endgroup\$
6
  • \$\begingroup\$ What kind of restrictions are you thinking about? I don't think Python in itself has any kind of restriction on exec, so you must be referring to problem statements? \$\endgroup\$
    – hallvabo
    Jan 28, 2011 at 8:43
  • \$\begingroup\$ @hallvabo, you're right it's eval() I was most likely thinking of. You can't do eval("print 1") because print 1 is a statement. I'll update the post. \$\endgroup\$
    – JPvdMerwe
    Jan 28, 2011 at 10:23
  • \$\begingroup\$ As a general rule, this is worth trying when you need to do something n times, but don't care about the index. As soon as you need to initialise a loop variable and increment it, it ends up slightly longer than a for loop \$\endgroup\$
    – gnibbler
    Feb 3, 2011 at 20:35
  • 2
    \$\begingroup\$ In Python 3 you can do eval("print(1)") since print() is now a function. \$\endgroup\$ Apr 20, 2014 at 23:02
  • 2
    \$\begingroup\$ In Python 3 you can also do exec("print(1)") since exec() is now a function. \$\endgroup\$ May 28, 2017 at 5:37
3
\$\begingroup\$

Was somewhat mentioned but I want to expand:

[a,b],[c,d]=[[1,2],[3,4]]

works as well as simple a,b=[1,2]. Another great thing is to use ternary operator (similiar to C-like ?:)

x if x<3 else y

and no one mentioned map. Map will call first function given as first argument on each item from second argument. For example assume that a is a list of strings of integers (from user input for example):

sum(map(int,a)) 

will make sum of all integers.

\$\endgroup\$
2
  • 6
    \$\begingroup\$ Quoting the OP: Please post one tip per answer. \$\endgroup\$
    – alexia
    Jun 23, 2014 at 13:53
  • \$\begingroup\$ x if cond else y == cond and x or y. \$\endgroup\$
    – DELETE_ME
    Jul 5, 2018 at 9:34
3
\$\begingroup\$

If you rely on data (mostly for kolmogorov-complexity problems), use the built-in zip encoding/decoding and store the data in a file (add +1 for the filename):

open('f','rb').read().decode('zip')

If you have to store the data in the source code, then you need to encode the zip with base64 and do:

"base64literal".decode('base64').decode('zip')

These don't necessarily save characters in all instances, though.

\$\endgroup\$
3
\$\begingroup\$

When your program needs to return a value, you might be able to use a yield, saving one character:

def a(b):yield b

However, to print it you'd need to do something like

for i in a(b):print i
\$\endgroup\$
2
  • 1
    \$\begingroup\$ If only a single value is yielded, print next(i()) will work too. \$\endgroup\$
    – alexia
    Jun 23, 2014 at 13:35
  • \$\begingroup\$ Or just do print[*a(b)] \$\endgroup\$ Jun 5, 2017 at 0:59
3
\$\begingroup\$

Shorter isinstance

Instead of

isinstance(x,C) # 15 bytes

there are several alternatives:

x.__class__==C  # 14 bytes
'a'in dir(x)    # 12 bytes, if the class has a distinguishing attribute 'a'
type(x)==C      # 10 bytes, doesn't work with old-style classes
'K'in`x`        # 8 bytes, only in python 2, if no other classes contain 'K'
                # watch out for false positives from the hex address

Some of them may save extra bytes depending on the context, because you can eliminate a space before or after the expression.

Thanks Sp3000 for contributing a couple of tips.

\$\endgroup\$
3
\$\begingroup\$

Optional empty sequence argument

Suppose we want to write a recursive function that prepends to a sequence (e.g. list, tuple) each time. For example, the Python 3 program

def f(n,k,L=[]):n and[f(n-1,k,[b]+L)for b in range(k)]or print(L)

works like itertools.product, taking n,k and printing all length n lists of numbers taken from range(k). (Example thanks to @xnor)

If we don't need L to be a list specifically, we can save on the optional empty list argument by making use of unpacking, like so:

def f(n,k,*T):n and[f(n-1,k,b,*T)for b in range(k)]or print(T)

where T is now a tuple instead. In the general case, this saves 3 bytes!

In Python 3.5+, this also works if we're appending to the end of a sequence, i.e. we can change f(n-1,k,L+[b]) to f(n-1,k,*T,b). The latter is a syntax error in earlier versions of Python though.

\$\endgroup\$
3
\$\begingroup\$

Use IDLE 3.3 to take multiline input

In IDLE versions 3.1 to 3.3, the command input() reads an entire multiline string like "line1\nline2", rather than a single line at a time as per the spec. This was fixed in version 3.4.

Calling input() only once is very convenient for golfing. Whether one can take advantage of this is debatable, but I think it is an acceptable interpreter- or environment-specific behavior.

\$\endgroup\$
2
  • \$\begingroup\$ Wait... so how do you... sort of, enter the string if Enter adds a newline rather than EOF? Does CTRL+D work on Windows? \$\endgroup\$
    – cat
    Mar 4, 2016 at 3:00
  • 1
    \$\begingroup\$ @tac I copy-paste it into IDLE. \$\endgroup\$
    – xnor
    Mar 4, 2016 at 3:30
3
\$\begingroup\$

Easiest way to swap two values

>>> a=5
>>> b=4
>>> a,b=b,a
>>> a
4
>>> b
5
\$\endgroup\$
3
\$\begingroup\$

To assign to a tuple, don't use parentheses. For example, a=1,2,3 assigns a to the tuple (1, 2, 3). b=7, assigns b to the tuple (7,). This works in both Python 2 and Python 3.

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

Slicing tricks for hard-wired lists of strings

Warning: Python is the language which worships readability above all else; so coding this way is a Mortal Sin.

This sort of thing comes up a lot; such as here where for a given digit in 0<=d<=9, we can get the 7-bit segment b value as a hex string from the list

b=['7e','30','6d','79','33','5b','5f','70','7f','7b'][d]

If the length of such a list is more than just a few elements, you're usually better off at least using split because you can replace a bunch of "','"s with a single character " " as delimiter. E.g.:

b='7e 30 6d 79 33 5b 5f 70 7f 7b'.split()[d]

This can be used for almost any list of strings (possibly at a small additional cost using a delimiter such as ",").

But if in addition, the strings we are selecting for all have the same length k (k==2 in our example), then with the magic of Python slicing, we can write the above as:

b='7e306d79335b5f707f7b'[2*d:][:2]

which saves a lot of bytes because we don't need character delimiters at all. But in that case, usually even shorter would be:

b='7367355777e0d93bf0fb'[d::10]
\$\endgroup\$
1
  • \$\begingroup\$ A late answer, but I'm very curious why you would want to golf Python code to drive a 7-segment display. Hmm, perhaps to golf "human nerdsnipe" into the example, which is a noteworthy semantic addition given that it uses 0 additional bytes :D \$\endgroup\$
    – i336_
    May 1, 2022 at 8:47
3
\$\begingroup\$

Unpacking in Python3

If you only need the first few values in the array

>>> a, b, *c = [1, 2, 3, 4, 5]
>>> a
1
>>> b
2
>>> c
[3, 4, 5]

Same applies to when you need last few values

>>> *a, b, c = [1, 2, 3, 4, 5]
>>> a
[1, 2, 3]
>>> b
4
>>> c
5

Or even with the first few and last few

>>> a, *b, c = [1, 2, 3, 4, 5]
>>> a
1
>>> b
[2, 3, 4]
>>> c
5
\$\endgroup\$
1
  • 4
    \$\begingroup\$ I was experimenting with this, and I found out you can also initialise an empty array if the number of values is one less than the number of variables, such as a,*b,c=1,2 or a,*b,c="ab" \$\endgroup\$
    – Jo King
    Oct 22, 2018 at 11:25
3
\$\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\$
3
\$\begingroup\$

When you want to use map with list then cast it to list, use * instead of list(...).

new_a=list(map(f,a))
new_a=[*map(f,a)]     # -3 char

Moreover, you also can convert some iterable things to list with saving 3 characters:

a=list("abc")
a=[*"abc"]
b=list(range(x,y))
b=[*range(x,y)]
\$\endgroup\$
3
  • 1
    \$\begingroup\$ the most concise way to do this is like this, saving one character:: *a,=map(f,a) *b,=range(x,y) \$\endgroup\$ Apr 13, 2021 at 20:45
  • \$\begingroup\$ sorry but I got "SyntaxError: starred assignment target must be in a list or tuple" when I try it. \$\endgroup\$
    – HK boy
    Apr 15, 2021 at 15:21
  • 2
    \$\begingroup\$ You must be forgetting the comma or possibly using Python 2. Here is a SO question about it: stackoverflow.com/questions/43190992/understanding-x-lst \$\endgroup\$ Apr 15, 2021 at 20:38
3
\$\begingroup\$

List all substrings

You can generate the nonempty contiguous substrings of a string s with a recursive function (Python 3 for [*s]).

40 bytes (TIO)

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

This will repeat substrings multiple times even if each substring appears once. You can making this a set to remove duplicates. This won't run if s is a list instead of a string, but a tuple will work.

40 bytes (TIO)

f=lambda s:{*s}and{s}|f(s[1:])|f(s[:-1])

Or, you can do this to make a list where each substring appears one for each time it's present:

46 bytes (TIO)

f=lambda s,b=-1:[*s]and[s]+f(s[1:],0)+f(s[:b])

Thanks for loopy walt for the s[:b] optimization.

This can be used to sum over some expression g over all substrings, for instance to count nonempty substrings with some Boolean property g.

48 bytes (TIO)

f=lambda s,b=-1:s>""and g(s)+f(s[1:],0)+f(s[:b])
\$\endgroup\$
3
\$\begingroup\$

Python 2 and 3 differences

A recent challenge pushed me to search for differences in two major versions of Python. More precisely, code that returns different results in different versions. This might be helpful in other polyglot challenges.

  1. Strings and bytes comparison
  • Python 2: '' == b''
  • Python 3: '' != b''
  1. Rounding (Luis Mendo answer)
  • Python 2: round(1*0.5) = 1.0
  • Python 3: round(1*0.5) = 0
  1. Division (Jonathan Allan answer)
  • Python 2: 10/11 = 0
  • Python 3: 10/11 = 0.9090909090909091
  1. Suggestions?
\$\endgroup\$
0
2
\$\begingroup\$

Run your code through an space-remover, like this one:

#Pygolfer
a=raw_input()
for i in [i for i in range(len(a)) if a[i]==" "]:
    try:b=a[:i]+a[i+1:];eval(b);a=b;print a
    except:pass

(This just tries to remove the spaces one by one, and try if the code still works. Please still do check your code manually.)

Manual things to do: print'string' works.

[str(i)for i in(1,2,3,4)] works.

etc.

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

You can generate pseudo random numbers using hash.

hash('V~')%10000

Will print 2014.

\$\endgroup\$
3
  • 1
    \$\begingroup\$ Prints 9454 for me. \$\endgroup\$
    – alexia
    Jun 23, 2014 at 13:07
  • 2
    \$\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\$ Aug 13, 2014 at 8:34
  • \$\begingroup\$ Pretty similar to this. \$\endgroup\$
    – DELETE_ME
    Jul 5, 2018 at 9:42
2
\$\begingroup\$

Try a lambda expression

By default, submissions may be functions and functions may be anonymous. A lambda expression is often the shortest framework for input/output. Compare:

lambda s:s+s[::-1]
def f(s):return s+s[::-1]
s=input();print s+s[::-1]

(These concatenate a string with its reverse.)

The big limitation is that the body of a lambda must be a single expression, and so cannot contain assignments. For built-ins, you can do assignments like e=enumerate outside the function body or as an optional argument.

This doesn't work for expressions in terms of the inputs. But, note that using a lambda might still be worth repeating a long expression.

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

The lambda is shorter even though we save a char in the named function by having it print rather than return. The break-even point for two uses is length 12.

However, if you have many assignments or complex structures like loops (that are hard to make recursive calls), you're probably be better off taking the hit and write a named function or program.

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

cmp in Python 2

Say you want to output P if x>0, N if x<0, and Z if x==0.

"ZPN"[cmp(x,0)]

Try it online

This function was removed in Python 3.0.1, although it remained in Python 3.0 by mistake.

\$\endgroup\$
1
  • 4
    \$\begingroup\$ Congratulations on the 100th answer to this question! \$\endgroup\$ Mar 2, 2016 at 20:51
2
\$\begingroup\$

Abuse of or in lambdas

I'm surprised this isn't in here yet, but if you need a multi-statement lambda, or evaluates both of its operands, as opposed to and which doesn't evaluate the second one if the first one is not True. For instance, a contrived example, to print the characters in a string one by one with an interval:

list(
    map(
        (lambda i: 
            sleep(.06) or print(i) or print(ord(i)) # all of these get executed
        ), 
        "compiling... "
    )
)
            

In this case it isn't shorter, but I've found it to be, sometimes.

\$\endgroup\$
3
  • \$\begingroup\$ Related \$\endgroup\$
    – Sp3000
    Mar 4, 2016 at 3:21
  • \$\begingroup\$ @Sp3000 *sighs* I tried searching this question for or, lambda, evaluation etc but didn't see that \$\endgroup\$
    – cat
    Mar 4, 2016 at 3:24
  • \$\begingroup\$ lambda i:[sleep(.06),print(i),print(ord(i))] \$\endgroup\$
    – DELETE_ME
    Jul 5, 2018 at 9:37
2
\$\begingroup\$

Omit needless spaces

Python tokens only need to separated by a space for

  • A letter followed by a letter
  • A letter followed by a digit

In all other cases, the space can be omitted (with a few exceptions). Here's a table.

  L D S
 +-----
L|s s n
D|n - n
S|n n n    

First token is row, second token is column
L: Letter
D: Digit
S: Symbol

s: space
n: no space
-: never happens (except multidigit numbers)

Examples

Letter followed by letter: Space

not b
for x in l:
lambda x:
def f(s):
x in b"abc"

Letter followed by digit: Space

x or 3
while 2<x:

Letter followed by symbol: No space

c<d
if~x:
x and-y
lambda(a,b):
print"yes"
return[x,y,z]

Digit followed by letter: No space

x+1if x>=0else 2
0in l

(Some versions of Python 2 will fail on a digit followed by else or or.)

Digit followed by digit: Never occurs

Consecutive digits make a multidigit number. I am not aware of any situation where two digits would be separated by a space.

Digit followed by symbol: No space

3<x
12+n
l=0,1,2

A space is needed for 1 .__add__ and other built-ins of integers, since otherwise the 1. is parsed as a float.

Symbol followed by letter: No space

~m
2876<<x&1
"()"in s

Symbol followed by digit: No space

-1
x!=2

Symbol followed by symbol: No space

x*(a+b)%~-y
t**=.5
{1:2,3:4}.get()
"% 10s"%"|"
\$\endgroup\$
4
  • \$\begingroup\$ In general, you can't have a letter after a digit if it confuses the lexer. A digit followed by e is expected to be a float literal, so something like 1else wouldn't work for versions of python that support exponents in the literal. Similarly, as 0o is the prefix of an octal literal, o can follow any digit but 0. For the complete lexical rules, refer to docs.python.org/2/reference/lexical_analysis.html \$\endgroup\$
    – xsot
    Aug 1, 2016 at 5:20
  • \$\begingroup\$ @xsot Do you know what versions of Python will parse this way? I remember a comment thread on this, but I can't find it. \$\endgroup\$
    – xnor
    Aug 1, 2016 at 5:51
  • \$\begingroup\$ I've always assumed these rules so I'm not sure which versions of python deviate from them. Possibly the older ones, if any. \$\endgroup\$
    – xsot
    Aug 1, 2016 at 6:12
  • \$\begingroup\$ @xsot It works on 2.7.10 but fails on Anachy's 2.7.2. \$\endgroup\$
    – xnor
    Aug 1, 2016 at 6:28
2
\$\begingroup\$

Booleans are integers, too!

assert True == 1
assert False == 0
assert 2 * True == 2
assert 3 * False == 0
assert (2>1)+(1<2) == 2

If you have a statement like [a,a+x][c] (where c is some boolean expression), you can do a+x*c instead and save a few bytes. Doing arithmetic with booleans can save you lots of bytes!

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

If you're drawing, for colors, instead of typing:

'#000' for black you can just use 0 (no apostrophes)
'#fff' for white you can simply use ~0 (no apostrophes)
'#f00' for red you can just use 'red'


Example of white being used with ~0

from PIL.ImageDraw import*
i=Image.new('RGB',(25,18),'#d72828')
Draw(i).rectangle((1,1,23,16),'#0048e0',~0)
i.show()
\$\endgroup\$
1
  • 2
    \$\begingroup\$ 255 is even shorter than 'red'. Some more ideas: ~255 is '#0ff' (cyan). 1<<7 is #800000 (half-brightness red); similarly 1<<15 and 1<<23 are half-brightness green and blue. \$\endgroup\$
    – Lynn
    Jan 3, 2018 at 19:10
2
\$\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\$
2
\$\begingroup\$

Recursive functions that print

Functions are allowed to print as programs do. A recursive function that prints can be shorter than both a pure function and a pure program.

Compare these Python 2 submissions to make a list of iteratively floor-halving a number while it's positive, like 10 -> [10, 5, 2, 1].

# 30 bytes: Program 
n=input()
while n:print n;n/=2

# 29 bytes: Function
f=lambda n:n*[0]and[n]+f(n/2)

# 27 bytes: Function that prints
def g(n):1/n;print n;g(n/2)

Try it online!

The function that prints uses 1/n to terminate with error on hitting n=0 after having printing the desired numbers. This saves characters over the program's while and the pure function's base case, giving it the edge in byte count. Often, the termination can be shorter as part of the expression to print or the recursive call. It might even happen on its own for free, like terminating on an empty string when the first character is read.

The key property of our function here is that we're repeatedly applying an operation and listing the results at each step, in order. Additional variables can still be used this way by having them as optional inputs to the function that are passed in the recursive call. Moreover, because we're def'ing a function rather than writing a lambda, we can put statements such as variable assignments in its body.

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

Note: The below makes sense only in the program is scored as characters, not as bytes.

I haven't seen this, though somebody may have posted it somewhere.

I needed to have some long literal ASCII strings in the code so somehow shortening them (as characters, not bytes) would be beneficial. After some experiments I came up with what I call the "Chinese reencoding". I call it that way because ASCII characters mostly seem to be squashed in unicode code points that represent Chinese characters. You take an ASCII string S, encode it in bytes as ASCII, and then decode it in UTF16-BE, like that:

E=S.encode().decode('utf16-be')

The resulting string is half the length. It has to be big endian, as the reverse reencoding may not work - and on most systems the shorter 'utf16' is little endian. You also may need to add a character like space if the original string has odd length, but many times this is OK. Also, for non ASCII characters this does not save length, because they result in too big unicode code points that are represented in the liong form ("\uXXXX")

In you code, use the following:

[E].encode('utf16-be').decode()

in order to get the original longer string, where [E] is the literal shortened string. This costs 29 additional characters, so the original string has to be longer than 58, obviously.

One example - below is my 12 days of Christmas (it can be shortened additionally, but let's use that as an example):

for i in range(12):print('On the %s day of Christmas\nMy true love sent to me\n%s'%('First Second Third Fourth Fifth Sixth Seventh Eighth Ninth Tenth Eleventh Twelfth'.split()[i],'\n'.join('Twelve Drummers Drumming,+Eleven Pipers Piping,+Ten Lords-a-Leaping,+Nine Ladies Dancing,+Eight Maids-a-Milking,+Seven Swans-a-Swimming,+Six Geese-a-Laying,+Five Gold Rings,+Four Calling Birds,+Three French Hens,+Two Turtle Doves, and+A Partridge in a Pear Tree.\n'.split('+')[11-i:])))

It's 477 characters long. Let's apply the "Chinese" trick to the two longer string:

r=lambda s:s.encode('utf-16be').decode();for i in range(12):print('On the %s day of Christmas\nMy true love sent to me\n%s'%(r('䙩牳琠卥捯湤⁔桩牤⁆潵牴栠䙩晴栠卩硴栠卥癥湴栠䕩杨瑨⁎楮瑨⁔敮瑨⁅汥癥湴栠呷敬晴栠').split()[i],'\n'.join(r('呷敬癥⁄牵浭敲猠䑲畭浩湧Ⱛ䕬敶敮⁐楰敲猠偩灩湧Ⱛ呥渠䱯牤猭愭䱥慰楮本⭎楮攠䱡摩敳⁄慮捩湧Ⱛ䕩杨琠䵡楤猭愭䵩汫楮本⭓敶敮⁓睡湳ⵡⵓ睩浭楮本⭓楸⁇敥獥ⵡⵌ慹楮本⭆楶攠䝯汤⁒楮杳Ⱛ䙯畲⁃慬汩湧⁂楲摳Ⱛ周牥攠䙲敮捨⁈敮猬⭔睯⁔畲瑬攠䑯癥猬\u2061湤⭁⁐慲瑲楤来\u2069渠愠健慲⁔牥攮ਠ').split('+')[11-i:])))

That's 362, including the lambda (it happens to be worth it, as it is used twice).

Now, all code is mostly ASCII characters, so you may have already guessed that you can use that with exec. There is higher overhead - 43 chars for "exec(''.encode('utf-16be').decode())" (in addition to the whole compressed program) and you may need to double escape some escaped characters in your literal strings (like '\n' in mine has to become '\n'). As a bonus you can always easily add that one space. The compressed porogram looks like:

exec("景爠椠楮\u2072慮来⠱㈩㩰物湴⠧佮⁴桥‥猠摡礠潦⁃桲楳瑭慳屮䵹⁴牵攠汯癥\u2073敮琠瑯\u206d敜渥猧┨❆楲獴⁓散潮搠周楲搠䙯畲瑨⁆楦瑨⁓楸瑨⁓敶敮瑨⁅楧桴栠乩湴栠呥湴栠䕬敶敮瑨⁔睥汦瑨✮獰汩琨⥛楝Ⱗ屮✮橯楮⠧呷敬癥⁄牵浭敲猠䑲畭浩湧Ⱛ䕬敶敮⁐楰敲猠偩灩湧Ⱛ呥渠䱯牤猭愭䱥慰楮本⭎楮攠䱡摩敳⁄慮捩湧Ⱛ䕩杨琠䵡楤猭愭䵩汫楮本⭓敶敮⁓睡湳ⵡⵓ睩浭楮本⭓楸⁇敥獥ⵡⵌ慹楮本⭆楶攠䝯汤⁒楮杳Ⱛ䙯畲⁃慬汩湧⁂楲摳Ⱛ周牥攠䙲敮捨⁈敮猬⭔睯⁔畲瑬攠䑯癥猬\u2061湤⭁⁐慲瑲楤来\u2069渠愠健慲⁔牥攮屮✮獰汩琨✫✩嬱ㄭ椺崩⤩".encode('utf-16be').decode())

and it's 299 characters long. You can see some high code points can always appear. I have not found a way to eliminate them, as the added handling code is not worth the benefit.

This is a cheap trick, in fact, but it can always be applied on top of your solution when the program is longish and there are no or few non-ASCII characters. Often you can devise a custom encoding that can stuff more than two ASCII chars in an unicode one, but it is specific for the task.

\$\endgroup\$
3
  • \$\begingroup\$ Most code-golf questions are scored in bytes, not characters \$\endgroup\$ Sep 7, 2019 at 22:00
  • \$\begingroup\$ Interesting, I don't do much and the ones that I have done were scored in characters - but it makes sense. \$\endgroup\$ Sep 7, 2019 at 22:02
  • 6
    \$\begingroup\$ exec(bytes('compressed code','u16')[2:]) is a shorter way to achieve this. \$\endgroup\$ Jan 12, 2021 at 0:42
2
\$\begingroup\$

To set lots of variables to the same thing use:

# x is a list of the names of the variables you want and y is
# the value you want all of them to have
exec(("%s="*len(x))%tuple(x)+str(y))
\$\endgroup\$
1
  • \$\begingroup\$ depending on the context for c in x:locals()[c]=y or for c in x:globals()[c]=y might also work. \$\endgroup\$
    – ovs
    Jun 12, 2020 at 13:59
2
\$\begingroup\$

Signum Function

Python doesn't have a nice signum built-in, even though its a very useful function for golfing. It takes a lot of bytes to use math.copysign or numpy.sign once you include the imports, and the latter doesn't even return ints, so you waste even more bytes if you need to use it in a list index, for example.

Here's a decent version you can use in that one answer where you really need integer signs multiple times:

s=lambda x:(x>0)-(x<0)

If you don't need an integer, you can shave off one /. If you see any way to shorten it, please comment.

As per Jakque, in Python 2, you can use the cmp function.

\$\endgroup\$
2
  • 2
    \$\begingroup\$ (x>0)-(x<0) is much shorter \$\endgroup\$
    – JayXon
    Jan 20, 2022 at 4:28
  • 2
    \$\begingroup\$ cmp (in pyhon 2 only) does also the trick and is cheaper Try it online! \$\endgroup\$
    – Jakque
    Jan 20, 2022 at 10:34

Your Answer

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

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