kuniga.me > Docs > Python Cheatsheet
Syntax for common tasks I run into often. Assumes Python 3.
from enum import Enum
class MyEnum(Enum):
A = 'a'
B = 'b'
from enum import Enum, auto
class MyEnum(Enum):
A = auto()
B = auto()
print(MyEnum('a')) # MyEnum.A
It does validation:
print(MyEnum('c'))
print(MyEnum.A.value) # 'a'
xs = [1, 2, 3]
ys = [f(x) for x in xs]
xs = [1, 2, 3]
ys = [x for x in xs if x % 2 == 0]
NOTE: filter and map can be combined into one.
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)]
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))
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 _ : []
.
del dict[key]
ys = {k: f(v) for k, v in xs.items()}
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])
s = {1, 2, 3}
Empty:
s = set()
Note that s = {}
creates an empty dictionary.
Add/insert
s.add(4)
s1 = set([1, 2])
s2 = set([2, 3])
s1 = s1 - s2 # {1}
s1 = set([1, 2])
s2 = set([2, 3])
s1 = s1.union(s2) # {1, 2, 3}
xs = {1, 2, 3}
ys = {f(x) for x in xs}
To bytes. Depends on the desired encoding.
bytes = str.encode("utf-8") # utf-8 is the default
From bytes:
str = bytes.decode()
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)
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
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)
if isinstance(obj, MyClass):
print("is of type MyClass")
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()
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)]
@dataclass
class Point:
x: int
y: int
z: int = 0
p = Point(x=10, y=20)
print(p.z) # 0
dataclasses.replace
:
p = Point(1, 2)
p_copy = dataclasses.replace(p)
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')
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
out = io.StringIO()
with redirect_stdout:
# call code which prints to stdout
execute()
stdout_str = out.getValue()
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())
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+
.
def f(*, a, b):
pass
f(a=1, b=2) # ok
f(1, 2) # error
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
Primitives like bool
, int
, str
, etc. are not implicitly bound to functions in the same scope like list
s are. Declaring it nonlocal
binds it explicitly.
def f():
x = 0
def inc():
nonlocal x
x += 1
inc()
print(x) # 1
Current time:
datetime.now()
Adding/subtracting time:
datetime.now() + datetime.timedelta(days=1)
Wall-clock time in seconds:
start = time.time()
call()
elapsed_secs = time.time() - start
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()
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()
from pathlib import Path
p = Path('/etc')
p = p / 'dir' # Path('/etc/dir')
Note the overloaded operator /
. Works with files too:
p = p / 'file.txt' # Path('/etc/file.txt')
Path('/home/file.txt').name # 'file.txt'
Note: this also returns the leaf directory:
Path('/home/me/').name # 'me'
It includes the .
.
Path('/home/me/file.txt').suffix # '.txt'
p = Path('/dev/null').parent # Path('dev/')
p = Path('.').resolve() # absolute path
print(str(Path('/dev/null'))) # dev/null
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)
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()
Keywords: regular expression
text = "the number is 12345"
if re.match('is ([0-9]+)', text):
print("matches")
text = "the number is 12345"
result = re.search('is ([0-9]+)', text, re.IGNORECASE)
if result:
print(result.group(1)) # 12345