Packers
A 2:1
packer
This is Python's fabled 2:1
packer, for your convenience. For chars scoring, it can reduce code by a factor of two.
Spoiler
Decompressor
Note: the overhead is 25 characters so your uncompressed code must be over 50 characters to get a benefit.
exec(bytes('...','u16')[2:])
Compressor
Note: the code size must be divisible by 2 for the compressor to work. If not, adding a trailing space should suffice.
print('CODE'.encode().decode('u16'))
A 3:1
packer
There also exists a 3:1
packer, but it only works with bytes above ASCII character 31.
Spoiler
Decompressor
Note: the overhead is 49 characters so your uncompressed code must be over 73 characters to get a benefit over unpacked code and over 144 characters to get a benefit over the 2:1 packer (assuming a compression ratio of strictly 3:1).
exec(bytes(ord(c)%i+32for c in'...'for i in b'efg'))
Compressor
def crt(a, n):
s, p = 0, 1
for x in n:
p *= x
for x, y in zip(a, n):
q = p // y
s += q * x * pow(q, -1, y)
return s % p
code = '''CODE'''
compressed = ''
for i in range(0, len(code), 3):
a = [ord(c) - 32 for c in code[i:i+3]]
compressed += chr(crt(a, [101, 102, 103]))
print(compressed)
Boolean access
Instead of:
a if condition else b
Use:
[b,a][condition]
Only works if a
and b
can both be safely evaluated, regardless of which one is picked.
Shorter if...else
As seen above, [b,a][condition]
may be used instead of an if...else
, but only if a
and b
cause no side effects. A laxer alternative is
condition and a or b
It saves one byte, but has the restriction that a
must be truthy. There is also
a*condition or b
but with this one, a
must be truthy, in addition to not causing any side effects.
Chained comparison
Use comparison chaining to save bytes on and
:
0<a and a<5 and 0<b and b<5
# vs
0<a<5 and 0<b<5
# vs
0<a<5>b>0
You can chain in
as well. This is less useful, but nice to know:
a in b and b<c
# vs
a in b<c
The next tip on short circuiting also utilizes comparison chaining.
Short circuiting ==
if f(x)>1:print(x)
can be rewritten implementing the short circuit:
f(x)>1==print(x)
Multiple assignment
If you need to assign multiple variables to the same value, it's better to combine them into one statement:
a=1;b=1
# vs
a=b=1
However, assigning to an object (such as a list) may result in some weird behavior, since all the variables share the same instance:
a=b=[]
a.append(1)
print(a,b)
>>> [1] [1]
Appending to a list
By creating a tuple, you can do += instead of append, saving 6 bytes:
a.append(b)
# vs
a+=b,
Walrus operator
Python 3.8 introduced assignment expressions (A.K.A. the walrus operator). Its main use in golf is to allow assigning and using a variable at the same time:
while 1:x=1;...
# vs
while x:=1:...
x=1;f(x);g(x)
# vs
f(x:=1);g(x)
Using byte strings as integer lists
Python's byte strings can act like integer lists, sometimes:
[48,96,33]
# vs
b'0`!'
This can, for example, help with iterating or indexing a list:
# Iteration
for x in 48,96,33:
for x in b'0`!':
# Indexing
[48,96,33][i]
b'0`!'[i]
Unpacking assignment
Here is a list of tricks for potentially saving bytes on variable assignment. All are based on the powerful feature of iterable unpacking:
t=a;a=b;b=t
# vs
a,b=b,a
a=L[0];b=L[1];c=L[2]
# vs
a,b,c=L
- Assigning a value + a list
x=1;L=[1]
# vs
x,=L=[1]
- Assigning a value + an empty list
x=1;L=[]
# vs
x,*L=1,
- Converting an iterable to a list
L=list(range(5))
# vs
*L,=range(5)
- Popping off the first element of a list
x=L.pop(0)
# vs
x,*L=L
- Popping off the last element of a list
x=L.pop()
# vs
*L,x=L
Print list separated by \n
AFAIK, the shortest way to print a list (or any iterator) with each element on a new line is:
*map(print,L),
Some longer alternatives include:
[*map(print,L)]
*_,=map(print,L)
0in map(print,L)
print(*L,sep='\n')
for x in L:print(x)
Splat on iterables
Splat makes functions like list
, set
, and tuple
a lot shorter:
list(L) -> [*L]
tuple(T) -> (*T,)
set(S) -> {*S}
It saves on concatenation as well:
[1,2]+[L]+[3,4] -> [1,2,*L,3,4]
(1,2)+L+(3,4) -> (1,2,*L,3,4)
A sys.argv
trick
Some of the holes on code.golf require input through ARGV
. Nine out of ten times,
for a in sys.argv[1:]:f(a)
will be the shortest option. But let's take a look at this alternative:
while 1:f(sys.argv.pop(1))
Both of these contain the same amount of bytes, except the second one is less useful as it is not stored in a variable. The advantage with the second option is that the while 1
might have room to fit a walrus operator (as discussed in this tip). Under these specific circumstances, it can usually save one byte.
Removing multiple imports
Suppose you want functions x
and y
from modules a
and b
. It may just so happen that a
itself imports b
, wherein you can write
import a
a.x()
a.b.y()
This will work so long as a
does not del b
in its own code. If "b"
appears in a.__all__
, you can also use
from a import*
x()
b.y()
The %
operator works on bytes too - b'%r'%x
and b'%d'%x
are really useful.
Hash
In Python 3, the hash
function is still deterministic on tuples - so you can get an easy hash like this: hash((*b'%r'%x,))
.