Python Cheatsheet

kuniga.me > Docs > Python Cheatsheet

Python Cheatsheet

Syntax for common tasks I run into often. Assumes Python 3.

Index

  1. Data Structures
    1. Enum
      1. No value
      2. From primitive
      3. To primitive
    2. List
      1. Map over list
      2. Filter list
      3. Any / All
      4. Sort list
    3. Dictionaries
      1. Default entries
      2. Remove entry
      3. Map over dictionary
      4. Sort dictionary
    4. Sets
      1. Create
      2. Insert
      3. Difference
      4. Union
      5. Map over set
    5. Strings
  2. Object Oriented
    1. Class Methods
    2. Inheritance
      1. Instance check
    3. Abstract classes
    4. Dataclasses
      1. Default Value
      2. Cloning
      3. Immutable
  3. Flow Control
    1. Exceptions
  4. Stdin/Stdout
    1. Capture stdout
  5. Files
    1. Read file
    2. Write file
  6. Functions
    1. Force named arguments in call
    2. Decorators
    3. Pass primitive values by reference
  7. Date and Time
    1. Elapsed time
  8. Context Manager
    1. Async
  9. Operating System
    1. Path
      1. Create
      2. Append path
      3. Get filename
      4. File extension
      5. Navigate to parent
      6. Resolve
      7. To String
    2. Subprocess
  10. Other Data Structures
    1. Queue
  11. Regex
    1. Check if it matches
    2. Extract

Data Structures

Enum

from enum import Enum

class MyEnum(Enum):
  A = 'a'
  B = 'b'

No value

from enum import Enum, auto

class MyEnum(Enum):
  A = auto()
  B = auto()

From primitive

print(MyEnum('a')) # MyEnum.A

It does validation:

print(MyEnum('c'))

To primitive

print(MyEnum.A.value) # 'a'

List

Map over list

xs = [1, 2, 3]
ys = [f(x) for x in xs]

Filter list

xs = [1, 2, 3]
ys = [x for x in xs if x % 2 == 0]

NOTE: filter and map can be combined into one.

Any / All

Find if any (all) element satisfy a predicate:

xs = [1, 2, 3]
ys = any(predicate(x) for x in xs)

NOTE: this is a generator, so it will short-circuit once if finds an element satisfying the predicate. The version below creates a list first, so no short-circuit happens.

xs = [1, 2, 3]
ys = [any(predicate(x) for x in xs)]

Sort list

Using default sorting:

>>> sorted([5, 2, 3, 1, 4])
[1, 2, 3, 4, 5]

Sorting using key function. For example, sort list of pairs by the second element:

sorted([[1, 3], [2, 2], [3, 1]], key=lambda x: x[1])

Sorting by custom comparator:

import functools

# Function that returns
# < 0 if a < b
# > 0 if a > b
# = 0 if a = b
def cmp(a, b):
    return a - b

sorted([3, 2, 1], key=functools.cmp_to_key(cmp))

Dictionaries

Default entries

from collections import defaultdict
dict = defaultdict(list)
dict['key'].append(1) # {'key': [1]}

Note: defaultdict takes a callback to be called when a new entry is added. list is equivalent to lambda _ : [].

Remove entry

del dict[key]

Map over dictionary

ys = {k: f(v) for k, v in xs.items()}

Sort dictionary

Dictionaries are unordered sets, so you likely want to work with a list after sorting.

d = {'a': 3, 'b': 2}
xs = sorted(d.items(), key=lambda x: x[1])

Sets

Create

s = {1, 2, 3}

Empty:

s = set()

Note that s = {} creates an empty dictionary.

Insert

Add/insert

s.add(4)

Difference

s1 = set([1, 2])
s2 = set([2, 3])
s1 = s1 - s2 # {1}

Union

s1 = set([1, 2])
s2 = set([2, 3])
s1 = s1.union(s2) # {1, 2, 3}

Map over set

xs = {1, 2, 3}
ys = {f(x) for x in xs}

Strings

To bytes. Depends on the desired encoding.

bytes = str.encode("utf-8") # utf-8 is the default

From bytes:

str = bytes.decode()

Object Oriented

Basic Syntax:

class C:
    def __init__(self, param):
        self.param

    def method(self):
        return self.param

    @staticmethod
    def static_method():
        return 1

Check if object is instance of a class:

class MyClass:
    pass
x = MyClass()
isinstance(x, MyClass)

Class Methods

Methods where the bound variable is an instance to the class.

class C:
    @classmethod
    def class_method(cls):
        print(cls) # __main__.C

    def method(self):
        print(self) # <__main__.C instance at 0x12345>;

See also: Class Methods in Revisiting Python: Object Oriented Programming

Inheritance

Example template:

class Base:
  def __init__(self, f):
    self.f = f

class Child(Base):
  def __init__(self, f, g):
    super().__init__(f)
    self.g = g

c = Child(1, 2)
print(c.f, c.g)

Instance check

if isinstance(obj, MyClass):
    print("is of type MyClass")

Abstract classes

from abc import ABC, abstractmethod

class MyAbstractClass(ABC):
  @abstractmethod
  def my_abstract_method(self, arg1, arg2):
    """Describe this method. No need to have body"""

class MyConcreteClass(MyAbstractClass):
  def my_abstract_method(self, arg1, arg2):
    return arg1 + arg2

c = MyConcreteClass()
# TypeError: Can't instantiate abstract class MyNonConcreteClass
# with abstract method my_abstract_method
d = MyNonConcreteClass()

Dataclasses

Lightweight syntax for creating classes / records.

from dataclasses import dataclass

@dataclass
class Point:
     x: int
     y: int

p = Point(10, 20)
print(p.x) # 10

q = Point(y=10, x=20)
print(q.x) # 20

Complex types:

# ... continued from above

@dataclass
class Polygon:
    pts: [Point]

pol = Polygon([p, q])
print(pol.pts) # [Point(x=10, y=20), Point(x=20, y=10)]

Default Value

@dataclass
class Point:
     x: int
     y: int
     z: int = 0

p = Point(x=10, y=20)
print(p.z) # 0

Cloning

dataclasses.replace:

p = Point(1, 2)
p_copy = dataclasses.replace(p)

Immutable

Prevents fields from being changed:

@dataclass(frozen=True)
class Wrapper:
    v: str = 'abc'

    def __init__(self):
        self.v = 'cde' # error

w = Wrapper()
w.v = 'cde' # error

It’s possible to bypass though:

@dataclass(frozen=True)
class Wrapper:
    v: str = 'abc'

    def __init__(self):
        # internal backdoor
        object.__setattr__(self, "v", "backdoor")

w = Wrapper()
# external backdoor
object.__setattr__(w, "foo", 'backdoor')

Flow Control

Exceptions

Basic syntax:

try:
    raise Exception('hello')
except Exception as ex:
    print('caught', str(ex))

Custom exception:

class MyException(Exception):
    pass

try:
    raise MyException('hello')
except MyException as ex:
    print('caught specific', str(ex))

Re-raise exception

try:
    raise Exception('hello')
except Exception as ex:
    print('caught specific', str(ex))
    raise

Raise new exception but preserve original stack trace:

try:
    raise Exception('hello')
except Exception as ex:
    raise Exception('new title') from ex

Stdin/Stdout

Capture stdout

out = io.StringIO()
with redirect_stdout:
    # call code which prints to stdout
    execute()
stdout_str = out.getValue()

Files

Read file

with open(filename, "r") as file:
    print(file.readlines())

Read all the contents into one string:

with open(filename, "r") as file:
    print(file.read())

Write file

Temporary file:

# Note: file gets deleted once it exits the scope
with tempfile.NamedTemporaryFile() as file:
    file.write("Hello World\n")
    print(f"writing to filename = {file.name}")

    # make sure to flush if something will read from it
    file.flush()

    # read as bytes
    file.read()

Note: default mode is w+b (write and bytes). If you’ll be reading strings, make sure to change to mode=w+.

Functions

Force named arguments in call

def f(*, a, b):
    pass

f(a=1, b=2) # ok
f(1, 2) # error

Decorators

A decorator is basically a function transformer. Basic template:

import functools
def my_decorator(f):
    @functools.wraps(f)
    def wrapper(*args, **kwargs):
        # pre-process
        ...
        r = f(*args, **kwargs)
        # post-process
        ...
        return r
    return wrapper

@my_decorator
def my_func():
    pass

Pass primitive values by reference

Primitives like bool, int, str, etc. are not implicitly bound to functions in the same scope like lists are. Declaring it nonlocal binds it explicitly.

def f():
    x = 0
    def inc():
        nonlocal x
        x += 1

    inc()
    print(x) # 1

Date and Time

Current time:

datetime.now()

Adding/subtracting time:

datetime.now() + datetime.timedelta(days=1)

Elapsed time

Wall-clock time in seconds:

start = time.time()
call()
elapsed_secs = time.time() - start

Context Manager

class ContextManager:
  def __enter__(self):
    self.v = [1, 2, 3]
    return self

  def __exit__(self, exc_type, exc_value, traceback):
    self.v = None
    return self

  def foo(self):
    print(len(self.v))

with ContextManager() as ctx:
  ctx.foo()

Async

class ContextManager:
  async def __aenter__(self):
    self.v = [1, 2, 3]
    return self

  async def __aexit__(self, exc_type, exc_value, traceback):
    self.v = None
    return self

  async def foo(self):
    print(len(self.v))

async with ContextManager() as ctx:
  await ctx.foo()

Operating System

Path

from pathlib import Path

Create

p = Path('/etc')

Append path

p = p / 'dir' # Path('/etc/dir')

Note the overloaded operator /. Works with files too:

p = p / 'file.txt' # Path('/etc/file.txt')

Get filename

Path('/home/file.txt').name # 'file.txt'

Note: this also returns the leaf directory:

Path('/home/me/').name # 'me'

File extension

It includes the ..

Path('/home/me/file.txt').suffix # '.txt'
p = Path('/dev/null').parent # Path('dev/')

Resolve

p = Path('.').resolve() # absolute path

To String

print(str(Path('/dev/null'))) # dev/null

Subprocess

import subprocess

Run command, get stdout:

command = ['ls', '-l']
res = subprocess.run(command, close_fds=True, stdout=subprocess.PIPE)
output = res.stdout.decode()

Check return code:

res = subprocess.run(command, close_fds=True, stdout=subprocess.PIPE)
print(res.returncode)

Other Data Structures

Queue

The Queue class is an advanced implementation that can be used for example in multi-thread applications. We can still use it as a plain queue data structure.

from queue import Queue

# Create
q = Queue()

# Insert back
q.put(item)

# Retrieve and remove front
first = q.get()

# Front element without removing
first = q[0]

# Size

# Queue doesn't expose len()
# since it can lead to incorrect
# usage in multi-threaded applications

# Is empty?
q.empty()

Regex

Keywords: regular expression

Check if it matches

text = "the number is 12345"
if re.match('is ([0-9]+)', text):
    print("matches")

Extract

text = "the number is 12345"
result = re.search('is ([0-9]+)', text, re.IGNORECASE)

if result:
    print(result.group(1)) # 12345