Previous: Files, Next: ModulesUp: Python

Hax

Table of Contents

A menagarie of cool python snippets!

1 Unpacking

def is_palindrome(indexable):
    if len(indexable) > 1:
        first, *rest, last = indexable
        return first == last and is_palindrome(rest)
    else:
        return True

1.1 Bonus Hack: Quicksort - EZ Sorting

Not the most efficient implementation, but we can implement quicksort extremely succinctly using list unpacking + recursion:

def quicksort(ls: list):
    if ls:
        pivot, *rest = ls
        smaller, greater = [], []
        for item in rest:
            (smaller if pivot > item else greater).append(item)
        return quicksort(smaller) + [pivot] + quicksort(greater)
    else:
        return []

Of course sorted(ls) does the job too.

2 Poor Man's Cache

def fib(n: int, cache={0: 1, 1: 1}):
    if n not in cache:
        cache[n] = fib(n - 1, cache) + fib(n - 2, cache)
    return cache[n]

The cache object is created when the function is defined, not every time it is called. You can reset the cache (and change the base cases!) by passing in a new cache object.

Bonus points for fixing up this one so it doesn't blow up the stack.

3 ♥ Generators ♥

def triangle_number(n: int):
    return sum(k for k in range(n + 1))

sum(k for k in range(n + 1)) is desugared to sum((k for k in range(n + 1))), so we're just taking the sum of a generator comprehension. Note this is not equivalent to sum([k for k in range(n + 1)]) as the list comprehension does not use constant memory!

4 Poor Man's defaultdict

my_dict = {"a": 1, "b": 2}
print(my_dict.get("A", -1))

If we forego the slice syntax, we can specify a default value to .get in order to avoid a KeyError.

5 Zip is it's own Inverse

import string

zipped = list(zip(range(10), string.ascii_lowercase, string.ascii_uppercase))
print("Zipped up: " + str(zipped))
dezipped = list(zip(*zipped))
print("Dezipped: " + str(dezipped))

6 = in Format Strings

Since 3.8 you can prepend the expression text to the evaluated expression if you append an = to the format expression.

name = "Bob"
age = "37"

print(f"Person({name=}, {age=})")

7 shlex for Commands

We can use shlex.split to transform a command string into a list of arguments suitable for subprocess.Popen or even:

import shlex, subprocess, pprint

command = """find . -type f -printf '%TY-%Tm-%Td %TT %p
'"""  # Lists files and when they were last edited
args = shlex.split(command)
result = subprocess.run(args, capture_output=True)
recently_edited = sorted(result.stdout.decode("utf-8").splitlines())[-2:]
pprint.pp(recently_edited)
# ['2022-08-01 18:12:41.0370089750 ./files.org',
#  '2022-11-05 21:40:23.4741009550 ./hax.org']

Also useful are shlex.quote(s) which returns a shell escaped version of s and shlex.join, the inverse to shlex.split.

8 Built in Iverson Brackets

We can perform arithmetic with boolean values and integers, basically True = 1 and False = 0 (so much for strong typing!). We can use this behaviour to implement Iverson Brackets fairly readably:

from math import gcd

def phi(n):
    return sum((gcd(n, i) == 1) for i in range(1, n + 1))

def ramp(x):
    return x*(x > 0)

9 Ad-hoc Timing

IPython's %timeit is extremely useful, but for situations where this is not possible, we can use time in conjuction with the -c flag to time a function belonging to a module in the current directory:

time pypy3 -c "from mymodule import foo;print(foo('arg'))"

Author: root

Created: 2024-03-23 Sat 11:44

Validate