# 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

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:

• Swapping two variables
t=a;a=b;b=t
# vs
a,b=b,a

• Unpacking an iterable
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()


# Bytes formatting

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,))`.