diff --git a/README.md b/README.md index e646a6c15..15a84eac0 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ Contents -------- **   ** **1. Collections:** ** ** **[`List`](#list)**__,__ **[`Dictionary`](#dictionary)**__,__ **[`Set`](#set)**__,__ **[`Tuple`](#tuple)**__,__ **[`Range`](#range)**__,__ **[`Enumerate`](#enumerate)**__,__ **[`Iterator`](#iterator)**__,__ **[`Generator`](#generator)**__.__ **   ** **2. Types:** **          ** **[`Type`](#type)**__,__ **[`String`](#string)**__,__ **[`Regular_Exp`](#regex)**__,__ **[`Format`](#format)**__,__ **[`Numbers`](#numbers-1)**__,__ **[`Combinatorics`](#combinatorics)**__,__ **[`Datetime`](#datetime)**__.__ -**   ** **3. Syntax:** **         ** **[`Args`](#arguments)**__,__ **[`Inline`](#inline)**__,__ **[`Import`](#imports)**__,__ **[`Decorator`](#decorator)**__,__ **[`Class`](#class)**__,__ **[`Duck_Types`](#duck-types)**__,__ **[`Enum`](#enum)**__,__ **[`Exception`](#exceptions)**__.__ +**   ** **3. Syntax:** **         ** **[`Function`](#function)**__,__ **[`Inline`](#inline)**__,__ **[`Import`](#imports)**__,__ **[`Decorator`](#decorator)**__,__ **[`Class`](#class)**__,__ **[`Duck_Type`](#duck-types)**__,__ **[`Enum`](#enum)**__,__ **[`Except`](#exceptions)**__.__ **   ** **4. System:** **        ** **[`Exit`](#exit)**__,__ **[`Print`](#print)**__,__ **[`Input`](#input)**__,__ **[`Command_Line_Arguments`](#command-line-arguments)**__,__ **[`Open`](#open)**__,__ **[`Path`](#paths)**__,__ **[`OS_Commands`](#os-commands)**__.__ **   ** **5. Data:** **             ** **[`JSON`](#json)**__,__ **[`Pickle`](#pickle)**__,__ **[`CSV`](#csv)**__,__ **[`SQLite`](#sqlite)**__,__ **[`Bytes`](#bytes)**__,__ **[`Struct`](#struct)**__,__ **[`Array`](#array)**__,__ **[`Memory_View`](#memory-view)**__,__ **[`Deque`](#deque)**__.__ **   ** **6. Advanced:** **   ** **[`Operator`](#operator)**__,__ **[`Match_Stmt`](#match-statement)**__,__ **[`Logging`](#logging)**__,__ **[`Introspection`](#introspection)**__,__ **[`Threading`](#threading)**__,__ **[`Coroutines`](#coroutines)**__.__ @@ -21,37 +21,37 @@ Contents Main ---- ```python -if __name__ == '__main__': # Skips next line if file was imported. - main() # Runs `def main(): ...` function. +if __name__ == '__main__': # Skips indented lines of code if file was imported. + main() # Executes user-defined `def main(): ...` function. ``` List ---- ```python - = [, , ...] # Creates new list. Also list(). + = [, , ...] # Creates a new list object. Also list(). ``` ```python - = [index] # First index is 0. Last -1. Allows assignments. + = [index] # First index is 0, last -1. Also `[i] = `. = [] # Also [from_inclusive : to_exclusive : ±step]. ``` ```python -.append() # Appends element to the end. Also += []. -.extend() # Appends elements to the end. Also += . +.append() # Appends element to the end. Also ` += []`. +.extend() # Appends multiple elements. Also ` += `. ``` ```python -.sort() # Sorts elements in ascending order. -.reverse() # Reverses the list in-place. - = sorted() # Returns new list with sorted elements. - = reversed() # Returns reversed iterator of elements. +.sort(reverse=False) # Sorts the elements of the list in ascending order. +.reverse() # Reverses the order of elements. Takes linear time. + = sorted() # Returns a new sorted list. Accepts `reverse=True`. + = reversed() # Returns reversed iterator. Also list(). ``` ```python - = max() # Returns largest element. Also min(, ...). - = sum() # Returns sum of elements. Also math.prod(). + = max() # Returns the largest element. Also min(, ...). + = sum() # Returns a sum of elements. Also math.prod(). ``` ```python @@ -69,42 +69,42 @@ flatter_list = list(itertools.chain.from_iterable()) = .count() # Returns number of occurrences. Also `if in : ...`. = .index() # Returns index of the first occurrence or raises ValueError. = .pop() # Removes and returns item from the end or at index if passed. -.insert(, ) # Inserts item at index and moves the rest to the right. -.remove() # Removes first occurrence of the item or raises ValueError. -.clear() # Removes all items. Also works on dictionary and set. +.insert(, ) # Inserts item at passed index and moves the rest to the right. +.remove() # Removes the first occurrence or raises ValueError exception. +.clear() # Removes all list's items. Also works on dictionary and set. ``` Dictionary ---------- ```python - = {key_1: val_1, key_2: val_2, ...} # Use `[key]` to get or set the value. + = {key_1: val_1, key_2: val_2, ...} # Use `[key]` to get or assign the value. ``` ```python - = .keys() # Collection of keys that reflects changes. - = .values() # Collection of values that reflects changes. + = .keys() # A collection of keys that reflects changes. + = .values() # A collection of values that reflects changes. = .items() # Coll. of key-value tuples that reflects chgs. ``` ```python -value = .get(key, default=None) # Returns default if key is missing. +value = .get(key, default=None) # Returns argument default if key is missing. value = .setdefault(key, default=None) # Returns and writes default if key is missing. - = collections.defaultdict() # Returns a dict with default value `()`. - = collections.defaultdict(lambda: 1) # Returns a dict with default value 1. + = collections.defaultdict() # Dict with automatic default value `()`. + = collections.defaultdict(lambda: 1) # Dictionary with automatic default value 1. ``` ```python = dict() # Creates a dict from coll. of key-value pairs. - = dict(zip(keys, values)) # Creates a dict from two collections. - = dict.fromkeys(keys [, value]) # Creates a dict from collection of keys. + = dict(zip(keys, values)) # Creates a dictionary from two collections. + = dict.fromkeys(keys [, value]) # Creates a dictionary from collection of keys. ``` ```python .update() # Adds items. Replaces ones with matching keys. value = .pop(key) # Removes item or raises KeyError if missing. {k for k, v in .items() if v == value} # Returns set of keys that point to the value. -{k: v for k, v in .items() if k in keys} # Filters the dictionary by keys. +{k: v for k, v in .items() if k in keys} # Returns a dict of items with specified keys. ``` ### Counter @@ -120,32 +120,32 @@ value = .pop(key) # Removes item or raises KeyErro Set --- ```python - = {, , ...} # Use `set()` for empty set. + = {, , ...} # Coll. of unique items. Also set(), set(). ``` ```python -.add() # Or: |= {} -.update( [, ...]) # Or: |= +.add() # Adds item to the set. Same as ` |= {}`. +.update( [, ...]) # Adds items to the set. Same as ` |= `. ``` ```python - = .union() # Or: | - = .intersection() # Or: & - = .difference() # Or: - - = .symmetric_difference() # Or: ^ - = .issubset() # Or: <= - = .issuperset() # Or: >= + = .union() # Returns a set of all items. Also | . + = .intersection() # Returns all shared items. Also & . + = .difference() # Returns set's unique items. Also - . + = .symmetric_difference() # Returns non-shared items. Also ^ . + = .issuperset() # Returns False if collection has unique items. + = .issubset() # Is collection a superset? Also <= . ``` ```python - = .pop() # Raises KeyError if empty. -.remove() # Raises KeyError if missing. -.discard() # Doesn't raise an error. + = .pop() # Removes and returns an item or raises KeyError. +.remove() # Removes the item or raises KeyError if missing. +.discard() # Same as remove() but it doesn't raise an error. ``` ### Frozen Set * **Is immutable and hashable.** -* **That means it can be used as a key in a dictionary or as an element in a set.** +* **That means it can be used as a key in a dictionary or as an item in a set.** ```python = frozenset() ``` @@ -155,9 +155,9 @@ Tuple ----- **Tuple is an immutable and hashable list.** ```python - = () # Empty tuple. - = (,) # Or: , - = (, [, ...]) # Or: , [, ...] + = () # Returns an empty tuple. Also tuple(), tuple(). + = (,) # Returns a tuple with single element. Same as `,`. + = (, [, ...]) # Returns a tuple. Same as `, [, ...]`. ``` ### Named Tuple @@ -165,14 +165,11 @@ Tuple ```python >>> from collections import namedtuple >>> Point = namedtuple('Point', 'x y') ->>> p = Point(1, y=2); p +>>> p = Point(1, y=2) +>>> print(p) Point(x=1, y=2) ->>> p[0] -1 ->>> p.x -1 ->>> getattr(p, 'y') -2 +>>> p.x, p[1] +(1, 2) ``` @@ -180,9 +177,9 @@ Range ----- **Immutable and hashable sequence of integers.** ```python - = range(stop) # range(to_exclusive) - = range(start, stop) # range(from_inclusive, to_exclusive) - = range(start, stop, ±step) # range(from_inclusive, to_exclusive, ±step_size) + = range(stop) # I.e. range(to_exclusive). Integers from 0 to `stop-1`. + = range(start, stop) # I.e. range(from_inc, to_exc). From start to `stop-1`. + = range(start, stop, ±step) # I.e. range(from_inclusive, to_exclusive, ±step_size). ``` ```python @@ -201,8 +198,10 @@ for i, el in enumerate(, start=0): # Returns next element and its index Iterator -------- +**Potentially endless stream of elements.** + ```python - = iter() # `iter()` returns unmodified iterator. + = iter() # Calling iter() returns unmodified iterator. = iter(, to_exclusive) # A sequence of return values until 'to_exclusive'. = next( [, default]) # Raises StopIteration or returns 'default' on end. = list() # Returns a list of iterator's remaining elements. @@ -215,18 +214,18 @@ import itertools as it ```python = it.count(start=0, step=1) # Returns updated value endlessly. Accepts floats. - = it.repeat( [, times]) # Returns element endlessly or 'times' times. - = it.cycle() # Repeats the sequence endlessly. + = it.repeat( [, times]) # Returns passed element endlessly or 'times' times. + = it.cycle() # Repeats the passed sequence of elements endlessly. ``` ```python - = it.chain(, [, ...]) # Empties collections in order (figuratively). + = it.chain(, [, ...]) # Empties collections in order (only figuratively). = it.chain.from_iterable() # Empties collections inside a collection in order. ``` ```python - = it.islice(, to_exclusive) # Only returns first 'to_exclusive' elements. - = it.islice(, from_inc, …) # `to_exclusive, +step_size`. Indices can be None. + = it.islice(, stop) # Only returns (i.e. yields) first 'stop' elements. + = it.islice(, start, stop) # Also accepts `+step`. Start and stop can be None. ``` @@ -251,13 +250,13 @@ def count(start, step): Type ---- -* **Everything is an object.** -* **Every object has a type.** +* **Everything in Python is an object.** +* **Every object has a certain type.** * **Type and class are synonymous.** ```python - = type() # Or: .__class__ - = isinstance(, ) # Or: issubclass(type(), ) + = type() # Returns object's type. Same as `.__class__`. + = isinstance(, ) # Same result as `issubclass(type(), )`. ``` ```python @@ -311,6 +310,9 @@ True String ------ **Immutable sequence of characters.** +```python + = 'abc' # Also "abc". Interprets \n, \t, \x00-\xff, etc. +``` ```python = .strip() # Strips all whitespace characters from both ends. @@ -318,40 +320,39 @@ String ``` ```python - = .split() # Splits on one or more whitespace characters. - = .split(sep=None, maxsplit=-1) # Splits on 'sep' str at most 'maxsplit' times. + = .split() # Splits it on one or more whitespace characters. + = .split(sep=None, maxsplit=-1) # Splits on 'sep' string at most 'maxsplit' times. = .splitlines(keepends=False) # On [\n\r\f\v\x1c-\x1e\x85\u2028\u2029] and \r\n. - = .join() # Joins elements using string as a separator. + = .join() # Joins items by using the string as a separator. ``` ```python - = in # Checks if string contains the substring. - = .startswith() # Pass tuple of strings for multiple options. + = in # Returns True if string contains the substring. + = .startswith() # Pass tuple of strings to give multiple options. = .find() # Returns start index of the first match or -1. - = .index() # Same, but raises ValueError if there's no match. ``` ```python - = .lower() # Changes the case. Also upper/capitalize/title(). + = .lower() # Lowers the case. Also upper/capitalize/title(). + = .casefold() # Lower() that converts ẞ/ß to ss, Σ/ς to σ, etc. = .replace(old, new [, count]) # Replaces 'old' with 'new' at most 'count' times. - = .translate() # Use `str.maketrans()` to generate table. + = .translate(table) # Use `str.maketrans()` to generate table. ``` ```python - = chr() # Converts int to Unicode character. - = ord() # Converts Unicode character to int. + = chr() # Converts passed integer into Unicode character. + = ord() # Converts passed Unicode character into integer. ``` * **Use `'unicodedata.normalize("NFC", )'` on strings like `'Motörhead'` before comparing them to other strings, because `'ö'` can be stored as one or two characters.** * **`'NFC'` converts such characters to a single character, while `'NFD'` converts them to two.** -### Property Methods ```python - = .isdecimal() # Checks for [0-9]. Also [०-९] and [٠-٩]. - = .isdigit() # Checks for [²³¹…] and isdecimal(). - = .isnumeric() # Checks for [¼½¾…], [零〇一…] and isdigit(). - = .isalnum() # Checks for [a-zA-Z…] and isnumeric(). - = .isprintable() # Checks for [ !#$%…] and isalnum(). - = .isspace() # Checks for [ \t\n\r\f\v\x1c-\x1f\x85\xa0…]. + = .isdecimal() # Checks all chars for [0-9]. Also [०-९], [٠-٩]. + = .isdigit() # Checks for [²³¹…] and isdecimal(). Also [፩-፱]. + = .isnumeric() # Checks for [¼½¾…] and isdigit(). Also [零〇一…]. + = .isalnum() # Checks for [ABC…] and isnumeric(). Also [ªµº…]. + = .isprintable() # Checks for [ !"#$…] and isalnum(). Also emojis. + = .isspace() # Checks for [ \t\n\r\f\v\x1c\x1d\x1e\x1f\x85…]. ``` @@ -362,7 +363,7 @@ Regex ```python import re = re.sub(r'', new, text, count=0) # Substitutes all occurrences with 'new'. - = re.findall(r'', text) # Returns all occurrences as strings. + = re.findall(r'', text) # Returns all occurrences of the pattern. = re.split(r'', text, maxsplit=0) # Add brackets around regex to keep matches. = re.search(r'', text) # First occurrence of the pattern or None. = re.match(r'', text) # Searches only at the beginning of the text. @@ -371,18 +372,18 @@ import re * **Raw string literals do not interpret escape sequences, thus enabling us to use regex-specific escape sequences that cause SyntaxWarning in normal string literals (since 3.12).** * **Argument 'new' of re.sub() can be a function that accepts Match object and returns a str.** -* **Argument `'flags=re.IGNORECASE'` can be used with all functions.** +* **Argument `'flags=re.IGNORECASE'` can be used with all functions that are listed above.** * **Argument `'flags=re.MULTILINE'` makes `'^'` and `'$'` match the start/end of each line.** -* **Argument `'flags=re.DOTALL'` makes `'.'` also accept the `'\n'`.** -* **`'re.compile()'` returns a Pattern object with methods sub(), findall(), …** +* **Argument `'flags=re.DOTALL'` makes `'.'` also accept the `'\n'` (besides all other chars).** +* **`'re.compile()'` returns a Pattern object with methods sub(), findall(), etc.** ### Match Object ```python = .group() # Returns the whole match. Also group(0). = .group(1) # Returns part inside the first brackets. - = .groups() # Returns all bracketed parts. - = .start() # Returns start index of the match. - = .end() # Returns exclusive end index of the match. + = .groups() # Returns all bracketed parts as strings. + = .start() # Returns start index of the whole match. + = .end() # Returns its exclusive end index. ``` ### Special Sequences @@ -398,8 +399,8 @@ import re Format ------ ```perl - = f'{}, {}' # Curly brackets can also contain expressions. - = '{}, {}'.format(, ) # Or: '{0}, {a}'.format(, a=) + = f'{}, {}' # Curly braces can also contain expressions. + = '{}, {}'.format(, ) # Same as '{0}, {a}'.format(, a=). = '%s, %s' % (, ) # Redundant and inferior C-style formatting. ``` @@ -419,8 +420,8 @@ Format {:.<10} # '......' {:0} # '' ``` -* **Objects are rendered using `'format(, "")'`.** -* **Options can be generated dynamically: `f'{:{}[…]}'`.** +* **Objects are rendered by calling the `'format(, "")'` function.** +* **Options inside curly braces can be generated dynamically: `f'{:{}[…]}'`.** * **Adding `'='` to the expression prepends it to the output: `f'{1+1=}'` returns `'1+1=2'`.** * **Adding `'!r'` to the expression converts object to string by calling its [repr()](#class) method.** @@ -485,68 +486,78 @@ Format ### Ints ```python -{90:c} # 'Z'. Unicode character with value 90. -{90:b} # '1011010'. Number 90 in binary. -{90:X} # '5A'. Number 90 in uppercase hexadecimal. +{90:c} # 'Z'. Returns Unicode character with value 90. +{90:b} # '1011010'. Binary representation of the int. +{90:X} # '5A'. Hexadecimal with upper-case letters. ``` Numbers ------- ```python - = int() # Or: math.trunc() - = float() # Or: - = complex(real=0, imag=0) # Or: ± j - = fractions.Fraction(0, 1) # Or: Fraction(numerator=0, denominator=1) - = decimal.Decimal() # Or: Decimal((sign, digits, exponent)) + = int() # Whole number of any size. Truncates floats. + = float() # 64-bit decimal number. Also . + = complex(real=0, imag=0) # A complex number. Also ` ± j`. + = fractions.Fraction(, ) # E.g. `Fraction(1, 2) / 3 == Fraction(1, 6)`. + = decimal.Decimal() # E.g. `Decimal((1, (2, 3), 4)) == -230_000`. ``` -* **`'int()'` and `'float()'` raise ValueError on malformed strings.** -* **Decimal numbers are stored exactly, unlike most floats where `'1.1 + 2.2 != 3.3'`.** -* **Floats can be compared with: `'math.isclose(, )'`.** +* **`'int()'` and `'float()'` raise ValueError if passed string is malformed.** +* **Decimal objects store numbers exactly, unlike most floats where `'1.1 + 2.2 != 3.3'`.** +* **Floats can be compared with: `'math.isclose(, , rel_tol=1e-09)'`.** * **Precision of decimal operations is set with: `'decimal.getcontext().prec = '`.** +* **Bools can be used anywhere ints can, because bool is a subclass of int: `'True + 1 == 2'`.** -### Basic Functions +### Built-in Functions ```python - = pow(, ) # Or: ** - = abs() # = abs() - = round( [, ±ndigits]) # `round(126, -1) == 130` + = pow(, ) # E.g. `pow(2, 3) == 2 ** 3 == 8`. + = abs() # E.g. `abs(complex(3, 4)) == 5`. + = round( [, ±ndigits]) # E.g. `round(123, -1) == 120`. + = min() # Also max(, [, ...]). + = sum() # Also math.prod(). ``` ### Math ```python -from math import e, pi, inf, nan, isinf, isnan # ` == nan` is always False. -from math import sin, cos, tan, asin, acos, atan # Also: degrees, radians. -from math import log, log10, log2 # Log can accept base as second arg. +from math import floor, ceil, trunc # They convert floats into integers. +from math import pi, inf, nan, isnan # `inf * 0` and `nan + 1` return nan. +from math import sqrt, factorial # `sqrt(-1)` will raise ValueError. +from math import sin, cos, tan # Also: asin, acos, degrees, radians. +from math import log, log10, log2 # Log accepts base as second argument. ``` ### Statistics ```python -from statistics import mean, median, variance # Also: stdev, quantiles, groupby. +from statistics import mean, median, mode # Mode returns the most common item. +from statistics import variance, stdev # Also: pvariance, pstdev, quantiles. ``` ### Random ```python -from random import random, randint, choice # Also: shuffle, gauss, triangular, seed. - = random() # A float inside [0, 1). - = randint(from_inc, to_inc) # An int inside [from_inc, to_inc]. - = choice() # Keeps the sequence intact. +from random import random, randint, uniform # Also: gauss, choice, shuffle, seed. ``` -### Bin, Hex ```python - = ±0b # Or: ±0x - = int('±', 2) # Or: int('±', 16) - = int('±0b', 0) # Or: int('±0x', 0) - = bin() # Returns '[-]0b'. Also hex(). + = random() # Select a random float from [0, 1). + = randint/uniform(a, b) # Select an int/float from [a, b]. + = gauss(mean, stdev) # Also triangular(low, high, mode). + = choice() # Keeps it intact. Also sample(p, n). +shuffle() # Works on all mutable sequences. +``` + +### Hexadecimal Numbers +```python + = 0x # E.g. `0xFF == 255`. Also 0b. + = int('±', 16) # Also int('±0x/±0b', 0). + = hex() # Returns '[-]0x'. Also bin(). ``` ### Bitwise Operators ```python - = & # And (0b1100 & 0b1010 == 0b1000). - = | # Or (0b1100 | 0b1010 == 0b1110). - = ^ # Xor (0b1100 ^ 0b1010 == 0b0110). - = << n_bits # Left shift. Use >> for right. - = ~ # Not. Also - - 1. + = & # E.g. `0b1100 & 0b1010 == 0b1000`. + = | # E.g. `0b1100 | 0b1010 == 0b1110`. + = ^ # E.g. `0b1100 ^ 0b1010 == 0b0110`. + = << n_bits # E.g. `0b1111 << 4 == 0b11110000`. + = ~ # E.g. `~0b1 == -0b10 == -(0b1+1)`. ``` @@ -557,36 +568,24 @@ import itertools as it ``` ```python ->>> list(it.product([0, 1], repeat=3)) -[(0, 0, 0), (0, 0, 1), (0, 1, 0), (0, 1, 1), - (1, 0, 0), (1, 0, 1), (1, 1, 0), (1, 1, 1)] +>>> list(it.product('abc', repeat=2)) # a b c +[('a', 'a'), ('a', 'b'), ('a', 'c'), # a x x x + ('b', 'a'), ('b', 'b'), ('b', 'c'), # b x x x + ('c', 'a'), ('c', 'b'), ('c', 'c')] # c x x x ``` ```python ->>> list(it.product('abc', 'abc')) # a b c -[('a', 'a'), ('a', 'b'), ('a', 'c'), # a x x x - ('b', 'a'), ('b', 'b'), ('b', 'c'), # b x x x - ('c', 'a'), ('c', 'b'), ('c', 'c')] # c x x x +>>> list(it.permutations('abc', 2)) # a b c +[('a', 'b'), ('a', 'c'), # a . x x + ('b', 'a'), ('b', 'c'), # b x . x + ('c', 'a'), ('c', 'b')] # c x x . ``` ```python ->>> list(it.combinations('abc', 2)) # a b c -[('a', 'b'), ('a', 'c'), # a . x x - ('b', 'c')] # b . . x -``` - -```python ->>> list(it.combinations_with_replacement('abc', 2)) # a b c -[('a', 'a'), ('a', 'b'), ('a', 'c'), # a x x x - ('b', 'b'), ('b', 'c'), # b . x x - ('c', 'c')] # c . . x -``` - -```python ->>> list(it.permutations('abc', 2)) # a b c -[('a', 'b'), ('a', 'c'), # a . x x - ('b', 'a'), ('b', 'c'), # b x . x - ('c', 'a'), ('c', 'b')] # c x x . +>>> list(it.combinations('abc', 2)) # a b c +[('a', 'b'), ('a', 'c'), # a . x x + ('b', 'c') # b . . x +] # c . . . ``` @@ -606,10 +605,10 @@ import zoneinfo, dateutil.tz
= datetime(year, month, day, hour=0) # Also: `minute=0, second=0, microsecond=0, …`.
= timedelta(weeks=0, days=0, hours=0) # Also: `minutes=0, seconds=0, microseconds=0`. ``` -* **Aware times and datetimes have defined timezone, while naive don't. If object is naive, it is presumed to be in the system's timezone!** -* **`'fold=1'` means the second pass in case of time jumping back for one hour.** +* **Times and datetimes that have defined timezone are called aware and ones that don't, naive. If time or datetime object is naive, it is presumed to be in the system's timezone!** +* **`'fold=1'` means the second pass in case of time jumping back (usually for one hour).** * **Timedelta normalizes arguments to ±days, seconds (< 86 400) and microseconds (< 1M). Its str() method returns `'[±D, ]H:MM:SS[.…]'` and total_seconds() a float of all seconds.** -* **Use `'.weekday()'` to get the day of the week as an int, with Monday being 0.** +* **Use `'.weekday()'` to get the day of the week as an integer, with Monday being 0.** ### Now ```python @@ -618,10 +617,10 @@ import zoneinfo, dateutil.tz ``` * **To extract time use `'.time()'`, `'.time()'` or `'.timetz()'`.** -### Timezone +### Timezones ```python - = timezone.utc # London without daylight saving time (DST). - = timezone() # Timezone with fixed offset from UTC. + = timezone.utc # Coordinated universal time (UK without DST). + = timezone() # Timezone with fixed offset from universal time. = dateutil.tz.tzlocal() # Local timezone with dynamic offset from UTC. = zoneinfo.ZoneInfo('') # 'Continent/City_Name' zone with dynamic offset. =
.astimezone([]) # Converts DT to the passed or local fixed zone. @@ -633,7 +632,7 @@ import zoneinfo, dateutil.tz ### Encode ```python = D/T/DT.fromisoformat() # Object from ISO string. Raises ValueError. -
= DT.strptime(, '') # Datetime from str, according to format. +
= DT.strptime(, '') # Datetime from custom string. See Format. = D/DT.fromordinal() # D/DT from days since the Gregorian NYE 1. = DT.fromtimestamp() # Local naive DT from seconds since the Epoch. = DT.fromtimestamp(, ) # Aware datetime from seconds since the Epoch. @@ -643,9 +642,9 @@ import zoneinfo, dateutil.tz ### Decode ```python - = .isoformat(sep='T') # Also `timespec='auto/hours/minutes/seconds/…'`. + = .isoformat(sep='T') # Also `timespec='auto/hours/minutes/seconds…'`. = .strftime('') # Custom string representation of the object. - = .toordinal() # Days since Gregorian NYE 1, ignoring time and tz. + = .toordinal() # Days since NYE 1. Ignores DT's time and zone. = .timestamp() # Seconds since the Epoch, from local naive DT. = .timestamp() # Seconds since the Epoch, from aware datetime. ``` @@ -656,53 +655,53 @@ import zoneinfo, dateutil.tz >>> dt.strftime("%dth of %B '%y (%a), %I:%M %p %Z") "14th of August '25 (Thu), 11:39 PM UTC+02:00" ``` -* **`'%z'` accepts `'±HH[:]MM'` and returns `'±HHMM'` or empty string if datetime is naive.** -* **`'%Z'` accepts `'UTC/GMT'` and local timezone's code and returns timezone's name, `'UTC[±HH:MM]'` if timezone is nameless, or an empty string if datetime is naive.** +* **`'%z'` accepts `'±HH[:]MM'` and returns `'±HHMM'` or empty string if object is naive.** +* **`'%Z'` accepts `'UTC/GMT'` and local timezone's code and returns timezone's name, `'UTC[±HH:MM]'` if timezone is nameless, or an empty string if object is naive.** ### Arithmetics ```python = > # Ignores time jumps (fold attribute). Also ==. = > # Ignores time jumps if they share tzinfo object.
= - # Ignores jumps. Convert to UTC for actual delta. - = - # Ignores jumps if they share tzinfo object. + = - # Ignores jumps if they share the tzinfo object. = ± # Returned datetime can fall into missing hour. = * # Also ` = abs()`, ` = ± `. = / # Also `(, ) = divmod(, )`. ``` -Arguments ---------- -### Inside Function Call +Function +-------- +**Independent block of code that returns a value when called.** ```python -func() # func(0, 0) -func() # func(x=0, y=0) -func(, ) # func(0, y=0) +def (): ... # E.g. `def func(x, y): ...`. +def (): ... # E.g. `def func(x=0, y=0): ...`. +def (, ): ... # E.g. `def func(x, y=0): ...`. ``` +* **Function returns None if it doesn't encounter `'return '` statement.** +* **Run `'global '` inside the function before assigning to global variable.** +* **Default values are evaluated when function is first encountered in the scope. Any mutation of a mutable default value will persist between invocations!** + +### Function Call -### Inside Function Definition ```python -def func(): ... # def func(x, y): ... -def func(): ... # def func(x=0, y=0): ... -def func(, ): ... # def func(x, y=0): ... + = () # E.g. `func(0, 0)`. + = () # E.g. `func(x=0, y=0)`. + = (, ) # E.g. `func(0, y=0)`. ``` -* **Default values are evaluated when function is first encountered in the scope.** -* **Any mutation of a mutable default value will persist between invocations!** Splat Operator -------------- -### Inside Function Call **Splat expands a collection into positional arguments, while splatty-splat expands a dictionary into keyword arguments.** ```python -args = (1, 2) -kwargs = {'x': 3, 'y': 4, 'z': 5} +args, kwargs = (1, 2), {'z': 3} func(*args, **kwargs) ``` #### Is the same as: ```python -func(1, 2, x=3, y=4, z=5) +func(1, 2, z=3) ``` ### Inside Function Definition @@ -717,40 +716,27 @@ def add(*a): 6 ``` -#### Legal argument combinations: -```python -def f(*args): ... # f(1, 2, 3) -def f(x, *args): ... # f(1, 2, 3) -def f(*args, z): ... # f(1, 2, z=3) -``` - -```python -def f(**kwargs): ... # f(x=1, y=2, z=3) -def f(x, **kwargs): ... # f(x=1, y=2, z=3) | f(1, y=2, z=3) -``` - -```python -def f(*args, **kwargs): ... # f(x=1, y=2, z=3) | f(1, y=2, z=3) | f(1, 2, z=3) | f(1, 2, 3) -def f(x, *args, **kwargs): ... # f(x=1, y=2, z=3) | f(1, y=2, z=3) | f(1, 2, z=3) | f(1, 2, 3) -def f(*args, y, **kwargs): ... # f(x=1, y=2, z=3) | f(1, y=2, z=3) -``` - -```python -def f(*, x, y, z): ... # f(x=1, y=2, z=3) -def f(x, *, y, z): ... # f(x=1, y=2, z=3) | f(1, y=2, z=3) -def f(x, y, *, z): ... # f(x=1, y=2, z=3) | f(1, y=2, z=3) | f(1, 2, z=3) +#### Allowed compositions of arguments and the ways they can be called: +```text ++---------------------------+--------------+--------------+----------------+ +| | func(1, 2) | func(1, y=2) | func(x=1, y=2) | ++---------------------------+--------------+--------------+----------------+ +| func(x, *args, **kwargs): | yes | yes | yes | +| func(*args, y, **kwargs): | | yes | yes | +| func(*, x, **kwargs): | | | yes | ++---------------------------+--------------+--------------+----------------+ ``` ### Other Uses ```python - = [* [, ...]] # Or: list() [+ ...] - = (*, [...]) # Or: tuple() [+ ...] - = {* [, ...]} # Or: set() [| ...] - = {** [, ...]} # Or: | ... + = [* [, ...]] # Or: list() [+ ...] + = (*, [...]) # Or: tuple() [+ ...] + = {* [, ...]} # Or: set() [| ...] + = {** [, ...]} # Or: | ... ``` ```python -head, *body, tail = # Head or tail can be omitted. +head, *body, tail = # Head or tail can be omitted. ``` @@ -764,10 +750,10 @@ Inline ### Comprehensions ```python - = [i+1 for i in range(10)] # Or: [1, 2, ..., 10] - = (i for i in range(10) if i > 5) # Or: iter([6, 7, 8, 9]) - = {i+5 for i in range(10)} # Or: {5, 6, ..., 14} - = {i: i*2 for i in range(10)} # Or: {0: 0, 1: 2, ..., 9: 18} + = [i+1 for i in range(10)] # Returns [1, 2, ..., 10]. + = (i for i in range(10) if i > 5) # Returns iter([6, 7, 8, 9]). + = {i+5 for i in range(10)} # Returns {5, 6, ..., 14}. + = {i: i*2 for i in range(10)} # Returns {0: 0, 1: 2, ..., 9: 18}. ``` ```python @@ -781,9 +767,9 @@ from functools import reduce ``` ```python - = map(lambda x: x + 1, range(10)) # Or: iter([1, 2, ..., 10]) - = filter(lambda x: x > 5, range(10)) # Or: iter([6, 7, 8, 9]) - = reduce(lambda out, x: out + x, range(10)) # Or: 45 + = map(lambda x: x + 1, range(10)) # Returns iter([1, 2, ..., 10]). + = filter(lambda x: x > 5, range(10)) # Returns iter([6, 7, 8, 9]). + = reduce(lambda out, x: out + x, range(10)) # Returns 45. Accepts 'initial'. ``` ### Any, All @@ -798,24 +784,32 @@ from functools import reduce ``` ```python ->>> [a if a else 'zero' for a in (0, 1, 2, 3)] # `any([0, '', [], None]) == False` +>>> [i if i else 'zero' for i in (0, 1, 2, 3)] # `any([0, '', [], None]) == False` ['zero', 1, 2, 3] ``` +### And, Or +```python + = and [and ...] # Returns first false or last object. + = or [or ...] # Returns first true or last object. +``` + +### Walrus Operator +```python +>>> [i for ch in '0123' if (i := int(ch)) > 0] # Assigns to variable mid-sentence. +[1, 2, 3] +``` + ### Named Tuple, Enum, Dataclass ```python from collections import namedtuple -Point = namedtuple('Point', 'x y') # Creates a tuple's subclass. +Point = namedtuple('Point', 'x y') # Creates tuple's subclass. point = Point(0, 0) # Returns its instance. -``` -```python from enum import Enum -Direction = Enum('Direction', 'N E S W') # Creates an enum. +Direction = Enum('Direction', 'N E S W') # Creates Enum's subclass. direction = Direction.N # Returns its member. -``` -```python from dataclasses import make_dataclass Player = make_dataclass('Player', ['loc', 'dir']) # Creates a class. player = Player(point, direction) # Returns its instance. @@ -827,13 +821,13 @@ Imports **Mechanism that makes code in one file available to another file.** ```python -import # Imports a built-in or '.py'. -import # Imports a built-in or '/__init__.py'. -import . # Imports a built-in or '/.py'. +import # Imports a built-in or '.py'. +import # Imports a built-in or '/__init__.py'. +import . # Imports a built-in or '/.py'. ``` -* **Package is a collection of modules, but it can also define its own objects.** +* **Package is a collection of modules, but it can also define its own functions, classes, etc.** * **On a filesystem this corresponds to a directory of Python files with an optional init script.** -* **Running `'import '` does not automatically provide access to the package's modules unless they are explicitly imported in its init script.** +* **Running `'import '` does not automatically provide access to the package's modules unless they are explicitly imported in the `'/__init__.py'` script.** * **Directory of the file that is passed to python command serves as a root of local imports.** * **For relative imports use `'from .[…][[.…]] import '`.** @@ -869,8 +863,7 @@ from functools import partial >>> multiply_by_3(10) 30 ``` -* **Partial is also useful in cases when a function needs to be passed as an argument because it enables us to set its arguments beforehand.** -* **A few examples being: `'defaultdict()'`, `'iter(, to_exc)'` and dataclass's `'field(default_factory=)'`.** +* **Partial is also useful in cases when a function needs to be passed as an argument because it enables us to set its arguments beforehand (`'collections.defaultdict()'`, `'iter(, to_exc)'` and `'dataclasses.field(default_factory=)'`).** ### Non-Local **If variable is being assigned to anywhere in the scope, it is regarded as a local variable, unless it is declared as a 'global' or a 'nonlocal'.** @@ -894,8 +887,7 @@ def get_counter(): Decorator --------- -* **A decorator takes a function, adds some functionality and returns it.** -* **It can be any [callable](#callable), but is usually implemented as a function that returns a [closure](#closure).** +**A decorator takes a function, adds some functionality and returns it. It can be any [callable](#callable), but is usually implemented as a function that returns a [closure](#closure).** ```python @decorator_name @@ -954,7 +946,7 @@ def debug(print_result=False): def add(x, y): return x + y ``` -* **Using only `'@debug'` to decorate the add() function would not work here, because debug would then receive the add() function as a 'print_result' argument. Decorators can however manually check if the argument they received is a function and act accordingly.** +* **Using only `'@debug'` to decorate the add() function would not work here, because debug would then receive the add() function as a 'print_result' argument. Decorators can how­ever manually check if the argument they received is a function and act accordingly.** Class @@ -981,9 +973,9 @@ class MyClass: >>> obj.a, str(obj), repr(obj) (1, '1', 'MyClass(1)') ``` -* **Return value of str() should be readable and of repr() unambiguous.** -* **If only repr() is defined, it will also be used for str().** -* **Methods decorated with `'@staticmethod'` do not receive 'self' nor 'cls' as their first argument.** +* **Methods whose names start and end with two underscores are called special methods. They are executed when object is passed to a built-in function or used as an operand, for example, `'print(a)'` calls `'a.__str__()'` and `'a + b'` calls `'a.__add__(b)'`.** +* **Methods decorated with `'@staticmethod'` receive neither 'self' nor 'cls' argument.** +* **Return value of str() special method should be readable and of repr() unambiguous. If only repr() is defined, it will also be used for str().** #### Expressions that call the str() method: ```python @@ -991,7 +983,6 @@ print() f'{}' logging.warning() csv.writer().writerow([]) -raise Exception() ``` #### Expressions that call the repr() method: @@ -999,33 +990,34 @@ raise Exception() print/str/repr([]) print/str/repr({: }) f'{!r}' -Z = dataclasses.make_dataclass('Z', ['a']); print/str/repr(Z()) ->>> +Z = make_dataclass('Z', ['a']); print/str/repr(Z()) ``` -### Inheritance +### Subclass +* **Inheritance is a mechanism that enables a class to extend some other class (that is, subclass to extend its parent), and by doing so inherit all of its methods and attributes.** +* **Subclass can then add its own methods and attributes or override inherited ones by reusing their names.** + ```python class Person: def __init__(self, name): self.name = name + def __repr__(self): + return f'Person({self.name!r})' + def __lt__(self, other): + return self.name < other.name class Employee(Person): def __init__(self, name, staff_num): super().__init__(name) self.staff_num = staff_num + def __repr__(self): + return f'Employee({self.name!r}, {self.staff_num})' ``` -#### Multiple inheritance: -```python -class A: pass -class B: pass -class C(A, B): pass -``` - -**MRO determines the order in which parent classes are traversed when searching for a method or an attribute:** ```python ->>> C.mro() -[, , , ] +>>> people = {Person('Ann'), Employee('Bob', 0)} +>>> sorted(people) +[Person('Ann'), Employee('Bob', 0)] ``` ### Type Annotations @@ -1034,9 +1026,9 @@ class C(A, B): pass ```python from collections import abc -: [| ...] [= ] # `|` since 3.10. -: list/set/abc.Iterable/abc.Sequence[] [= ] # Since 3.9. -: dict/tuple[, ...] [= ] # Since 3.9. +: [| ...] [= ] +: list/set/abc.Iterable/abc.Sequence[] [= ] +: tuple/dict[, ...] [= ] ``` ### Dataclass @@ -1052,13 +1044,13 @@ class : ``` * **Objects can be made [sortable](#sortable) with `'order=True'` and immutable with `'frozen=True'`.** * **For object to be [hashable](#hashable), all attributes must be hashable and 'frozen' must be True.** -* **Function field() is needed because `': list = []'` would make a list that is shared among all instances. Its 'default_factory' argument can be any [callable](#callable).** +* **Function field() is needed because `': list = []'` would make a list that is shared among all instances. Its 'default_factory' argument accepts any [callable](#callable) object.** * **For attributes of arbitrary type use `'typing.Any'`.** ```python -Point = make_dataclass('Point', ['x', 'y']) -Point = make_dataclass('Point', [('x', float), ('y', float)]) -Point = make_dataclass('Point', [('x', float, 0), ('y', float, 0)]) +P = make_dataclass('P', ['x', 'y']) +P = make_dataclass('P', [('x', float), ('y', float)]) +P = make_dataclass('P', [('x', float, 0), ('y', float, 0)]) ``` ### Property @@ -1082,7 +1074,7 @@ class Person: ``` ### Slots -**Mechanism that restricts objects to attributes listed in 'slots', reduces their memory footprint.** +**Mechanism that restricts objects to attributes listed in 'slots'.** ```python class MyClassWithSlots: @@ -1103,9 +1095,8 @@ Duck Types **A duck type is an implicit type that prescribes a set of special methods. Any object that has those methods defined is considered a member of that duck type.** ### Comparable -* **If eq() method is not overridden, it returns `'id(self) == id(other)'`, which is the same as `'self is other'`.** -* **That means all user-defined objects compare not equal by default.** -* **Only the left side object has eq() method called, unless it returns NotImplemented, in which case the right object is consulted. False is returned if both return NotImplemented.** +* **If eq() method is not overridden, it returns `'id(self) == id(other)'`, which is the same as `'self is other'`. That means all user-defined objects compare not equal by default (because id() returns object's memory address that is guaranteed to be unique).** +* **Only the left side object has eq() method called, unless it returns NotImplemented, in which case the right object is consulted. Result is False if both return NotImplemented.** * **Ne() automatically works on any object that has eq() defined.** ```python @@ -1119,9 +1110,8 @@ class MyComparable: ``` ### Hashable -* **Hashable object needs both hash() and eq() methods and its hash value should never change.** -* **Hashable objects that compare equal must have the same hash value, meaning default hash() that returns `'id(self)'` will not do.** -* **That is why Python automatically makes classes unhashable if you only implement eq().** +* **Hashable object needs both hash() and eq() methods and its hash value must not change.** +* **Hashable objects that compare equal must have the same hash value, meaning default hash() that returns `'id(self)'` will not do. That is why Python automatically makes classes unhashable if you only implement eq().** ```python class MyHashable: @@ -1139,7 +1129,7 @@ class MyHashable: ``` ### Sortable -* **With 'total_ordering' decorator, you only need to provide eq() and one of lt(), gt(), le() or ge() special methods and the rest will be automatically generated.** +* **With 'total_ordering' decorator, you only need to provide eq() and one of lt(), gt(), le() or ge() special methods (used by <, >, <=, >=) and the rest will be automatically generated.** * **Functions sorted() and min() only require lt() method, while max() only requires gt(). However, it is best to define them all so that confusion doesn't arise in other contexts.** * **When two lists, strings or dataclasses are compared, their values get compared in order until a pair of unequal values is found. The comparison of this two values is then returned. The shorter sequence is considered smaller in case of all values being equal.** * **To sort collection of strings in proper alphabetical order pass `'key=locale.strxfrm'` to sorted() after running `'locale.setlocale(locale.LC_COLLATE, "en_US.UTF-8")'`.** @@ -1164,7 +1154,8 @@ class MySortable: ### Iterator * **Any object that has methods next() and iter() is an iterator.** * **Next() should return next item or raise StopIteration exception.** -* **Iter() should return 'self', i.e. unmodified object on which it was called.** +* **Iter() should return an iterator of remaining items, i.e. 'self'.** +* **Any object that has iter() method can be used in a for loop.** ```python class Counter: def __init__(self): @@ -1183,14 +1174,14 @@ class Counter: ``` #### Python has many different iterator objects: -* **Sequence iterators returned by the [iter()](#iterator) function, such as list\_iterator and set\_iterator.** +* **Sequence iterators returned by the [iter()](#iterator) function, such as list\_iterator, etc.** * **Objects returned by the [itertools](#itertools) module, such as count, repeat and cycle.** * **Generators returned by the [generator functions](#generator) and [generator expressions](#comprehensions).** * **File objects returned by the [open()](#open) function, etc.** ### Callable -* **All functions and classes have a call() method, hence are callable.** -* **Use `'callable()'` or `'isinstance(, collections.abc.Callable)'` to check if object is callable.** +* **All functions and classes have a call() method that is executed when they are called.** +* **Use `'callable()'` or `'isinstance(, collections.abc.Callable)'` to check if object is callable. You can also call the object and check if it raised TypeError.** * **When this cheatsheet uses `''` as an argument, it means `''`.** ```python class Counter: @@ -1209,8 +1200,8 @@ class Counter: ### Context Manager * **With statements only work on objects that have enter() and exit() special methods.** -* **Enter() should lock the resources and optionally return an object.** -* **Exit() should release the resources.** +* **Enter() should lock the resources and optionally return an object (file, lock, etc.).** +* **Exit() should release the resources (for example close a file, release a lock, etc.).** * **Any exception that happens inside the with block is passed to the exit() method.** * **The exit() method can suppress the exception by returning a true value.** ```python @@ -1258,7 +1249,7 @@ True ### Collection * **Only required methods are iter() and len(). Len() should return the number of items.** -* **This cheatsheet actually means `''` when it uses `''`.** +* **This cheatsheet actually means `''` when it uses the `''`.** * **I chose not to use the name 'iterable' because it sounds scarier and more vague than 'collection'. The main drawback of this decision is that the reader could think a certain function doesn't accept iterators when it does, since iterators are the only built-in objects that are iterable but are not collections.** ```python class MyCollection: @@ -1273,8 +1264,7 @@ class MyCollection: ``` ### Sequence -* **Only required methods are getitem() and len().** -* **Getitem() should return an item at the passed index or raise IndexError.** +* **Only required methods are getitem() and len(). Getitem() should return the item at the passed index or raise IndexError (it may also support negative indices and/or slices).** * **Iter() and contains() automatically work on any object that has getitem() defined.** * **Reversed() automatically works on any object that has getitem() and len() defined. It returns reversed iterator of object's items.** ```python @@ -1298,8 +1288,8 @@ class MySequence: * **Passing ABC Iterable to isinstance() or issubclass() only checks whether object/class has special method iter(), while ABC Collection checks for iter(), contains() and len().** ### ABC Sequence -* **It's a richer interface than the basic sequence.** -* **Extending it generates iter(), contains(), reversed(), index() and count().** +* **It's a richer interface than the basic sequence that also requires just getitem() and len().** +* **Extending it generates iter(), contains(), reversed(), index() and count() special methods.** * **Unlike `'abc.Iterable'` and `'abc.Collection'`, it is not a duck type. That is why `'issubclass(MySequence, abc.Sequence)'` would return False even if MySequence had all the methods defined. It however recognizes list, tuple, range, str, bytes, bytearray, array, memoryview and deque, since they are registered as Sequence's virtual subclasses.** ```python from collections import abc @@ -1342,8 +1332,8 @@ from enum import Enum, auto ```python class (Enum): = auto() # Increment of the last numeric value or 1. - = # Values don't have to be hashable. - = , # Values can be collections (this is a tuple). + = # Values don't have to be hashable or unique. + = , # Values can be collections. This is a tuple. ``` * **Methods receive the member they were called on as the 'self' argument.** * **Accessing a member named after a reserved keyword causes SyntaxError.** @@ -1352,20 +1342,20 @@ class (Enum): = . # Returns a member. Raises AttributeError. = [''] # Returns a member. Raises KeyError. = () # Returns a member. Raises ValueError. - = .name # Returns member's name. - = .value # Returns member's value. + = .name # Returns the member's name. + = .value # Returns the member's value. ``` ```python - = list() # Returns enum's members. - = [a.name for a in ] # Returns enum's member names. - = [a.value for a in ] # Returns enum's member values. + = list() # Returns a list of enum's members. + = ._member_names_ # Returns a list of member names. + = [m.value for m in ] # Returns a list of member values. ``` ```python - = type() # Returns member's enum. - = itertools.cycle() # Returns endless iterator of members. - = random.choice(list()) # Returns a random member. + = type() # Returns an enum. Also .__class__. + = itertools.cycle() # Returns an endless iterator of members. + = random.choice(list()) # Randomly selects one of the members. ``` ### Inline @@ -1407,8 +1397,8 @@ finally: ``` * **Code inside the `'else'` block will only be executed if `'try'` block had no exceptions.** * **Code inside the `'finally'` block will always be executed (unless a signal is received).** -* **All variables that are initialized in executed blocks are also visible in all subsequent blocks, as well as outside the try statement (only function block delimits scope).** -* **To catch signals use `'signal.signal(signal_number, )'`.** +* **All variables that are initialized in executed blocks are also visible in all subsequent blocks, as well as outside the try statement (only the function block delimits scope).** +* **To catch signals use `'signal.signal(signal_number, handler_function)'`.** ### Catching Exceptions ```python @@ -1417,10 +1407,10 @@ except as : ... except (, [...]): ... except (, [...]) as : ... ``` -* **Also catches subclasses of the exception.** -* **Use `'traceback.print_exc()'` to print the full error message to stderr.** -* **Use `'print()'` to print just the cause of the exception (its arguments).** -* **Use `'logging.exception()'` to log the passed message, followed by the full error message of the caught exception. For details see [Logging](#logging).** +* **Also catches subclasses, e.g. `'IndexError'` is caught by `'except LookupError:'`.** +* **Use `'traceback.print_exc()'` to print the full error message to standard error stream.** +* **Use `'print()'` to print just the cause of the exception (that is, its arguments).** +* **Use `'logging.exception()'` to log the passed message, followed by the full error message of the caught exception. For details about setting up the logger see [Logging](#logging).** * **Use `'sys.exc_info()'` to get exception type, object, and traceback of caught exception.** ### Raising Exceptions @@ -1443,33 +1433,33 @@ arguments = .args exc_type = .__class__ filename = .__traceback__.tb_frame.f_code.co_filename func_name = .__traceback__.tb_frame.f_code.co_name -line = linecache.getline(filename, .__traceback__.tb_lineno) +line_str = linecache.getline(filename, .__traceback__.tb_lineno) trace_str = ''.join(traceback.format_tb(.__traceback__)) -error_msg = ''.join(traceback.format_exception(type(), , .__traceback__)) +error_msg = ''.join(traceback.format_exception(*sys.exc_info())) ``` ### Built-in Exceptions ```text BaseException - +-- SystemExit # Raised by the sys.exit() function. - +-- KeyboardInterrupt # Raised when the user hits the interrupt key (ctrl-c). + +-- SystemExit # Raised by the sys.exit() function (see #Exit for details). + +-- KeyboardInterrupt # Raised when the user hits the interrupt key (control-c). +-- Exception # User-defined exceptions should be derived from this class. +-- ArithmeticError # Base class for arithmetic errors such as ZeroDivisionError. +-- AssertionError # Raised by `assert ` if expression returns false value. +-- AttributeError # Raised when object doesn't have requested attribute/method. - +-- EOFError # Raised by input() when it hits an end-of-file condition. + +-- EOFError # Is raised by input() when it hits an end-of-file condition. +-- LookupError # Base class for errors when a collection can't find an item. - | +-- IndexError # Raised when a sequence index is out of range. - | +-- KeyError # Raised when a dictionary key or set element is missing. + | +-- IndexError # Raised when index of a sequence (list/str) is out of range. + | +-- KeyError # Raised when a dictionary key or a set element is missing. +-- MemoryError # Out of memory. May be too late to start deleting variables. +-- NameError # Raised when nonexistent name (variable/func/class) is used. | +-- UnboundLocalError # Raised when local name is used before it's being defined. - +-- OSError # Errors such as FileExistsError/TimeoutError (see #Open). - | +-- ConnectionError # Errors such as BrokenPipeError/ConnectionAbortedError. - +-- RuntimeError # Raised by errors that don't fall into other categories. + +-- OSError # Errors such as FileExistsError, TimeoutError (see #Open). + | +-- ConnectionError # Errors such as BrokenPipeError and ConnectionAbortedError. + +-- RuntimeError # Is raised by errors that don't fit into other categories. | +-- NotImplementedEr… # Can be raised by abstract methods or by unfinished code. - | +-- RecursionError # Raised when the maximum recursion depth is exceeded. - +-- StopIteration # Raised when an empty iterator is passed to next(). + | +-- RecursionError # Raised if max recursion depth is exceeded (3k by default). + +-- StopIteration # Raised when exhausted (empty) iterator is passed to next(). +-- TypeError # When an argument of the wrong type is passed to function. +-- ValueError # When argument has the right type but inappropriate value. ``` @@ -1488,8 +1478,8 @@ BaseException #### Useful built-in exceptions: ```python -raise TypeError('Argument is of the wrong type!') -raise ValueError('Argument has the right type but an inappropriate value!') +raise TypeError('Passed argument is of the wrong type!') +raise ValueError('Argument has the right type but its value is off!') raise RuntimeError('I am too lazy to define my own exception!') ``` @@ -1505,9 +1495,9 @@ Exit **Exits the interpreter by raising SystemExit exception.** ```python import sys -sys.exit() # Exits with exit code 0 (success). -sys.exit() # Exits with the passed exit code. -sys.exit() # Prints to stderr and exits with 1. +sys.exit() # Exits with exit code 0 (success). +sys.exit() # Exits with the passed exit code. +sys.exit() # Prints to stderr and exits with 1. ``` @@ -1516,7 +1506,7 @@ Print ```python print(, ..., sep=' ', end='\n', file=sys.stdout, flush=False) ``` -* **Use `'file=sys.stderr'` for messages about errors.** +* **Use `'file=sys.stderr'` for messages about errors so they can be processed separately.** * **Stdout and stderr streams hold output in a buffer until they receive a string containing '\n' or '\r', buffer reaches 4096 characters, `'flush=True'` is used, or program exits.** ### Pretty Print @@ -1535,7 +1525,7 @@ Input ``` * **Reads a line from the user input or pipe if present (trailing newline gets stripped).** * **If argument is passed, it gets printed to the standard output before input is read.** -* **EOFError is raised if user hits EOF (ctrl-d/ctrl-z⏎) or input stream gets exhausted.** +* **EOFError is raised if user hits EOF (ctrl-d/ctrl-z⏎) or if stream is already exhausted.** Command Line Arguments @@ -1549,19 +1539,19 @@ arguments = sys.argv[1:] ### Argument Parser ```python from argparse import ArgumentParser, FileType -p = ArgumentParser(description=) # Returns a parser. +p = ArgumentParser(description=) # Returns a parser object. p.add_argument('-', '--', action='https://github.com/store_true') # Flag (defaults to False). p.add_argument('-', '--', type=) # Option (defaults to None). p.add_argument('', type=, nargs=1) # Mandatory first argument. p.add_argument('', type=, nargs='+') # Mandatory remaining args. -p.add_argument('', type=, nargs='?/*') # Optional argument/s. +p.add_argument('', type=, nargs='?') # Optional argument. Also *. args = p.parse_args() # Exits on parsing error. = args. # Returns `()`. ``` * **Use `'help='` to set argument description that will be displayed in help message.** -* **Use `'default='` to set option's or optional argument's default value.** -* **Use `'type=FileType()'` for files. Accepts 'encoding', but 'newline' is None.** +* **Use `'default='` to override None as option's or optional argument's default value.** +* **Use `'type=FileType()'` for files. It accepts 'encoding', but 'newline' is None.** Open @@ -1571,7 +1561,7 @@ Open ```python = open(, mode='r', encoding=None, newline=None) ``` -* **`'encoding=None'` means that the default encoding is used, which is platform dependent. Best practice is to use `'encoding="utf-8"'` whenever possible.** +* **`'encoding=None'` means that the default encoding is used, which is platform dependent. Best practice is to use `'encoding="utf-8"'` until it becomes the default (Python 3.15).** * **`'newline=None'` means all different end of line combinations are converted to '\n' on read, while on write all '\n' characters are converted to system's default line separator.** * **`'newline=""'` means no conversions take place, but input is still broken into chunks by readline() and readlines() on every '\n', '\r' and '\r\n'.** @@ -1593,21 +1583,21 @@ Open ### File Object ```python -.seek(0) # Moves to the start of the file. +.seek(0) # Moves current position to the start of file. .seek(offset) # Moves 'offset' chars/bytes from the start. -.seek(0, 2) # Moves to the end of the file. +.seek(0, 2) # Moves current position to the end of file. .seek(±offset, origin) # Origin: 0 start, 1 current position, 2 end. ``` ```python - = .read(size=-1) # Reads 'size' chars/bytes or until EOF. + = .read(size=-1) # Reads 'size' chars/bytes or until the EOF. = .readline() # Returns a line or empty string/bytes on EOF. - = .readlines() # Returns a list of remaining lines. - = next() # Returns a line using buffer. Do not mix. + = .readlines() # Returns remaining lines. Also list(). + = next() # Returns a line using a read-ahead buffer. ``` ```python -.write() # Writes a string or bytes object. +.write() # Writes a str or bytes object to write buffer. .writelines() # Writes a coll. of strings or bytes objects. .flush() # Flushes write buffer. Runs every 4096/8192 B. .close() # Closes the file after flushing write buffer. @@ -1638,30 +1628,30 @@ from pathlib import Path ```python = os.getcwd() # Returns working dir. Starts as shell's $PWD. - = os.path.join(, ...) # Joins two or more pathname components. + = os.path.join(, ...) # Uses os.sep to join strings or Path objects. = os.path.realpath() # Resolves symlinks and calls path.abspath(). ``` ```python - = os.path.basename() # Returns final component of the path. - = os.path.dirname() # Returns path without the final component. + = os.path.basename() # Returns path's final component, i.e. file/dir. + = os.path.dirname() # Returns path with its final component removed. = os.path.splitext() # Splits on last period of the final component. ``` ```python - = os.listdir(path='.') # Returns filenames located at the path. + = os.listdir(path='.') # Returns all filenames located at the path. = glob.glob('') # Returns paths matching the wildcard pattern. ``` ```python - = os.path.exists() # Or: .exists() - = os.path.isfile() # Or: .is_file() - = os.path.isdir() # Or: .is_dir() + = os.path.exists() # Checks if path exists. Also .exists(). + = os.path.isfile() # Also .is_file() and .is_file(). + = os.path.isdir() # Also .is_dir() and .is_dir(). ``` ```python - = os.stat() # Or: .stat() - = .st_mtime/st_size/… # Modification time, size in bytes, etc. + = os.stat() # File's status. Also .stat(). + = .st_mtime/st_size/… # Returns modification time, size in bytes, etc. ``` ### DirEntry @@ -1669,9 +1659,9 @@ from pathlib import Path ```python = os.scandir(path='.') # Returns DirEntry objects located at the path. - = .path # Returns the whole path as a string. - = .name # Returns final component as a string. - = open() # Opens the file and returns a file object. + = .path # Is absolute if 'path' argument was absolute. + = .name # Returns path's final component as a string. + = open() # Opens the file and returns its file object. ``` ### Path Object @@ -1682,7 +1672,7 @@ from pathlib import Path ``` ```python - = Path() # Returns relative CWD. Also Path('.'). + = Path() # Returns current working dir. Also Path('.'). = Path.cwd() # Returns absolute CWD. Also Path().resolve(). = Path.home() # Returns user's home directory (absolute). = Path(__file__).resolve() # Returns module's path if CWD wasn't changed. @@ -1690,9 +1680,9 @@ from pathlib import Path ```python = .parent # Returns Path without the final component. - = .name # Returns final component as a string. - = .stem # Returns final component w/o last extension. - = .suffix # Returns last extension prepended with a dot. + = .name # Returns path's final component as a string. + = .suffix # Returns name's last extension, e.g. '.gz'. + = .stem # Returns name without the last extension. = .parts # Returns all path's components as strings. ``` @@ -1702,8 +1692,8 @@ from pathlib import Path ``` ```python - = str() # Returns path as a string. - = open() # Also .read/write_text/bytes(). + = str() # Returns path as string. Also .as_uri(). + = open() # Also .read_text/write_bytes(). ``` @@ -1714,39 +1704,39 @@ import os, shutil, subprocess ``` ```python -os.chdir() # Changes the current working directory. -os.mkdir(, mode=0o777) # Creates a directory. Permissions are in octal. -os.makedirs(, mode=0o777) # Creates all path's dirs. Also `exist_ok=False`. +os.chdir() # Changes the current working directory (CWD). +os.mkdir(, mode=0o777) # Creates a directory. Permissions are in octal. +os.makedirs(, mode=0o777) # Creates all path's dirs. Also `exist_ok=False`. ``` ```python -shutil.copy(from, to) # Copies the file. 'to' can exist or be a dir. -shutil.copy2(from, to) # Also copies creation and modification time. -shutil.copytree(from, to) # Copies the directory. 'to' must not exist. +shutil.copy(from, to) # Copies the file. 'to' can exist or be a dir. +shutil.copy2(from, to) # Also copies creation and modification time. +shutil.copytree(from, to) # Copies the directory. 'to' must not exist. ``` ```python -os.rename(from, to) # Renames/moves the file or directory. -os.replace(from, to) # Same, but overwrites file 'to' even on Windows. -shutil.move(from, to) # Rename() that moves into 'to' if it's a dir. +os.rename(from, to) # Renames or moves the file or directory 'from'. +os.replace(from, to) # Same, but overwrites file 'to' even on Windows. +shutil.move(from, to) # Rename() that moves into 'to' if it's a dir. ``` ```python -os.remove() # Deletes the file. -os.rmdir() # Deletes the empty directory. -shutil.rmtree() # Deletes the directory. +os.remove() # Deletes the file. Or `pip3 install send2trash`. +os.rmdir() # Deletes the empty directory or raises OSError. +shutil.rmtree() # Deletes the directory and all of its contents. ``` -* **Paths can be either strings, Paths, or DirEntry objects.** -* **Functions report OS related errors by raising either OSError or one of its [subclasses](#exceptions-1).** +* **Paths can be either strings, Path objects, or DirEntry objects.** +* **Functions report OS related errors by raising OSError or one of its [subclasses](#exceptions-1).** ### Shell Commands ```python - = os.popen('') # Executes commands in sh/cmd. Returns combined stdout. - = .read(size=-1) # Reads 'size' chars or until EOF. Also readline/s(). - = .close() # Returns None if last command exited with returncode 0. + = os.popen('') # Executes commands in sh/cmd. Returns combined stdout. + = .read(size=-1) # Reads 'size' chars or until EOF. Also readline/s(). + = .close() # Returns None if last command exited with returncode 0. ``` -#### Sends '1 + 1' to the basic calculator and captures its output: +#### Sends "1 + 1" to the basic calculator and captures its output: ```python >>> subprocess.run('bc', input='1 + 1\n', capture_output=True, text=True) CompletedProcess(args='bc', returncode=0, stdout='2\n', stderr='') @@ -1769,8 +1759,8 @@ JSON ```python import json - = json.dumps() # Converts collection to JSON string. - = json.loads() # Converts JSON string to collection. + = json.dumps() # Converts collection to JSON string. + = json.loads() # Converts JSON string to collection. ``` ### Read Collection from JSON File @@ -1794,8 +1784,8 @@ Pickle ```python import pickle - = pickle.dumps() # Converts object to bytes object. - = pickle.loads() # Converts bytes object to object. + = pickle.dumps() # Converts object to bytes object. + = pickle.loads() # Converts bytes object to object. ``` ### Read Object from Pickle File @@ -1821,33 +1811,34 @@ CSV import csv ``` -### Read ```python - = csv.reader() # Also: `dialect='excel', delimiter=','`. - = next() # Returns next row as a list of strings. - = list() # Returns a list of remaining rows. + = open(, newline='') # Opens the CSV (text) file for reading. + = csv.reader() # Also: `dialect='excel', delimiter=','`. + = next() # Returns next row as a list of strings. + = list() # Returns a list of all remaining rows. ``` -* **File must be opened with a `'newline=""'` argument, or newlines embedded inside quoted fields will not be interpreted correctly!** -* **To print the spreadsheet to the console use [Tabulate](#table) library.** -* **For XML and binary Excel files (xlsx, xlsm and xlsb) use [Pandas](#dataframe-plot-encode-decode) library.** -* **Reader accepts any iterator of strings, not just files.** +* **Without the `'newline=""'` argument, every '\r\n' sequence that is embedded inside a quoted field will get converted to '\n'! For details about newline argument see [Open](#open).** +* **To print the spreadsheet to the console use either [Tabulate](#table) or PrettyTable library.** +* **For XML and binary Excel files (extensions xlsx, xlsm and xlsb) use [Pandas](#fileformats) library.** +* **Reader accepts any iterator (or collection) of strings, not just text files.** ### Write ```python - = csv.writer() # Also: `dialect='excel', delimiter=','`. -.writerow() # Encodes objects using `str()`. -.writerows() # Appends multiple rows. + = open(, 'w', newline='') # Opens the CSV (text) file for writing. + = csv.writer() # Also: `dialect='excel', delimiter=','`. +.writerow() # Encodes each object using `str()`. +.writerows() # Appends multiple rows to opened file. ``` -* **File must be opened with a `'newline=""'` argument, or '\r' will be added in front of every '\n' on platforms that use '\r\n' line endings!** +* **If file is opened without the `'newline=""'` argument, '\r' will be added in front of every '\n' on platforms that use '\r\n' line endings (i.e., newlines may get doubled on Windows)!** * **Open existing file with `'mode="a"'` to append to it or `'mode="w"'` to overwrite it.** ### Parameters * **`'dialect'` - Master parameter that sets the default values. String or a 'csv.Dialect' object.** -* **`'delimiter'` - A one-character string used to separate fields.** -* **`'lineterminator'` - How writer terminates rows. Reader looks for '\n', '\r' and '\r\n'.** +* **`'delimiter'` - A one-character string that separates fields (comma, tab, semicolon, etc.).** +* **`'lineterminator'` - Sets how writer terminates rows. Reader looks for '\n', '\r' and '\r\n'.** * **`'quotechar'` - Character for quoting fields containing delimiters, quotechars, '\n' or '\r'.** -* **`'escapechar'` - Character for escaping quotechars.** -* **`'doublequote'` - Whether quotechars inside fields are/get doubled or escaped.** +* **`'escapechar'` - Character for escaping quotechars (not needed if doublequote is True).** +* **`'doublequote'` - Whether quotechars inside fields are/get doubled instead of escaped.** * **`'quoting'` - 0: As necessary, 1: All, 2: All but numbers which are read as floats, 3: None.** * **`'skipinitialspace'` - Is space character at the start of the field stripped by the reader.** @@ -1894,14 +1885,14 @@ import sqlite3 ### Read ```python - = .execute('') # Can raise a subclass of sqlite3.Error. - = .fetchone() # Returns next row. Also next(). + = .execute('') # Can raise a subclass of the sqlite3.Error. + = .fetchone() # Returns the next row. Also next(). = .fetchall() # Returns remaining rows. Also list(). ``` ### Write ```python -.execute('') # Can raise a subclass of sqlite3.Error. +.execute('') # Can raise a subclass of the sqlite3.Error. .commit() # Saves all changes since the last commit. .rollback() # Discards all changes since the last commit. ``` @@ -1914,20 +1905,20 @@ with : # Exits the block with commit() o ### Placeholders ```python -.execute('', ) # Replaces '?'s in query with values. -.execute('', ) # Replaces ':'s with values. -.executemany('', ) # Runs execute() multiple times. +.execute('', ) # Replaces every question mark with an item. +.execute('', ) # Replaces every : with a matching value. +.executemany('', ) # Executes the query once for each collection. ``` * **Passed values can be of type str, int, float, bytes, None, or bool (stored as 1 or 0).** +* **SQLite does not restrict columns to any type unless table is declared as strict.** ### Example **Values are not actually saved in this example because `'conn.commit()'` is omitted!** ```python >>> conn = sqlite3.connect('test.db') ->>> conn.execute('CREATE TABLE person (person_id INTEGER PRIMARY KEY, name, height)') ->>> conn.execute('INSERT INTO person VALUES (NULL, ?, ?)', ('Jean-Luc', 187)).lastrowid -1 ->>> conn.execute('SELECT * FROM person').fetchall() +>>> conn.execute('CREATE TABLE person (name TEXT, height INTEGER) STRICT') +>>> conn.execute('INSERT INTO person VALUES (?, ?)', ('Jean-Luc', 187)) +>>> conn.execute('SELECT rowid, * FROM person').fetchall() [(1, 'Jean-Luc', 187)] ``` @@ -1938,8 +1929,8 @@ with : # Exits the block with commit() o from sqlalchemy import create_engine, text = create_engine('') # Url: 'dialect://user:password@host/dbname'. = .connect() # Creates a connection. Also .close(). - = .execute(text(''), …) # ``. Replaces ':'s with values. -with .begin(): ... # Exits the block with commit or rollback. + = .execute(text(''), …) # ``. Replaces every : with value. +with .begin(): ... # Exits the block with a commit or rollback. ``` ```text @@ -1959,15 +1950,15 @@ Bytes **A bytes object is an immutable sequence of single bytes. Mutable version is called bytearray.** ```python - = b'' # Only accepts ASCII characters and \x00-\xff. - = [index] # Returns an int in range from 0 to 255. - = [] # Returns bytes even if it has only one element. + = b'' # Only accepts ASCII chars and [\x00-\xff]. + = [index] # Returns an integer in range from 0 to 255. + = [] # Returns bytes even if it has one element. = .join() # Joins elements using bytes as a separator. ``` ### Encode ```python - = bytes() # Ints must be in range from 0 to 255. + = bytes() # Integers must be in range from 0 to 255. = bytes(, 'utf-8') # Encodes the string. Also .encode(). = bytes.fromhex('') # Hex pairs can be separated by whitespaces. = .to_bytes(n_bytes, …) # `byteorder='big/little', signed=False`. @@ -1975,7 +1966,7 @@ Bytes ### Decode ```python - = list() # Returns ints in range from 0 to 255. + = list() # Returns integers in range from 0 to 255. = str(, 'utf-8') # Returns a string. Also .decode(). = .hex() # Returns hex pairs. Accepts `sep=`. = int.from_bytes(, …) # `byteorder='big/little', signed=False`. @@ -2004,8 +1995,8 @@ Struct ```python from struct import pack, unpack - = pack('', [, ...]) # Packs objects according to format string. - = unpack('', ) # Use iter_unpack() to get iterator of tuples. + = pack('', [, ...]) # Packs numbers according to format string. + = unpack('', ) # Use iter_unpack() to get iter of tuples. ``` ```python @@ -2039,22 +2030,22 @@ b'\x00\x01\x00\x02\x00\x00\x00\x03' Array ----- -**List that can only hold numbers of a predefined type. Available types and their minimum sizes in bytes are listed above. Type sizes and byte order are always determined by the system, however bytes of each element can be reversed with byteswap() method.** +**List that can only hold numbers of a predefined type. Available types and their minimum sizes in bytes are listed above. Type sizes and byte order are always determined by the sys­tem, however bytes of each element can be reversed (by calling the byteswap() method).** ```python from array import array ``` ```python - = array('', ) # Creates array from collection of numbers. - = array('', ) # Writes passed bytes to array's memory. - = array('', ) # Treats passed array as a sequence of numbers. -.fromfile(, n_items) # Appends file's contents to array's memory. + = array('' [, ]) # Creates array. Accepts collection of numbers. + = array('', ) # Copies passed bytes into the array's memory. + = array('', ) # Treats passed array as a sequence of numbers. +.fromfile(, n_items) # Appends file contents to the array's memory. ``` ```python - = bytes() # Returns a copy of array's memory. -.write() # Writes array's memory to the binary file. + = bytes() # Returns a copy of array's memory as bytes. +.write() # Appends array's memory to the binary file. ``` @@ -2063,24 +2054,24 @@ Memory View **A sequence object that points to the memory of another bytes-like object. Each element can reference a single or multiple consecutive bytes, depending on format. Order and number of elements can be changed with slicing.** ```python - = memoryview() # Immutable if bytes is passed, else mutable. - = [index] # Returns int or float. Bytes if format is 'c'. - = [] # Returns memoryview with rearranged elements. - = .cast('') # Only works between B/b/c and other types. -.release() # Releases memory buffer of the base object. + = memoryview() # Returns mutable memoryview if array is passed. + = [index] # Returns an int/float. Bytes if format is 'c'. + = [] # Returns memoryview with rearranged elements. + = .cast('') # Only works between B/b/c and the other types. +.release() # Releases the memory buffer of the base object. ``` ```python - = bytes() # Returns a new bytes object. Also bytearray(). - = .join() # Joins memoryviews using bytes as a separator. - = array('', ) # Treats memoryview as a sequence of numbers. -.write() # Writes `bytes()` to the binary file. + = bytes() # Returns a new bytes object. Also bytearray(). + = .join() # Joins memoryviews using bytes as a separator. + = array('', ) # Treats passed mview as a sequence of numbers. +.write() # Appends `bytes()` to the binary file. ``` ```python - = list() # Returns a list of ints, floats, or bytes. - = str(, 'utf-8') # Treats memoryview as a bytes object. - = .hex() # Returns hex pairs. Accepts `sep=`. + = list() # Returns list of ints, floats or bytes objects. + = str(, 'utf-8') # Treats passed memoryview as `bytes()`. + = .hex() # Returns hexadecimal pairs. Also `sep=`. ``` @@ -2093,17 +2084,17 @@ from collections import deque ``` ```python - = deque() # Use `maxlen=` to set size limit. -.appendleft() # Opposite element is dropped if full. -.extendleft() # Passed collection gets reversed. -.rotate(n=1) # Last element becomes first. - = .popleft() # Raises IndexError if deque is empty. + = deque() # Use `maxlen=` to set size limit. +.appendleft() # Opposite element is dropped if full. +.extendleft() # Prepends reversed coll. to the deque. +.rotate(n=1) # Last element becomes the first one. + = .popleft() # Raises IndexError if deque is empty. ``` Operator -------- -**Module of functions that provide the functionality of operators. Functions are ordered and grouped by operator precedence, from least to most binding. Logical and arithmetic operators in lines 1, 3 and 5 are also ordered by precedence within their own group.** +**Module of functions that provide the functionality of operators. Functions are grouped by operator precedence, from least to most binding. Functions and operators in first, third and fifth line are also ordered by precedence within a group.** ```python import operator as op ``` @@ -2111,11 +2102,11 @@ import operator as op ```python = op.not_() # or, and, not (or/and missing) = op.eq/ne/lt/ge/is_/is_not/contains(, ) # ==, !=, <, >=, is, is not, in - = op.or_/xor/and_(, ) # |, ^, & - = op.lshift/rshift(, ) # <<, >> - = op.add/sub/mul/truediv/floordiv/mod(, ) # +, -, *, /, //, % - = op.neg/invert() # -, ~ - = op.pow(, ) # ** + = op.or_/xor/and_(, ) # |, ^, & (sorted by precedence) + = op.lshift/rshift(, ) # <<, >> (i.e. << n_bits) + = op.add/sub/mul/truediv/floordiv/mod(, ) # +, -, *, /, //, % (two groups) + = op.neg/invert() # -, ~ (negate and bitwise not) + = op.pow(, ) # ** (pow() accepts 3 arguments) = op.itemgetter/attrgetter/methodcaller( [, ...]) # [index/key], .name, .name([…]) ``` @@ -2126,12 +2117,12 @@ sorted_by_both = sorted(, key=op.itemgetter(1, 0)) first_element = op.methodcaller('pop', 0)() ``` * **Most operators call the object's special method that is named after them (second object is passed as an argument), while logical operators call their own code that relies on bool().** -* **Comparisons can be chained: `'x < y < z'` gets converted to `'(x < y) and (y < z)`'.** +* **Comparisons can be chained: `'x < y < z'` gets converted to `'(x < y) and (y < z)'`.** Match Statement --------------- -**Executes the first block with matching pattern. Added in Python 3.10.** +**Executes the first block with matching pattern.** ```python match : @@ -2142,22 +2133,21 @@ match : ### Patterns ```python - = 1/'abc'/True/None/math.pi # Matches the literal or a dotted name. + = 1/'abc'/True/None/math.pi # Matches the literal or attribute's value. = () # Matches any object of that type (or ABC). = _ # Matches any object. Useful in last case. = # Matches any object and binds it to name. = as # Binds match to name. Also (). - = | [| ...] # Matches any of the patterns. - = [, ...] # Matches sequence with matching items. - = {: , ...} # Matches dictionary with matching items. - = (=, ...) # Matches object with matching attributes. + = | [| ...] # Matches if any of listed patterns match. + = [, ...] # Matches a sequence. All items must match. + = {: , ...} # Matches a dict if it has matching items. + = (=, ...) # Matches object that has matching attrbs. ``` -* **Sequence pattern can also be written as a tuple.** +* **Sequence pattern can also be written as a tuple, either with or without the brackets.** * **Use `'*'` and `'**'` in sequence/mapping patterns to bind remaining items.** * **Sequence pattern must match all items of the collection, while mapping pattern does not.** -* **Patterns can be surrounded with brackets to override precedence (`'|'` > `'as'` > `','`).** -* **Built-in types allow a single positional pattern that is matched against the entire object.** -* **All names that are bound in the matching case, as well as variables initialized in its block, are visible after the match statement.** +* **Patterns can be surrounded with brackets to override their precedence: `'|'` > `'as'` > `','`. For example, `'[1, 2]'` is matched by the `'case 1|2, 2|3 as x if x == 2:'` block.** +* **All names that are bound in the matching case, as well as variables initialized in its body, are visible after the match statement (only function block delimits scope).** ### Example ```python @@ -2179,29 +2169,29 @@ import logging as log ```python log.basicConfig(filename=, level='DEBUG') # Configures the root logger (see Setup). -log.debug/info/warning/error/critical() # Sends message to the root logger. - = log.getLogger(__name__) # Returns logger named after the module. -.() # Sends message to the logger. +log.debug/info/warning/error/critical() # Sends passed message to the root logger. + = log.getLogger(__name__) # Returns a logger named after the module. +.() # Sends the message. Same levels as above. .exception() # Error() that appends caught exception. ``` ### Setup ```python log.basicConfig( - filename=None, # Logs to stderr or appends to file. + filename=None, # Prints to stderr or appends to file. format='%(levelname)s:%(name)s:%(message)s', # Add '%(asctime)s' for local datetime. - level=log.WARNING, # Drops messages with lower priority. + level=log.WARNING, # Drops messages with a lower priority. handlers=[log.StreamHandler(sys.stderr)] # Uses FileHandler if filename is set. ) ``` ```python - = log.Formatter('') # Creates a Formatter. - = log.FileHandler(, mode='a') # Creates a Handler. Also `encoding=None`. -.setFormatter() # Adds Formatter to the Handler. -.setLevel() # Processes all messages by default. -.addHandler() # Adds Handler to the Logger. -.setLevel() # What is sent to its/ancestors' handlers. + = log.Formatter('') # Formats messages according to format. + = log.FileHandler(, mode='a') # Appends to file. Also `encoding=None`. +.setFormatter() # Only outputs bare messages by default. +.setLevel() # Prints/saves every message by default. +.addHandler() # Logger can have more than one handler. +.setLevel() # What's sent to its/ancestors' handlers. .propagate = # Cuts off ancestors' handlers if False. ``` * **Parent logger can be specified by naming the child logger `'.'`.** @@ -2230,21 +2220,21 @@ Introspection ------------- ```python = dir() # Local names of variables, functions, classes and modules. - = vars() # Dict of local names and their objects. Also locals(). + = vars() # Dict of local names and their objects. Same as locals(). = globals() # Dict of global names and their objects, e.g. __builtin__. ``` ```python = dir() # Returns names of object's attributes (including methods). = vars() # Returns dict of writable attributes. Also .__dict__. - = hasattr(, '') # Checks if object possesses attribute with passed name. -value = getattr(, '') # Returns object's attribute or raises AttributeError. + = hasattr(, '') # Checks if object possesses attribute of the passed name. +value = getattr(, '') # Returns the object's attribute or raises AttributeError. setattr(, '', value) # Sets attribute. Only works on objects with __dict__ attr. delattr(, '') # Deletes attribute from __dict__. Also `del .`. ``` ```python - = inspect.signature() # Returns a Signature object of the passed function. + = inspect.signature() # Returns Signature object of the passed function or class. = .parameters # Returns dict of Parameters. Also .return_annotation. = .kind # Returns ParameterKind member (Parameter.KEYWORD_ONLY, …). = .annotation # Returns Parameter.empty if missing. Also .default. @@ -2263,63 +2253,63 @@ from concurrent.futures import ThreadPoolExecutor, as_completed ```python = Thread(target=) # Use `args=` to set the arguments. .start() # Starts the thread. Also .is_alive(). -.join() # Waits for the thread to finish executing. +.join() # Waits until the thread has finished executing. ``` * **Use `'kwargs='` to pass keyword arguments to the function.** -* **Use `'daemon=True'`, or the program will not be able to exit while the thread is alive.** +* **Use `'daemon=True'`, or the program won't be able to exit while the thread is alive.** ### Lock ```python - = Lock/RLock() # RLock can only be released by acquirer. -.acquire() # Waits for the lock to be available. -.release() # Makes the lock available again. + = Lock/RLock() # RLock can only be released by acquirer thread. +.acquire() # Blocks (waits) until lock becomes available. +.release() # Releases the lock so it can be acquired again. ``` #### Or: ```python -with : # Enters the block by calling acquire() and - ... # exits it with release(), even on error. +with : # Enters the block by calling method acquire(). + ... # Exits it by calling release(), even on error. ``` ### Semaphore, Event, Barrier ```python = Semaphore(value=1) # Lock that can be acquired by 'value' threads. = Event() # Method wait() blocks until set() is called. - = Barrier(n_times) # Wait() blocks until it's called n times. + = Barrier() # Wait() blocks until it's called integer times. ``` ### Queue ```python - = queue.Queue(maxsize=0) # A thread-safe first-in-first-out queue. -.put() # Blocks until queue stops being full. -.put_nowait() # Raises queue.Full exception if full. - = .get() # Blocks until queue stops being empty. - = .get_nowait() # Raises queue.Empty exception if empty. + = queue.Queue(maxsize=0) # A first-in-first-out queue. It's thread safe. +.put() # The call blocks until queue stops being full. +.put_nowait() # Raises queue.Full exception if queue is full. + = .get() # The call blocks until queue stops being empty. + = .get_nowait() # Raises queue.Empty exception if it is empty. ``` ### Thread Pool Executor ```python - = ThreadPoolExecutor(max_workers=None) # Or: `with ThreadPoolExecutor() as : ...` + = ThreadPoolExecutor(max_workers=None) # Also `with ThreadPoolExecutor() as : …`. = .map(, , ...) # Multithreaded and non-lazy map(). Keeps order. = .submit(, , ...) # Creates a thread and returns its Future obj. -.shutdown() # Blocks until all threads finish executing. +.shutdown() # Waits for all the threads to finish executing. ``` ```python = .done() # Checks if the thread has finished executing. - = .result(timeout=None) # Waits for thread to finish and returns result. + = .result(timeout=None) # Raises TimeoutError after 'timeout' seconds. = .cancel() # Cancels or returns False if running/finished. = as_completed() # `next()` returns next completed Future. ``` * **Map() and as\_completed() also accept 'timeout'. It causes futures.TimeoutError when next() is called/blocking. Map() times from original call and as_completed() from first call to next(). As\_completed() fails if next() is called too late, even if all threads are done.** -* **Exceptions that happen inside threads are raised when map iterator's next() or Future's result() are called. Future's exception() method returns exception object or None.** +* **Exceptions that happen inside threads are raised when map iterator's next() or Future's result() are called. Future's exception() method returns an exception object or None.** * **ProcessPoolExecutor provides true parallelism but: everything sent to/from workers must be [pickable](#pickle), queues must be sent using executor's 'initargs' and 'initializer' parameters, and executor should only be reachable via `'if __name__ == "__main__": ...'`.** Coroutines ---------- -* **Coroutines have a lot in common with threads, but unlike threads, they only give up control when they call another coroutine and they don’t use as much memory.** -* **Coroutine definition starts with `'async'` and its call with `'await'`.** +* **Coroutines have a lot in common with threads, but unlike threads, they only give up control when they call another coroutine and they don’t consume as much memory.** +* **Coroutine definition starts with `'async'` keyword and its call with `'await'` keyword.** * **Use `'asyncio.run()'` to start the first/main coroutine.** ```python @@ -2328,27 +2318,27 @@ import asyncio as aio ```python = () # Creates a coroutine by calling async def function. - = await # Starts the coroutine and returns its result. - = aio.create_task() # Schedules the coroutine for execution. + = await # Starts the coroutine. Returns its result or None. + = aio.create_task() # Schedules it for execution. Always keep the task. = await # Returns coroutine's result. Also .cancel(). ``` ```python = aio.gather(, ...) # Schedules coros. Returns list of results on await. = aio.wait(, return_when=…) # `'ALL/FIRST_COMPLETED'`. Returns (done, pending). - = aio.as_completed() # Iter of coros that return next result on await. + = aio.as_completed() # Iter of coros. Each returns next result on await. ``` #### Runs a terminal game where you control an asterisk that must avoid numbers: ```python import asyncio, collections, curses, curses.textpad, enum, random -P = collections.namedtuple('P', 'x y') # Position -D = enum.Enum('D', 'n e s w') # Direction -W, H = 15, 7 # Width, Height +P = collections.namedtuple('P', 'x y') # Position (x and y coordinates). +D = enum.Enum('D', 'n e s w') # Direction (north, east, etc.). +W, H = 15, 7 # Width and height of the field. def main(screen): - curses.curs_set(0) # Makes cursor invisible. + curses.curs_set(0) # Makes the cursor invisible. screen.nodelay(True) # Makes getch() non-blocking. asyncio.run(main_coroutine(screen)) # Starts running asyncio code. @@ -2356,7 +2346,7 @@ async def main_coroutine(screen): moves = asyncio.Queue() state = {'*': P(0, 0)} | {id_: P(W//2, H//2) for id_ in range(10)} ai = [random_controller(id_, moves) for id_ in range(10)] - mvc = [human_controller(screen, moves), model(moves, state), view(state, screen)] + mvc = [controller(screen, moves), model(moves, state), view(state, screen)] tasks = [asyncio.create_task(coro) for coro in ai + mvc] await asyncio.wait(tasks, return_when=asyncio.FIRST_COMPLETED) @@ -2366,7 +2356,7 @@ async def random_controller(id_, moves): moves.put_nowait((id_, d)) await asyncio.sleep(random.triangular(0.01, 0.65)) -async def human_controller(screen, moves): +async def controller(screen, moves): while True: key_mappings = {258: D.s, 259: D.n, 260: D.w, 261: D.e} if d := key_mappings.get(screen.getch()): @@ -2417,10 +2407,10 @@ Plot import matplotlib.pyplot as plt plt.plot/bar/scatter(x_data, y_data [, label=]) # Also plt.plot(y_data). -plt.legend() # Adds a legend. -plt.title/xlabel/ylabel() # Adds a title or label. +plt.legend() # Adds a legend of labels. +plt.title/xlabel/ylabel() # Adds title or axis label. plt.show() # Also plt.savefig(). -plt.clf() # Clears the plot. +plt.clf() # Clears the plot (figure). ``` @@ -2442,7 +2432,7 @@ Console App ```python # $ pip3 install windows-curses import curses, os -from curses import A_REVERSE, KEY_UP, KEY_DOWN, KEY_LEFT, KEY_RIGHT, KEY_ENTER +from curses import A_REVERSE, KEY_UP, KEY_DOWN, KEY_LEFT, KEY_RIGHT def main(screen): ch, first, selected, paths = 0, 0, 0, os.listdir() @@ -2455,9 +2445,9 @@ def main(screen): ch = screen.getch() selected -= (ch == KEY_UP) and (selected > 0) selected += (ch == KEY_DOWN) and (selected < len(paths)-1) - first = min(first, selected) - first = max(first, selected - (height-1)) - if ch in [KEY_LEFT, KEY_RIGHT, KEY_ENTER, ord('\n'), ord('\r')]: + first -= (first > selected) + first += (first < selected-(height-1)) + if ch in [KEY_LEFT, KEY_RIGHT, ord('\n')]: new_dir = '..' if ch == KEY_LEFT else paths[selected] if os.path.isdir(new_dir): os.chdir(new_dir) @@ -2470,30 +2460,28 @@ if __name__ == '__main__': GUI App ------- -#### A weight converter GUI application: +#### Runs a desktop app for converting weights from metric units into pounds: ```python # $ pip3 install PySimpleGUI import PySimpleGUI as sg -text_box = sg.Input(default_text='100', enable_events=True, key='-QUANTITY-') -dropdown = sg.InputCombo(['g', 'kg', 't'], 'kg', readonly=True, enable_events=True, k='-UNIT-') -label = sg.Text('100 kg is 220.462 lbs.', key='-OUTPUT-') -button = sg.Button('Close') -window = sg.Window('Weight Converter', [[text_box, dropdown], [label], [button]]) +text_box = sg.Input(default_text='100', enable_events=True, key='QUANTITY') +dropdown = sg.InputCombo(['g', 'kg', 't'], 'kg', readonly=True, enable_events=True, k='UNIT') +label = sg.Text('100 kg is 220.462 lbs.', key='OUTPUT') +window = sg.Window('Weight Converter', [[text_box, dropdown], [label], [sg.Button('Close')]]) while True: event, values = window.read() if event in [sg.WIN_CLOSED, 'Close']: break try: - quantity = float(values['-QUANTITY-']) + quantity = float(values['QUANTITY']) except ValueError: continue - unit = values['-UNIT-'] - factors = {'g': 0.001, 'kg': 1, 't': 1000} - lbs = quantity * factors[unit] / 0.45359237 - window['-OUTPUT-'].update(value=f'{quantity} {unit} is {lbs:g} lbs.') + unit = values['UNIT'] + lbs = quantity * {'g': 0.001, 'kg': 1, 't': 1000}[unit] / 0.45359237 + window['OUTPUT'].update(value=f'{quantity} {unit} is {lbs:g} lbs.') window.close() ``` @@ -2505,15 +2493,16 @@ Scraping # $ pip3 install requests beautifulsoup4 import requests, bs4, os -response = requests.get('https://en.wikipedia.org/wiki/Python_(programming_language)') -document = bs4.BeautifulSoup(response.text, 'html.parser') -table = document.find('table', class_='infobox vevent') +get = lambda url: requests.get(url, headers={'User-Agent': 'cpc-bot'}) +response = get('https://en.wikipedia.org/wiki/Python_(programming_language)') +document = bs4.BeautifulSoup(response.text, 'html.parser') +table = document.find('table', class_='infobox vevent') python_url = table.find('th', text='Website').next_sibling.a['href'] -logo_url = table.find('img')['src'] -filename = os.path.basename(logo_url) +logo_url = table.find('img')['src'] +filename = os.path.basename(logo_url) with open(filename, 'wb') as file: - file.write(requests.get(f'https:{logo_url}').content) -print(f'{python_url}, file://{os.path.abspath(filename)}') + file.write(get(f'https:{logo_url}').content) +print(f'URL: {python_url}, logo: file://{os.path.abspath(filename)}') ``` ### Selenium @@ -2521,25 +2510,27 @@ print(f'{python_url}, file://{os.path.abspath(filename)}') ```python # $ pip3 install selenium from selenium import webdriver +``` - = webdriver.Chrome/Firefox/Safari/Edge() # Opens a browser. Also .quit(). -.get('') # Also .implicitly_wait(seconds). - = .page_source # Returns HTML of fully rendered page. - = .find_element('css selector', …) # '#.[=""]…'. - = .find_elements('xpath', …) # '//[@=""]…'. See XPath. - = .get_attribute() # Property if exists. Also .text. -.click/clear() # Also .send_keys(). +```python + = webdriver.Chrome/Firefox/Safari/Edge() # Opens the browser. Also .quit(). +.implicitly_wait(seconds) # Sets timeout for find_element/s() methods. +.get('') # Blocks until browser fires the load event. + = .page_source # Returns HTML of the page's current state. + = .find_element('xpath', ) # Accepts '//[@=""]…'. + = .get_attribute('') # Returns attribute or property if exists. +.click/clear() # Also .text and .send_keys(). ``` #### XPath — also available in lxml, Scrapy, and browser's console via `'$x("")'`: ```python - = //[/ or // ] # /, //, /../ - = ///following:: # Next element. Also preceding/parent/… - = # ` = */a/…`, ` = [1/2/…]`. - = [ [and/or ]] # For negation use `not()`. - = @[=""] # `text()=`, `.=` match (complete) text. - = contains(@, "") # Is a substring of attr's value? - = [//] # Has matching child? Descendant if //. + = //[/ or // ] # E.g. …/child, …//descendant, …/../sibling. + = ///following:: # Next element. Also preceding::, parent::. + = # Tag accepts */a/…. Use [1/2/…] for index. + = [ [and/or ]] # Use not() to negate condition. + = @[=""] # `text()=` and `.=` match (complete) text. + = contains(@, "") # Is a substring of attribute's value? + = [//] # Has matching child? Descendant if //. ``` @@ -2559,14 +2550,14 @@ app.run(host=None, port=None, debug=None) # Or: $ flask --app FILE run [--ARG[= * **Install a WSGI server like [Waitress](https://flask.palletsprojects.com/en/latest/deploying/waitress/) and a HTTP server such as [Nginx](https://flask.palletsprojects.com/en/latest/deploying/nginx/) for better security.** * **Debug mode restarts the app whenever script changes and displays errors in the browser.** -### Static Request +### Serving Files ```python @app.route('/img/') def serve_file(filename): return fl.send_from_directory('DIRNAME', filename) ``` -### Dynamic Request +### Serving HTML ```python @app.route('/') def serve_html(sport): @@ -2577,7 +2568,7 @@ def serve_html(sport): * **`'fl.request.args[]'` returns parameter from query string (URL part right of '?').** * **`'fl.session[] = '` stores session data. It requires secret key to be set at the startup with `'app.secret_key = '`.** -### REST Request +### Serving JSON ```python @app.post('//odds') def serve_json(sport): @@ -2673,7 +2664,7 @@ import numpy as np ```python = .reshape() # Also `.shape = `. = .flatten() # Also ` = .ravel()`. - = .transpose() # Or: .T + = .transpose() # Flips the table over its diagonal. ``` ```python @@ -2710,8 +2701,7 @@ import numpy as np <1/2d_arr> = <2d>[<2d/1d_bools>] # 1d_bools must have size of a column. ``` * **`':'` returns a slice of all dimension's indices. Omitted dimensions default to `':'`.** -* **Python converts `'obj[i, j]'` to `'obj[(i, j)]'`. This makes `'<2d>[row_i, col_i]'` and `'<2d>[row_indices]'` indistinguishable to NumPy if tuple of indices is passed!** -* **Indexing with a slice and 1d object works the same as when using two slices (lines 4, 6, 7).** +* **Python converts `'obj[i, j]'` to `'obj[(i, j)]'`. This makes `'<2d>[row_i, col_i]'` and `'<2d>[row_indices]'` indistinguishable to NumPy if tuple of two indices is passed!** * **`'ix_([1, 2], [3, 4])'` returns `'[[1], [2]]'` and `'[[3, 4]]'`. Due to broadcasting rules, this is the same as using `'[[1, 1], [2, 2]]'` and `'[[3, 4], [3, 4]]'`.** * **Any value that is broadcastable to the indexed shape can be assigned to the selection.** @@ -2774,7 +2764,7 @@ from PIL import Image = Image.new('', (width, height)) # Creates new image. Also `color=`. = Image.open() # Identifies format based on file's contents. = .convert('') # Converts image to the new mode (see Modes). -.save() # Selects format based on extension (PNG/JPG…). +.save() # Accepts `quality=` if extension is jpg. .show() # Displays image in default preview app. ``` @@ -2797,9 +2787,9 @@ from PIL import Image ``` ### Modes -* **`'L'` - Lightness (greyscale image). Each pixel is an int between 0 and 255.** -* **`'RGB'` - Red, green, blue (true color image). Each pixel is a tuple of three ints.** -* **`'RGBA'` - RGB with alpha. Low alpha (i.e. forth int) makes pixel more transparent.** +* **`'L'` - Lightness (greyscale image). Each pixel is an integer between 0 and 255.** +* **`'RGB'` - Red, green, blue (true color image). Each pixel is a tuple of three integers.** +* **`'RGBA'` - RGB with alpha. Low alpha (i.e. fourth int) makes pixel more transparent.** * **`'HSV'` - Hue, saturation, value. Three ints representing color in HSV color space.** @@ -2827,12 +2817,12 @@ img.show() ```python from PIL import ImageDraw = ImageDraw.Draw() # Object for adding 2D graphics to the image. -.point((x, y)) # Draws a point. Truncates floats into ints. -.line((x1, y1, x2, y2 [, ...])) # To get anti-aliasing use Image's resize(). +.point((x, y)) # Draws a point. Also `fill=`. +.line((x1, y1, x2, y2 [, ...])) # For anti-aliasing use .resize((w, h)). .arc((x1, y1, x2, y2), deg1, deg2) # Draws in clockwise dir. Also pieslice(). .rectangle((x1, y1, x2, y2)) # Also rounded_rectangle(), regular_polygon(). -.polygon((x1, y1, x2, y2, ...)) # Last point gets connected to the first. -.ellipse((x1, y1, x2, y2)) # To rotate use Image's rotate() and paste(). +.polygon((x1, y1, x2, y2, ...)) # Last point gets connected to the first one. +.ellipse((x1, y1, x2, y2)) # To rotate use .rotate(anticlock_deg). .text((x, y), , font=) # ` = ImageFont.truetype(, size)`. ``` * **Use `'fill='` to set the primary color.** @@ -2942,7 +2932,7 @@ write_to_wav_file('test.wav', samples_f) from random import uniform samples_f, params = read_wav_file('test.wav') samples_f = (f + uniform(-0.05, 0.05) for f in samples_f) -write_to_wav_file('test.wav', samples_f, params) +write_to_wav_file('test.wav', samples_f, p=params) ``` #### Plays the WAV file: @@ -2950,8 +2940,7 @@ write_to_wav_file('test.wav', samples_f, params) # $ pip3 install simpleaudio from simpleaudio import play_buffer with wave.open('test.wav') as file: - p = file.getparams() - frames = file.readframes(-1) + frames, p = file.readframes(-1), file.getparams() play_buffer(frames, p.nchannels, p.sampwidth, p.framerate).wait_done() ``` @@ -2970,26 +2959,27 @@ Synthesizer #### Plays Popcorn by Gershon Kingsley: ```python # $ pip3 install simpleaudio -import array, itertools as it, math, simpleaudio +import itertools as it, math, array, simpleaudio + +def play_notes(notes, bpm=132, f=44100): + get_pause = lambda n_beats: it.repeat(0, int(n_beats * 60/bpm * f)) + sin_f = lambda i, hz: math.sin(i * 2 * math.pi * hz / f) + get_wave = lambda hz, n_beats: (sin_f(i, hz) for i in range(int(n_beats * 60/bpm * f))) + get_hz = lambda note: 440 * 2 ** ((int(note[:2]) - 69) / 12) + get_nbeats = lambda note: 1/2 if '♩' in note else 1/4 if '♪' in note else 1 + get_samples = lambda n: get_wave(get_hz(n), get_nbeats(n)) if n else get_pause(1/4) + samples_f = it.chain.from_iterable(get_samples(n) for n in notes.split(',')) + samples_i = array.array('h', (int(fl * 5000) for fl in samples_f)) + simpleaudio.play_buffer(samples_i, 1, 2, f).wait_done() -F = 44100 -P1 = '71♩,69♪,,71♩,66♪,,62♩,66♪,,59♩,,,71♩,69♪,,71♩,66♪,,62♩,66♪,,59♩,,,' -P2 = '71♩,73♪,,74♩,73♪,,74♪,,71♪,,73♩,71♪,,73♪,,69♪,,71♩,69♪,,71♪,,67♪,,71♩,,,' -get_pause = lambda seconds: it.repeat(0, int(seconds * F)) -sin_f = lambda i, hz: math.sin(i * 2 * math.pi * hz / F) -get_wave = lambda hz, seconds: (sin_f(i, hz) for i in range(int(seconds * F))) -get_hz = lambda note: 440 * 2 ** ((int(note[:2]) - 69) / 12) -get_sec = lambda note: 1/4 if '♩' in note else 1/8 -get_samples = lambda note: get_wave(get_hz(note), get_sec(note)) if note else get_pause(1/8) -samples_f = it.chain.from_iterable(get_samples(n) for n in (P1+P2).split(',')) -samples_i = array.array('h', (int(f * 30000) for f in samples_f)) -simpleaudio.play_buffer(samples_i, 1, 2, F).wait_done() +play_notes('83♩,81♪,,83♪,,78♪,,74♪,,78♪,,71♪,,,,83♪,,81♪,,83♪,,78♪,,74♪,,78♪,,71♪,,,,' + '83♩,85♪,,86♪,,85♪,,86♪,,83♪,,85♩,83♪,,85♪,,81♪,,83♪,,81♪,,83♪,,79♪,,83♪,,,,') ``` Pygame ------ -#### Opes a window and draws a square that can be moved with arrow keys: +#### Opens a window and draws a square that can be moved with arrow keys: ```python # $ pip3 install pygame import pygame as pg @@ -2998,10 +2988,10 @@ pg.init() screen = pg.display.set_mode((500, 500)) rect = pg.Rect(240, 240, 20, 20) while not pg.event.get(pg.QUIT): - deltas = {pg.K_UP: (0, -20), pg.K_RIGHT: (20, 0), pg.K_DOWN: (0, 20), pg.K_LEFT: (-20, 0)} for event in pg.event.get(pg.KEYDOWN): - dx, dy = deltas.get(event.key, (0, 0)) - rect = rect.move((dx, dy)) + dx = (event.key == pg.K_RIGHT) - (event.key == pg.K_LEFT) + dy = (event.key == pg.K_DOWN) - (event.key == pg.K_UP) + rect = rect.move((dx * 20, dy * 20)) screen.fill(pg.Color('black')) pg.draw.rect(screen, pg.Color('white'), rect) pg.display.flip() @@ -3012,14 +3002,14 @@ pg.quit() **Object for storing rectangular coordinates.** ```python = pg.Rect(x, y, width, height) # Creates Rect object. Truncates passed floats. - = .x/y/centerx/centery/… # Top, right, bottom, left. Allows assignments. - = .topleft/center/… # Topright, bottomright, bottomleft. Same. - = .move((delta_x, delta_y)) # Use move_ip() to move in-place. + = .x/y/centerx/centery/… # `top/right/bottom/left`. Allows assignments. + = .topleft/center/… # `topright/bottomright/bottomleft/size`. Same. + = .move((delta_x, delta_y)) # Use move_ip() to move the rectangle in-place. ``` ```python - = .collidepoint((x, y)) # Checks if rectangle contains the point. - = .colliderect() # Checks if the two rectangles overlap. + = .collidepoint((x, y)) # Checks whether rectangle contains the point. + = .colliderect() # Checks whether the two rectangles overlap. = .collidelist() # Returns index of first colliding Rect or -1. = .collidelistall() # Returns indices of all colliding rectangles. ``` @@ -3027,7 +3017,7 @@ pg.quit() ### Surface **Object for representing images.** ```python - = pg.display.set_mode((width, height)) # Opens new window and returns its surface. + = pg.display.set_mode((width, height)) # Opens a new window and returns its surface. = pg.Surface((width, height)) # New RGB surface. RGBA if `flags=pg.SRCALPHA`. = pg.image.load() # Loads the image. Format depends on source. = pg.surfarray.make_surface() # Also ` = surfarray.pixels3d()`. @@ -3035,28 +3025,28 @@ pg.quit() ``` ```python -.fill(color) # Tuple, Color('#rrggbb[aa]') or Color(). +.fill(color) # Pass tuple of ints or pg.Color(''). .set_at((x, y), color) # Updates pixel. Also .get_at((x, y)). .blit(, (x, y)) # Draws passed surface at specified location. ``` ```python -from pygame.transform import scale, ... - = scale(, (width, height)) # Returns scaled surface. - = rotate(, anticlock_degrees) # Returns rotated and scaled surface. - = flip(, x_bool, y_bool) # Returns flipped surface. +from pygame.transform import scale, rotate # Also: flip, smoothscale, scale_by. + = scale(, (width, height)) # Scales the surface. `smoothscale()` blurs it. + = rotate(, angle) # Rotates the surface for counterclock degrees. + = flip(, flip_x=False) # Mirrors the surface. Also `flip_y=False`. ``` ```python -from pygame.draw import line, ... -line(, color, (x1, y1), (x2, y2), width) # Draws a line to the surface. +from pygame.draw import line, arc, rect # Also: ellipse, polygon, circle, aaline. +line(, color, (x1, y1), (x2, y2)) # Draws a line to the surface. Also `width=1`. arc(, color, , from_rad, to_rad) # Also ellipse(, color, , width=0). rect(, color, , width=0) # Also polygon(, color, points, width=0). ``` ```python = pg.font.Font(, size) # Loads TTF file. Pass None for default font. - = .render(text, antialias, color) # Background color can be specified at the end. + = .render(text, antialias, color) # Accepts background color as fourth argument. ``` ### Sound @@ -3131,7 +3121,7 @@ def draw(screen, images, mario, tiles): screen.fill((85, 168, 255)) mario.facing_left = mario.spd.x < 0 if mario.spd.x else mario.facing_left is_airborne = D.s not in get_boundaries(mario.rect, tiles) - image_index = 4 if is_airborne else (next(mario.frame_cycle) if mario.spd.x else 6) + image_index = 4 if is_airborne else next(mario.frame_cycle) if mario.spd.x else 6 screen.blit(images[image_index + (mario.facing_left * 9)], mario.rect) for t in tiles: is_border = t.x in [0, (W-1)*16] or t.y in [0, (H-1)*16] @@ -3192,16 +3182,17 @@ Name: a, dtype: int64 ``` ```python -.plot.line/area/bar/pie/hist() # Generates a plot. `plt.show()` displays it. +.plot.line/area/bar/pie/hist() # Generates a plot. Accepts `title=`. +plt.show() # Displays the plot. Also plt.savefig(). ``` -* **Also `'.quantile()'` and `'pd.cut(, bins=)'`.** -* **Indexing objects can't be tuples because `'obj[x, y]'` is converted to `'obj[(x, y)]'`.** -* **Pandas uses NumPy types like `'np.int64'`. Series is converted to `'float64'` if we assign np.nan to any item. Use `'.astype()'` to get converted Series.** -* **Series will silently overflow if we run `'pd.Series([100], dtype="int8") + 100'`!** +* **Use `'print(.to_string())'` to print a Series that has more than sixty items.** +* **Use `'.index'` to get collection of keys and `'.index = '` to update them.** +* **Only pass a list or Series to loc/iloc because `'obj[x, y]'` is converted to `'obj[(x, y)]'` and `'.loc[key_1, key_2]'` is how you retrieve a value from a multi-indexed Series.** +* **Pandas uses NumPy types like `'np.int64'`. Series is converted to `'float64'` if np.nan is assigned to any item. Use `'.astype()'` to get converted Series.** #### Series — Aggregate, Transform, Map: ```python - = .sum/max/mean/idxmax/all/count() # Or: .agg(lambda : ) + = .sum/max/mean/std/idxmax/count() # Or: .agg(lambda : ) = .rank/diff/cumsum/ffill/interpol…() # Or: .agg/transform(lambda : ) = .isna/fillna/isin([]) # Or: .agg/transform/map(lambda : ) ``` @@ -3224,7 +3215,6 @@ Name: a, dtype: int64 | | y 2.0 | y 2.0 | y 2.0 | +--------------+-------------+-------------+---------------+ ``` -* **Last result has a multi-index. Use `'[key_1, key_2]'` to get its values.** ### DataFrame **Table with labeled rows and columns.** @@ -3314,7 +3304,7 @@ c 6 7 #### DataFrame — Aggregate, Transform, Map: ```python - = .sum/max/mean/idxmax/all/count() # Or: .apply/agg(lambda : ) + = .sum/max/mean/std/idxmax/count() # Or: .apply/agg(lambda : ) = .rank/diff/cumsum/ffill/interpo…() # Or: .apply/agg/transform(lambda : ) = .isna/fillna/isin([]) # Or: .applymap(lambda : ) ``` @@ -3355,18 +3345,19 @@ c 6 7 = pd.read_json/pickle() # Also io.StringIO(), io.BytesIO(). = pd.read_csv/excel() # Also `header/index_col/dtype/usecols/…=`. = pd.read_html() # Raises ImportError if webpage has zero tables. - = pd.read_parquet/feather/hdf() # Read_hdf() accepts `key=` argument. - = pd.read_sql('', ) # Pass SQLite3/Alchemy connection (see #SQLite). + = pd.read_parquet/feather/hdf() # Function read_hdf() accepts `key=`. + = pd.read_sql('
', ) # Pass SQLite3/Alchemy connection. See #SQLite. ``` ```python -.to_json/csv/html/parquet/latex() # Returns a string/bytes if path is omitted. -.to_pickle/excel/feather/hdf() # To_hdf() requires `key=` argument. +.to_json/csv/html/latex/parquet() # Returns a string/bytes if path is omitted. +.to_pickle/excel/feather/hdf() # Method to_hdf() requires `key=`. .to_sql('', ) # Also `if_exists='fail/replace/append'`. ``` * **`'$ pip3 install "pandas[excel]" odfpy lxml pyarrow'` installs dependencies.** -* **Read\_csv() only parses dates of columns that were specified by 'parse\_dates' argument. It automatically tries to detect the format, but it can be helped with 'date\_format' or 'dayfirst' arguments. Both dates and datetimes get stored as pd.Timestamp objects.** -* **If 'parse\_dates' and 'index_col' are the same column, we get a DF with DatetimeIndex. Its `'resample("y/m/d/h")'` method returns a Resampler object that is similar to GroupBy.** +* **Csv functions use the same dialect as standard library's csv module (e.g. `'sep=","'`).** +* **Read\_csv() only parses dates of columns that are listed in 'parse\_dates'. It automatically tries to detect the format, but it can be helped with 'date\_format' or 'dayfirst' arguments.** +* **We get a dataframe with DatetimeIndex if 'parse_dates' argument includes 'index\_col'. Its `'resample("y/m/d/h")'` method returns Resampler object that is similar to GroupBy.** ### GroupBy **Object that groups together rows of a dataframe based on the value of the passed column.** @@ -3380,7 +3371,7 @@ c 6 7 ``` ```python - = .sum/max/mean/idxmax/all() # Or: .agg(lambda : ) + = .sum/max/mean/std/idxmax/count() # Or: .agg(lambda : ) = .rank/diff/cumsum/ffill() # Or: .transform(lambda : ) = .fillna() # Or: .transform(lambda : ) ``` @@ -3419,19 +3410,19 @@ import plotly.express as px, pandas as pd ``` ```python - = px.line(, x=col_key, y=col_key) # Or: px.line(x=, y=) -.update_layout(margin=dict(t=0, r=0, b=0, l=0)) # Also `paper_bgcolor='rgb(0, 0, 0)'`. -.write_html/json/image('') # .show() displays the plot. + = px.line( [, y=col_key/s [, x=col_key]]) # Also px.line(y= [, x=]). +.update_layout(paper_bgcolor='#rrggbb') # Also `margin=dict(t=0, r=0, b=0, l=0)`. +.write_html/json/image('') # Use .show() to display the plot. ``` ```python - = px.area/bar/box(, x=col_key, y=col_key) # Also `color=col_key`. - = px.scatter(, x=col_key, y=col_key) # Also `color/size/symbol=col_key`. - = px.scatter_3d(, x=col_key, y=col_key, …) # `z=col_key`. Also color/size/symbol. - = px.histogram(, x=col_key [, nbins=]) # Number of bins depends on DF size. + = px.area/bar/box(, x=col_key, y=col_keys) # Also `color=col_key`. All are optional. + = px.scatter(, x=col_key, y=col_keys) # Also `color/size/symbol=col_key`. Same. + = px.scatter_3d(, x=col_key, y=col_key, …) # `z=col_key`. Also color, size, symbol. + = px.histogram(, x=col_keys, y=col_key) # Also color, nbins. All are optional. ``` -#### Displays a line chart of total coronavirus deaths per million grouped by continent: +#### Displays a line chart of total COVID-19 deaths per million grouped by continent: ![Covid Deaths](web/covid_deaths.png)
@@ -3439,7 +3430,7 @@ import plotly.express as px, pandas as pd ```python covid = pd.read_csv('https://raw.githubusercontent.com/owid/covid-19-data/8dde8ca49b' '6e648c17dd420b2726ca0779402651/public/data/owid-covid-data.csv', - usecols=['iso_code', 'date', 'total_deaths', 'population']) + usecols=['iso_code', 'date', 'population', 'total_deaths']) continents = pd.read_csv('https://gto76.github.io/python-cheatsheet/web/continents.csv', usecols=['Three_Letter_Country_Code', 'Continent_Name']) df = pd.merge(covid, continents, left_on='iso_code', right_on='Three_Letter_Country_Code') @@ -3447,17 +3438,17 @@ df = df.groupby(['Continent_Name', 'date']).sum().reset_index() df['Total Deaths per Million'] = df.total_deaths * 1e6 / df.population df = df[df.date > '2020-03-14'] df = df.rename({'date': 'Date', 'Continent_Name': 'Continent'}, axis='columns') -px.line(df, x='Date', y='Total Deaths per Million', color='Continent').show() +px.line(df, x='Date', y='Total Deaths per Million', color='Continent') ``` -#### Displays a multi-axis line chart of total coronavirus cases and changes in prices of Bitcoin, Dow Jones and gold: +#### Displays a multi-axis line chart of total COVID-19 cases and changes in prices of Bitcoin, Dow Jones and gold: ![Covid Cases](web/covid_cases.png) -
+
```python # $ pip3 install pandas lxml selenium plotly -import pandas as pd, selenium.webdriver, plotly.graph_objects as go +import pandas as pd, selenium.webdriver, io, plotly.graph_objects as go def main(): covid, (bitcoin, gold, dow) = get_covid_cases(), get_tickers() @@ -3465,24 +3456,25 @@ def main(): display_data(df) def get_covid_cases(): - url = 'https://covid.ourworldindata.org/data/owid-covid-data.csv' - df = pd.read_csv(url, usecols=['location', 'date', 'total_cases'], parse_dates=['date']) - df = df[df.location == 'World'] + url = 'https://catalog.ourworldindata.org/garden/covid/latest/compact/compact.csv' + df = pd.read_csv(url, parse_dates=['date']) + df = df[df.country == 'World'] s = df.set_index('date').total_cases return s.rename('Total Cases') def get_tickers(): with selenium.webdriver.Chrome() as driver: + driver.implicitly_wait(10) symbols = {'Bitcoin': 'BTC-USD', 'Gold': 'GC=F', 'Dow Jones': '%5EDJI'} - for name, symbol in symbols.items(): - yield get_ticker(driver, name, symbol) + return [get_ticker(driver, name, symbol) for name, symbol in symbols.items()] def get_ticker(driver, name, symbol): url = f'https://finance.yahoo.com/quote/%7Bsymbol%7D/history/' driver.get(url + '?period1=1579651200&period2=9999999999') if buttons := driver.find_elements('xpath', '//button[@name="reject"]'): buttons[0].click() - dataframes = pd.read_html(driver.page_source, parse_dates=['Date']) + html = io.StringIO(driver.page_source) + dataframes = pd.read_html(html, parse_dates=['Date']) s = dataframes[0].set_index('Date').Open return s.rename(name) @@ -3522,37 +3514,38 @@ Appendix ```python # $ pip3 install cython -import pyximport; pyximport.install() # Module that runs imported Cython scripts. -import # Script must be saved with '.pyx' extension. -.main() # Main() isn't automatically executed. +import pyximport; pyximport.install() # Module that runs Cython scripts. +import # Script must have '.pyx' extension. ``` -#### Definitions: -* **All `'cdef'` definitions are optional, but they contribute to the speed-up.** -* **Also supports C pointers via `'*'` and `'&'`, structs, unions, and enums.** +#### All `'cdef'` definitions are optional, but they contribute to the speed-up: +```python +cdef [= ] # Either Python or C type variable. +cdef * [= &] # Use [0] to get the value. +cdef [size] [= ] # Also `[:] = `. +cdef * [= ] # E.g. `< *> malloc(n_bytes)`. +``` ```python -cdef [= ] -cdef [n_elements] [= ] -cdef ( ): ... +cdef ( [*]): ... # Omitted types default to `object`. ``` ```python -cdef class : - cdef public - def __init__(self, ): - self. = +cdef class : # Also `cdef struct :`. + cdef public [*] # Also `... [*]`. + def __init__(self, ): # Also `cdef __dealloc__(self):`. + self. = # Also `... free()`. ``` ### Virtual Environments **System for installing libraries directly into project's directory.** ```perl -$ python3 -m venv NAME # Creates virtual environment in current directory. -$ source NAME/bin/activate # Activates it. On Windows run `NAME\Scripts\activate`. -$ pip3 install LIBRARY # Installs the library into active environment. -$ python3 FILE # Runs the script in active environment. Also `./FILE`. -$ deactivate # Deactivates the active virtual environment. +$ python3 -m venv NAME # Creates virtual environment in current directory. +$ source NAME/bin/activate # Activates it. On Windows run `NAME\Scripts\activate`. +$ pip3 install LIBRARY # Installs the library into active environment. +$ python3 FILE # Runs the script in active environment. Also `./FILE`. +$ deactivate # Deactivates the active virtual environment. ``` ### Basic Script Template diff --git a/index.html b/index.html index 75c7b34cc..f0368a21d 100644 --- a/index.html +++ b/index.html @@ -56,7 +56,7 @@
- +
@@ -94,7 +94,7 @@
ToC = {
     '1. Collections': [List, Dictionary, Set, Tuple, Range, Enumerate, Iterator, Generator],
     '2. Types':       [Type, String, Regular_Exp, Format, Numbers, Combinatorics, Datetime],
-    '3. Syntax':      [Args, Inline, Import, Decorator, Class, Duck_Types, Enum, Exception],
+    '3. Syntax':      [Function, Inline, Import, Decorator, Class, Duck_Type, Enum, Except],
     '4. System':      [Exit, Print, Input, Command_Line_Arguments, Open, Path, OS_Commands],
     '5. Data':        [JSON, Pickle, CSV, SQLite, Bytes, Struct, Array, Memory_View, Deque],
     '6. Advanced':    [Operator, Match_Stmt, Logging, Introspection, Threading, Coroutines],
@@ -107,26 +107,26 @@
 
 
 
-

#Main

if __name__ == '__main__':      # Skips next line if file was imported.
-    main()                      # Runs `def main(): ...` function.
+

#Main

if __name__ == '__main__':      # Skips indented lines of code if file was imported.
+    main()                      # Executes user-defined `def main(): ...` function.
 
-

#List

<list> = [<el_1>, <el_2>, ...]  # Creates new list. Also list(<collection>).
+

#List

<list> = [<el_1>, <el_2>, ...]  # Creates a new list object. Also list(<collection>).
 
-
<el>   = <list>[index]          # First index is 0. Last -1. Allows assignments.
+
<el>   = <list>[index]          # First index is 0, last -1. Also `<list>[i] = <el>`.
 <list> = <list>[<slice>]        # Also <list>[from_inclusive : to_exclusive : ±step].
 
-
<list>.append(<el>)             # Appends element to the end. Also <list> += [<el>].
-<list>.extend(<collection>)     # Appends elements to the end. Also <list> += <coll>.
+
<list>.append(<el>)             # Appends element to the end. Also `<list> += [<el>]`.
+<list>.extend(<collection>)     # Appends multiple elements. Also `<list> += <coll>`.
 
-
<list>.sort()                   # Sorts elements in ascending order.
-<list>.reverse()                # Reverses the list in-place.
-<list> = sorted(<collection>)   # Returns new list with sorted elements.
-<iter> = reversed(<list>)       # Returns reversed iterator of elements.
+
<list>.sort(reverse=False)      # Sorts the elements of the list in ascending order.
+<list>.reverse()                # Reverses the order of elements. Takes linear time.
+<list> = sorted(<collection>)   # Returns a new sorted list. Accepts `reverse=True`.
+<iter> = reversed(<list>)       # Returns reversed iterator. Also list(<iterator>).
 
-
<el>  = max(<collection>)       # Returns largest element. Also min(<el_1>, ...).
-<num> = sum(<collection>)       # Returns sum of elements. Also math.prod(<coll>).
+
<el>  = max(<collection>)       # Returns the largest element. Also min(<el_1>, ...).
+<num> = sum(<collection>)       # Returns a sum of elements. Also math.prod(<coll>).
 
elementwise_sum  = [sum(pair) for pair in zip(list_a, list_b)]
 sorted_by_second = sorted(<collection>, key=lambda el: el[1])
@@ -142,30 +142,30 @@
 <int> = <list>.count(<el>)      # Returns number of occurrences. Also `if <el> in <coll>: ...`.
 <int> = <list>.index(<el>)      # Returns index of the first occurrence or raises ValueError.
 <el>  = <list>.pop()            # Removes and returns item from the end or at index if passed.
-<list>.insert(<int>, <el>)      # Inserts item at index and moves the rest to the right.
-<list>.remove(<el>)             # Removes first occurrence of the item or raises ValueError.
-<list>.clear()                  # Removes all items. Also works on dictionary and set.
+<list>.insert(<int>, <el>)      # Inserts item at passed index and moves the rest to the right.
+<list>.remove(<el>)             # Removes the first occurrence or raises ValueError exception.
+<list>.clear()                  # Removes all list's items. Also works on dictionary and set.
 
-

#Dictionary

<dict> = {key_1: val_1, key_2: val_2, ...}      # Use `<dict>[key]` to get or set the value.
+

#Dictionary

<dict> = {key_1: val_1, key_2: val_2, ...}      # Use `<dict>[key]` to get or assign the value.
 
-
<view> = <dict>.keys()                          # Collection of keys that reflects changes.
-<view> = <dict>.values()                        # Collection of values that reflects changes.
+
<view> = <dict>.keys()                          # A collection of keys that reflects changes.
+<view> = <dict>.values()                        # A collection of values that reflects changes.
 <view> = <dict>.items()                         # Coll. of key-value tuples that reflects chgs.
 
-
value  = <dict>.get(key, default=None)          # Returns default if key is missing.
+
value  = <dict>.get(key, default=None)          # Returns argument default if key is missing.
 value  = <dict>.setdefault(key, default=None)   # Returns and writes default if key is missing.
-<dict> = collections.defaultdict(<type>)        # Returns a dict with default value `<type>()`.
-<dict> = collections.defaultdict(lambda: 1)     # Returns a dict with default value 1.
+<dict> = collections.defaultdict(<type>)        # Dict with automatic default value `<type>()`.
+<dict> = collections.defaultdict(lambda: 1)     # Dictionary with automatic default value 1.
 
<dict> = dict(<collection>)                     # Creates a dict from coll. of key-value pairs.
-<dict> = dict(zip(keys, values))                # Creates a dict from two collections.
-<dict> = dict.fromkeys(keys [, value])          # Creates a dict from collection of keys.
+<dict> = dict(zip(keys, values))                # Creates a dictionary from two collections.
+<dict> = dict.fromkeys(keys [, value])          # Creates a dictionary from collection of keys.
 
<dict>.update(<dict>)                           # Adds items. Replaces ones with matching keys.
 value = <dict>.pop(key)                         # Removes item or raises KeyError if missing.
 {k for k, v in <dict>.items() if v == value}    # Returns set of keys that point to the value.
-{k: v for k, v in <dict>.items() if k in keys}  # Filters the dictionary by keys.
+{k: v for k, v in <dict>.items() if k in keys}  # Returns a dict of items with specified keys.
 

Counter

>>> from collections import Counter
 >>> counter = Counter(['blue', 'blue', 'blue', 'red', 'red'])
@@ -174,52 +174,49 @@
 [('blue', 3), ('red', 2), ('yellow', 1)]
 
-

#Set

<set> = {<el_1>, <el_2>, ...}                   # Use `set()` for empty set.
+

#Set

<set> = {<el_1>, <el_2>, ...}                # Coll. of unique items. Also set(), set(<coll>).
 
-
<set>.add(<el>)                                 # Or: <set> |= {<el>}
-<set>.update(<collection> [, ...])              # Or: <set> |= <set>
+
<set>.add(<el>)                              # Adds item to the set. Same as `<set> |= {<el>}`.
+<set>.update(<collection> [, ...])           # Adds items to the set. Same as `<set> |= <set>`.
 
-
<set>  = <set>.union(<coll.>)                   # Or: <set> | <set>
-<set>  = <set>.intersection(<coll.>)            # Or: <set> & <set>
-<set>  = <set>.difference(<coll.>)              # Or: <set> - <set>
-<set>  = <set>.symmetric_difference(<coll.>)    # Or: <set> ^ <set>
-<bool> = <set>.issubset(<coll.>)                # Or: <set> <= <set>
-<bool> = <set>.issuperset(<coll.>)              # Or: <set> >= <set>
+
<set>  = <set>.union(<coll>)                 # Returns a set of all items. Also <set> | <set>.
+<set>  = <set>.intersection(<coll>)          # Returns all shared items. Also <set> & <set>.
+<set>  = <set>.difference(<coll>)            # Returns set's unique items. Also <set> - <set>.
+<set>  = <set>.symmetric_difference(<coll>)  # Returns non-shared items. Also <set> ^ <set>.
+<bool> = <set>.issuperset(<coll>)            # Returns False if collection has unique items.
+<bool> = <set>.issubset(<coll>)              # Is collection a superset? Also <set> <= <set>.
 
-
<el> = <set>.pop()                              # Raises KeyError if empty.
-<set>.remove(<el>)                              # Raises KeyError if missing.
-<set>.discard(<el>)                             # Doesn't raise an error.
+
<el> = <set>.pop()                           # Removes and returns an item or raises KeyError.
+<set>.remove(<el>)                           # Removes the item or raises KeyError if missing.
+<set>.discard(<el>)                          # Same as remove() but it doesn't raise an error.
 

Frozen Set

  • Is immutable and hashable.
  • -
  • That means it can be used as a key in a dictionary or as an element in a set.
  • +
  • That means it can be used as a key in a dictionary or as an item in a set.
<frozenset> = frozenset(<collection>)
 
-

#Tuple

Tuple is an immutable and hashable list.

<tuple> = ()                               # Empty tuple.
-<tuple> = (<el>,)                          # Or: <el>,
-<tuple> = (<el_1>, <el_2> [, ...])         # Or: <el_1>, <el_2> [, ...]
+

#Tuple

Tuple is an immutable and hashable list.

<tuple> = ()                        # Returns an empty tuple. Also tuple(), tuple(<coll>).
+<tuple> = (<el>,)                   # Returns a tuple with single element. Same as `<el>,`.
+<tuple> = (<el_1>, <el_2> [, ...])  # Returns a tuple. Same as `<el_1>, <el_2> [, ...]`.
 

Named Tuple

Tuple's subclass with named elements.

>>> from collections import namedtuple
 >>> Point = namedtuple('Point', 'x y')
->>> p = Point(1, y=2); p
+>>> p = Point(1, y=2)
+>>> print(p)
 Point(x=1, y=2)
->>> p[0]
-1
->>> p.x
-1
->>> getattr(p, 'y')
-2
+>>> p.x, p[1]
+(1, 2)
 
-

#Range

Immutable and hashable sequence of integers.

<range> = range(stop)                      # range(to_exclusive)
-<range> = range(start, stop)               # range(from_inclusive, to_exclusive)
-<range> = range(start, stop, ±step)        # range(from_inclusive, to_exclusive, ±step_size)
+

#Range

Immutable and hashable sequence of integers.

<range> = range(stop)                # I.e. range(to_exclusive). Integers from 0 to `stop-1`.
+<range> = range(start, stop)         # I.e. range(from_inc, to_exc). From start to `stop-1`.
+<range> = range(start, stop, ±step)  # I.e. range(from_inclusive, to_exclusive, ±step_size).
 
@@ -230,24 +227,25 @@ ...
-

#Iterator

<iter> = iter(<collection>)                # `iter(<iter>)` returns unmodified iterator.
+

#Iterator

Potentially endless stream of elements.

<iter> = iter(<collection>)                # Calling iter(<iter>) returns unmodified iterator.
 <iter> = iter(<function>, to_exclusive)    # A sequence of return values until 'to_exclusive'.
 <el>   = next(<iter> [, default])          # Raises StopIteration or returns 'default' on end.
 <list> = list(<iter>)                      # Returns a list of iterator's remaining elements.
 
+

Itertools

import itertools as it
 
<iter> = it.count(start=0, step=1)         # Returns updated value endlessly. Accepts floats.
-<iter> = it.repeat(<el> [, times])         # Returns element endlessly or 'times' times.
-<iter> = it.cycle(<collection>)            # Repeats the sequence endlessly.
+<iter> = it.repeat(<el> [, times])         # Returns passed element endlessly or 'times' times.
+<iter> = it.cycle(<collection>)            # Repeats the passed sequence of elements endlessly.
 
-
<iter> = it.chain(<coll>, <coll> [, ...])  # Empties collections in order (figuratively).
+
<iter> = it.chain(<coll>, <coll> [, ...])  # Empties collections in order (only figuratively).
 <iter> = it.chain.from_iterable(<coll>)    # Empties collections inside a collection in order.
 
-
<iter> = it.islice(<coll>, to_exclusive)   # Only returns first 'to_exclusive' elements.
-<iter> = it.islice(<coll>, from_inc, …)    # `to_exclusive, +step_size`. Indices can be None.
+
<iter> = it.islice(<coll>, stop)           # Only returns (i.e. yields) first 'stop' elements.
+<iter> = it.islice(<coll>, start, stop)    # Also accepts `+step`. Start and stop can be None.
 

#Generator

  • Any function that contains a yield statement returns a generator.
  • @@ -264,11 +262,11 @@ (10, 12, 14)

#Type

    -
  • Everything is an object.
  • -
  • Every object has a type.
  • +
  • Everything in Python is an object.
  • +
  • Every object has a certain type.
  • Type and class are synonymous.
  • -
<type> = type(<el>)                          # Or: <el>.__class__
-<bool> = isinstance(<el>, <type>)            # Or: issubclass(type(<el>), <type>)
+
<type> = type(<el>)                # Returns object's type. Same as `<el>.__class__`.
+<bool> = isinstance(<el>, <type>)  # Same result as `issubclass(type(<el>), <type>)`.
 
@@ -306,43 +304,44 @@ ┃ decimal.Decimal │ ✓ │ │ │ │ ┃ ┗━━━━━━━━━━━━━━━━━━━━┷━━━━━━━━━━┷━━━━━━━━━━┷━━━━━━━━━━┷━━━━━━━━━━┷━━━━━━━━━━┛
-

#String

Immutable sequence of characters.

<str>  = <str>.strip()                       # Strips all whitespace characters from both ends.
-<str>  = <str>.strip('<chars>')              # Strips passed characters. Also lstrip/rstrip().
+

#String

Immutable sequence of characters.

<str>  = 'abc'                               # Also "abc". Interprets \n, \t, \x00-\xff, etc.
 
-
<list> = <str>.split()                       # Splits on one or more whitespace characters.
-<list> = <str>.split(sep=None, maxsplit=-1)  # Splits on 'sep' str at most 'maxsplit' times.
+
<str>  = <str>.strip()                       # Strips all whitespace characters from both ends.
+<str>  = <str>.strip('<chars>')              # Strips passed characters. Also lstrip/rstrip().
+
+
<list> = <str>.split()                       # Splits it on one or more whitespace characters.
+<list> = <str>.split(sep=None, maxsplit=-1)  # Splits on 'sep' string at most 'maxsplit' times.
 <list> = <str>.splitlines(keepends=False)    # On [\n\r\f\v\x1c-\x1e\x85\u2028\u2029] and \r\n.
-<str>  = <str>.join(<coll_of_strings>)       # Joins elements using string as a separator.
+<str>  = <str>.join(<coll_of_strings>)       # Joins items by using the string as a separator.
 
-
<bool> = <sub_str> in <str>                  # Checks if string contains the substring.
-<bool> = <str>.startswith(<sub_str>)         # Pass tuple of strings for multiple options.
+
<bool> = <sub_str> in <str>                  # Returns True if string contains the substring.
+<bool> = <str>.startswith(<sub_str>)         # Pass tuple of strings to give multiple options.
 <int>  = <str>.find(<sub_str>)               # Returns start index of the first match or -1.
-<int>  = <str>.index(<sub_str>)              # Same, but raises ValueError if there's no match.
 
-
<str>  = <str>.lower()                       # Changes the case. Also upper/capitalize/title().
+
<str>  = <str>.lower()                       # Lowers the case. Also upper/capitalize/title().
+<str>  = <str>.casefold()                    # Lower() that converts ẞ/ß to ss, Σ/ς to σ, etc.
 <str>  = <str>.replace(old, new [, count])   # Replaces 'old' with 'new' at most 'count' times.
-<str>  = <str>.translate(<table>)            # Use `str.maketrans(<dict>)` to generate table.
+<str>  = <str>.translate(table)              # Use `str.maketrans(<dict>)` to generate table.
 
-
<str>  = chr(<int>)                          # Converts int to Unicode character.
-<int>  = ord(<str>)                          # Converts Unicode character to int.
+
<str>  = chr(<int>)                          # Converts passed integer into Unicode character.
+<int>  = ord(<str>)                          # Converts passed Unicode character into integer.
 
  • Use 'unicodedata.normalize("NFC", <str>)' on strings like 'Motörhead' before comparing them to other strings, because 'ö' can be stored as one or two characters.
  • 'NFC' converts such characters to a single character, while 'NFD' converts them to two.
-

Property Methods

<bool> = <str>.isdecimal()                   # Checks for [0-9]. Also [०-९] and [٠-٩].
-<bool> = <str>.isdigit()                     # Checks for [²³¹…] and isdecimal().
-<bool> = <str>.isnumeric()                   # Checks for [¼½¾…], [零〇一…] and isdigit().
-<bool> = <str>.isalnum()                     # Checks for [a-zA-Z…] and isnumeric().
-<bool> = <str>.isprintable()                 # Checks for [ !#$%…] and isalnum().
-<bool> = <str>.isspace()                     # Checks for [ \t\n\r\f\v\x1c-\x1f\x85\xa0…].
-
- +
<bool> = <str>.isdecimal()                   # Checks all chars for [0-9]. Also [०-९], [٠-٩].
+<bool> = <str>.isdigit()                     # Checks for [²³¹…] and isdecimal(). Also [፩-፱].
+<bool> = <str>.isnumeric()                   # Checks for [¼½¾…] and isdigit(). Also [零〇一…].
+<bool> = <str>.isalnum()                     # Checks for [ABC…] and isnumeric(). Also [ªµº…].
+<bool> = <str>.isprintable()                 # Checks for [ !"#$…] and isalnum(). Also emojis.
+<bool> = <str>.isspace()                     # Checks for [ \t\n\r\f\v\x1c\x1d\x1e\x1f\x85…].
+

#Regex

Functions for regular expression matching.

import re
 <str>   = re.sub(r'<regex>', new, text, count=0)  # Substitutes all occurrences with 'new'.
-<list>  = re.findall(r'<regex>', text)            # Returns all occurrences as strings.
+<list>  = re.findall(r'<regex>', text)            # Returns all occurrences of the pattern.
 <list>  = re.split(r'<regex>', text, maxsplit=0)  # Add brackets around regex to keep matches.
 <Match> = re.search(r'<regex>', text)             # First occurrence of the pattern or None.
 <Match> = re.match(r'<regex>', text)              # Searches only at the beginning of the text.
@@ -353,16 +352,16 @@
 
  • Raw string literals do not interpret escape sequences, thus enabling us to use regex-specific escape sequences that cause SyntaxWarning in normal string literals (since 3.12).
  • Argument 'new' of re.sub() can be a function that accepts Match object and returns a str.
  • -
  • Argument 'flags=re.IGNORECASE' can be used with all functions.
  • +
  • Argument 'flags=re.IGNORECASE' can be used with all functions that are listed above.
  • Argument 'flags=re.MULTILINE' makes '^' and '$' match the start/end of each line.
  • -
  • Argument 'flags=re.DOTALL' makes '.' also accept the '\n'.
  • -
  • 're.compile(<regex>)' returns a Pattern object with methods sub(), findall(), …
  • +
  • Argument 'flags=re.DOTALL' makes '.' also accept the '\n' (besides all other chars).
  • +
  • 're.compile(<regex>)' returns a Pattern object with methods sub(), findall(), etc.

Match Object

<str>   = <Match>.group()                         # Returns the whole match. Also group(0).
 <str>   = <Match>.group(1)                        # Returns part inside the first brackets.
-<tuple> = <Match>.groups()                        # Returns all bracketed parts.
-<int>   = <Match>.start()                         # Returns start index of the match.
-<int>   = <Match>.end()                           # Returns exclusive end index of the match.
+<tuple> = <Match>.groups()                        # Returns all bracketed parts as strings.
+<int>   = <Match>.start()                         # Returns start index of the whole match.
+<int>   = <Match>.end()                           # Returns its exclusive end index.
 

Special Sequences

'\d' == '[0-9]'                                   # Also [०-९…]. Matches a decimal character.
@@ -374,8 +373,8 @@
 
  • By default, decimal characters and alphanumerics from all alphabets are matched unless 'flags=re.ASCII' is used. It restricts special sequence matches to the first 128 Unicode characters and also prevents '\s' from accepting '\x1c', '\x1d', '\x1e' and '\x1f' (non-printable characters that divide text into files, tables, rows and fields, respectively).
  • Use a capital letter for negation (all non-ASCII characters will be matched when used in combination with ASCII flag).
  • -

    #Format

    <str> = f'{<el_1>}, {<el_2>}'            # Curly brackets can also contain expressions.
    -<str> = '{}, {}'.format(<el_1>, <el_2>)  # Or: '{0}, {a}'.format(<el_1>, a=<el_2>)
    +

    #Format

    <str> = f'{<el_1>}, {<el_2>}'            # Curly braces can also contain expressions.
    +<str> = '{}, {}'.format(<el_1>, <el_2>)  # Same as '{0}, {a}'.format(<el_1>, a=<el_2>).
     <str> = '%s, %s' % (<el_1>, <el_2>)      # Redundant and inferior C-style formatting.
     
    @@ -393,8 +392,8 @@
      -
    • Objects are rendered using 'format(<el>, "<options>")'.
    • -
    • Options can be generated dynamically: f'{<el>:{<str/int>}[…]}'.
    • +
    • Objects are rendered by calling the 'format(<el>, "<options>")' function.
    • +
    • Options inside curly braces can be generated dynamically: f'{<el>:{<str/int>}[…]}'.
    • Adding '=' to the expression prepends it to the output: f'{1+1=}' returns '1+1=2'.
    • Adding '!r' to the expression converts object to string by calling its repr() method.
    @@ -450,81 +449,81 @@
  • When both rounding up and rounding down are possible, the one that returns result with even last digit is chosen. That makes '{6.5:.0f}' a '6' and '{7.5:.0f}' an '8'.
  • This rule only effects numbers that can be represented exactly by a float (.5, .25, …).
  • -

    Ints

    {90:c}                                   # 'Z'. Unicode character with value 90.
    -{90:b}                                   # '1011010'. Number 90 in binary.
    -{90:X}                                   # '5A'. Number 90 in uppercase hexadecimal.
    +

    Ints

    {90:c}                                   # 'Z'. Returns Unicode character with value 90.
    +{90:b}                                   # '1011010'. Binary representation of the int.
    +{90:X}                                   # '5A'. Hexadecimal with upper-case letters.
     
    -

    #Numbers

    <int>      = int(<float/str/bool>)                # Or: math.trunc(<float>)
    -<float>    = float(<int/str/bool>)                # Or: <int/float>e±<int>
    -<complex>  = complex(real=0, imag=0)              # Or: <int/float> ± <int/float>j
    -<Fraction> = fractions.Fraction(0, 1)             # Or: Fraction(numerator=0, denominator=1)
    -<Decimal>  = decimal.Decimal(<str/int>)           # Or: Decimal((sign, digits, exponent))
    +

    #Numbers

    <int>      = int(<float/str/bool>)             # Whole number of any size. Truncates floats.
    +<float>    = float(<int/str/bool>)             # 64-bit decimal number. Also <float>e±<int>.
    +<complex>  = complex(real=0, imag=0)           # A complex number. Also `<float> ± <float>j`.
    +<Fraction> = fractions.Fraction(<int>, <int>)  # E.g. `Fraction(1, 2) / 3 == Fraction(1, 6)`.
    +<Decimal>  = decimal.Decimal(<str/int/tuple>)  # E.g. `Decimal((1, (2, 3), 4)) == -230_000`.
     
      -
    • 'int(<str>)' and 'float(<str>)' raise ValueError on malformed strings.
    • -
    • Decimal numbers are stored exactly, unlike most floats where '1.1 + 2.2 != 3.3'.
    • -
    • Floats can be compared with: 'math.isclose(<float>, <float>)'.
    • +
    • 'int(<str>)' and 'float(<str>)' raise ValueError if passed string is malformed.
    • +
    • Decimal objects store numbers exactly, unlike most floats where '1.1 + 2.2 != 3.3'.
    • +
    • Floats can be compared with: 'math.isclose(<float>, <float>, rel_tol=1e-09)'.
    • Precision of decimal operations is set with: 'decimal.getcontext().prec = <int>'.
    • +
    • Bools can be used anywhere ints can, because bool is a subclass of int: 'True + 1 == 2'.
    -

    Basic Functions

    <num> = pow(<num>, <num>)                         # Or: <number> ** <number>
    -<num> = abs(<num>)                                # <float> = abs(<complex>)
    -<num> = round(<num> [, ±ndigits])                 # `round(126, -1) == 130`
    +

    Built-in Functions

    <num> = pow(<num>, <num>)                      # E.g. `pow(2, 3) == 2 ** 3 == 8`.
    +<num> = abs(<num>)                             # E.g. `abs(complex(3, 4)) == 5`.
    +<num> = round(<num> [, ±ndigits])              # E.g. `round(123, -1) == 120`.
    +<num> = min(<collection>)                      # Also max(<num>, <num> [, ...]).
    +<num> = sum(<collection>)                      # Also math.prod(<collection>).
     
    -

    Math

    from math import e, pi, inf, nan, isinf, isnan    # `<el> == nan` is always False.
    -from math import sin, cos, tan, asin, acos, atan  # Also: degrees, radians.
    -from math import log, log10, log2                 # Log can accept base as second arg.
    +

    Math

    from math import floor, ceil, trunc            # They convert floats into integers.
    +from math import pi, inf, nan, isnan           # `inf * 0` and `nan + 1` return nan.
    +from math import sqrt, factorial               # `sqrt(-1)` will raise ValueError.
    +from math import sin, cos, tan                 # Also: asin, acos, degrees, radians.
    +from math import log, log10, log2              # Log accepts base as second argument.
     
    -

    Statistics

    from statistics import mean, median, variance     # Also: stdev, quantiles, groupby.
    +

    Statistics

    from statistics import mean, median, mode      # Mode returns the most common item.
    +from statistics import variance, stdev         # Also: pvariance, pstdev, quantiles.
     
    -

    Random

    from random import random, randint, choice        # Also: shuffle, gauss, triangular, seed.
    -<float> = random()                                # A float inside [0, 1).
    -<int>   = randint(from_inc, to_inc)               # An int inside [from_inc, to_inc].
    -<el>    = choice(<sequence>)                      # Keeps the sequence intact.
    +

    Random

    from random import random, randint, uniform    # Also: gauss, choice, shuffle, seed.
     
    -

    Bin, Hex

    <int> = ±0b<bin>                                  # Or: ±0x<hex>
    -<int> = int('±<bin>', 2)                          # Or: int('±<hex>', 16)
    -<int> = int('±0b<bin>', 0)                        # Or: int('±0x<hex>', 0)
    -<str> = bin(<int>)                                # Returns '[-]0b<bin>'. Also hex().
    +
    <float> = random()                             # Select a random float from [0, 1).
    +<num>   = randint/uniform(a, b)                # Select an int/float from [a, b].
    +<float> = gauss(mean, stdev)                   # Also triangular(low, high, mode).
    +<el>    = choice(<sequence>)                   # Keeps it intact. Also sample(p, n).
    +shuffle(<list>)                                # Works on all mutable sequences.
    +
    +

    Hexadecimal Numbers

    <int> = 0x<hex>                                # E.g. `0xFF == 255`. Also 0b<bin>.
    +<int> = int('±<hex>', 16)                      # Also int('±0x<hex>/±0b<bin>', 0).
    +<str> = hex(<int>)                             # Returns '[-]0x<hex>'. Also bin().
     
    -

    Bitwise Operators

    <int> = <int> & <int>                             # And (0b1100 & 0b1010 == 0b1000).
    -<int> = <int> | <int>                             # Or  (0b1100 | 0b1010 == 0b1110).
    -<int> = <int> ^ <int>                             # Xor (0b1100 ^ 0b1010 == 0b0110).
    -<int> = <int> << n_bits                           # Left shift. Use >> for right.
    -<int> = ~<int>                                    # Not. Also -<int> - 1.
    +

    Bitwise Operators

    <int> = <int> & <int>                          # E.g. `0b1100 & 0b1010 == 0b1000`.
    +<int> = <int> | <int>                          # E.g. `0b1100 | 0b1010 == 0b1110`.
    +<int> = <int> ^ <int>                          # E.g. `0b1100 ^ 0b1010 == 0b0110`.
    +<int> = <int> << n_bits                        # E.g. `0b1111 << 4 == 0b11110000`.
    +<int> = ~<int>                                 # E.g. `~0b1 == -0b10 == -(0b1+1)`.
     

    #Combinatorics

    import itertools as it
     
    -
    >>> list(it.product([0, 1], repeat=3))
    -[(0, 0, 0), (0, 0, 1), (0, 1, 0), (0, 1, 1),
    - (1, 0, 0), (1, 0, 1), (1, 1, 0), (1, 1, 1)]
    -
    -
    >>> list(it.product('abc', 'abc'))                    #   a  b  c
    -[('a', 'a'), ('a', 'b'), ('a', 'c'),                  # a x  x  x
    - ('b', 'a'), ('b', 'b'), ('b', 'c'),                  # b x  x  x
    - ('c', 'a'), ('c', 'b'), ('c', 'c')]                  # c x  x  x
    -
    -
    >>> list(it.combinations('abc', 2))                   #   a  b  c
    -[('a', 'b'), ('a', 'c'),                              # a .  x  x
    - ('b', 'c')]                                          # b .  .  x
    +
    >>> list(it.product('abc', repeat=2))        #   a  b  c
    +[('a', 'a'), ('a', 'b'), ('a', 'c'),         # a x  x  x
    + ('b', 'a'), ('b', 'b'), ('b', 'c'),         # b x  x  x
    + ('c', 'a'), ('c', 'b'), ('c', 'c')]         # c x  x  x
     
    -
    >>> list(it.combinations_with_replacement('abc', 2))  #   a  b  c
    -[('a', 'a'), ('a', 'b'), ('a', 'c'),                  # a x  x  x
    - ('b', 'b'), ('b', 'c'),                              # b .  x  x
    - ('c', 'c')]                                          # c .  .  x
    +
    >>> list(it.permutations('abc', 2))          #   a  b  c
    +[('a', 'b'), ('a', 'c'),                     # a .  x  x
    + ('b', 'a'), ('b', 'c'),                     # b x  .  x
    + ('c', 'a'), ('c', 'b')]                     # c x  x  .
     
    -
    >>> list(it.permutations('abc', 2))                   #   a  b  c
    -[('a', 'b'), ('a', 'c'),                              # a .  x  x
    - ('b', 'a'), ('b', 'c'),                              # b x  .  x
    - ('c', 'a'), ('c', 'b')]                              # c x  x  .
    +
    >>> list(it.combinations('abc', 2))          #   a  b  c
    +[('a', 'b'), ('a', 'c'),                     # a .  x  x
    + ('b', 'c')                                  # b .  .  x
    +]                                            # c .  .  .
     

    #Datetime

    Provides 'date', 'time', 'datetime' and 'timedelta' classes. All are immutable and hashable.

    # $ pip3 install python-dateutil
     from datetime import date, time, datetime, timedelta, timezone
    @@ -538,10 +537,10 @@
     <TD> = timedelta(weeks=0, days=0, hours=0)  # Also: `minutes=0, seconds=0, microseconds=0`.
     
      -
    • Aware times and datetimes have defined timezone, while naive don't. If object is naive, it is presumed to be in the system's timezone!
    • -
    • 'fold=1' means the second pass in case of time jumping back for one hour.
    • +
    • Times and datetimes that have defined timezone are called aware and ones that don't, naive. If time or datetime object is naive, it is presumed to be in the system's timezone!
    • +
    • 'fold=1' means the second pass in case of time jumping back (usually for one hour).
    • Timedelta normalizes arguments to ±days, seconds (< 86 400) and microseconds (< 1M). Its str() method returns '[±D, ]H:MM:SS[.…]' and total_seconds() a float of all seconds.
    • -
    • Use '<D/DT>.weekday()' to get the day of the week as an int, with Monday being 0.
    • +
    • Use '<D/DT>.weekday()' to get the day of the week as an integer, with Monday being 0.

    Now

    <D/DTn> = D/DT.today()                      # Current local date or naive DT. Also DT.now().
     <DTa>   = DT.now(<tzinfo>)                  # Aware DT from current time in passed timezone.
    @@ -550,8 +549,8 @@
     
    • To extract time use '<DTn>.time()', '<DTa>.time()' or '<DTa>.timetz()'.
    -

    Timezone

    <tzinfo> = timezone.utc                     # London without daylight saving time (DST).
    -<tzinfo> = timezone(<timedelta>)            # Timezone with fixed offset from UTC.
    +

    Timezones

    <tzinfo> = timezone.utc                     # Coordinated universal time (UK without DST).
    +<tzinfo> = timezone(<timedelta>)            # Timezone with fixed offset from universal time.
     <tzinfo> = dateutil.tz.tzlocal()            # Local timezone with dynamic offset from UTC.
     <tzinfo> = zoneinfo.ZoneInfo('<iana_key>')  # 'Continent/City_Name' zone with dynamic offset.
     <DTa>    = <DT>.astimezone([<tzinfo>])      # Converts DT to the passed or local fixed zone.
    @@ -563,7 +562,7 @@
     
  • To get ZoneInfo() to work on Windows run '> pip3 install tzdata'.
  • Encode

    <D/T/DT> = D/T/DT.fromisoformat(<str>)      # Object from ISO string. Raises ValueError.
    -<DT>     = DT.strptime(<str>, '<format>')   # Datetime from str, according to format.
    +<DT>     = DT.strptime(<str>, '<format>')   # Datetime from custom string. See Format.
     <D/DTn>  = D/DT.fromordinal(<int>)          # D/DT from days since the Gregorian NYE 1.
     <DTn>    = DT.fromtimestamp(<float>)        # Local naive DT from seconds since the Epoch.
     <DTa>    = DT.fromtimestamp(<float>, <tz>)  # Aware datetime from seconds since the Epoch.
    @@ -573,9 +572,9 @@
     
  • ISO strings come in following forms: 'YYYY-MM-DD', 'HH:MM:SS.mmmuuu[±HH:MM]', or both separated by an arbitrary character. All parts following the hours are optional.
  • Python uses the Unix Epoch: '1970-01-01 00:00 UTC', '1970-01-01 01:00 CET', …
  • -

    Decode

    <str>    = <D/T/DT>.isoformat(sep='T')      # Also `timespec='auto/hours/minutes/seconds/…'`.
    +

    Decode

    <str>    = <D/T/DT>.isoformat(sep='T')      # Also `timespec='auto/hours/minutes/seconds…'`.
     <str>    = <D/T/DT>.strftime('<format>')    # Custom string representation of the object.
    -<int>    = <D/DT>.toordinal()               # Days since Gregorian NYE 1, ignoring time and tz.
    +<int>    = <D/DT>.toordinal()               # Days since NYE 1. Ignores DT's time and zone.
     <float>  = <DTn>.timestamp()                # Seconds since the Epoch, from local naive DT.
     <float>  = <DTa>.timestamp()                # Seconds since the Epoch, from aware datetime.
     
    @@ -586,44 +585,43 @@
      -
    • '%z' accepts '±HH[:]MM' and returns '±HHMM' or empty string if datetime is naive.
    • -
    • '%Z' accepts 'UTC/GMT' and local timezone's code and returns timezone's name, 'UTC[±HH:MM]' if timezone is nameless, or an empty string if datetime is naive.
    • +
    • '%z' accepts '±HH[:]MM' and returns '±HHMM' or empty string if object is naive.
    • +
    • '%Z' accepts 'UTC/GMT' and local timezone's code and returns timezone's name, 'UTC[±HH:MM]' if timezone is nameless, or an empty string if object is naive.

    Arithmetics

    <bool>   = <D/T/DTn> > <D/T/DTn>            # Ignores time jumps (fold attribute). Also ==.
     <bool>   = <DTa>     > <DTa>                # Ignores time jumps if they share tzinfo object.
     <TD>     = <D/DTn>   - <D/DTn>              # Ignores jumps. Convert to UTC for actual delta.
    -<TD>     = <DTa>     - <DTa>                # Ignores jumps if they share tzinfo object.
    +<TD>     = <DTa>     - <DTa>                # Ignores jumps if they share the tzinfo object.
     <D/DT>   = <D/DT>    ± <TD>                 # Returned datetime can fall into missing hour.
     <TD>     = <TD>      * <float>              # Also `<TD> = abs(<TD>)`, `<TD> = <TD> ± <TD>`.
     <float>  = <TD>      / <TD>                 # Also `(<int>, <TD>) = divmod(<TD>, <TD>)`.
     
    -

    #Arguments

    Inside Function Call

    func(<positional_args>)                           # func(0, 0)
    -func(<keyword_args>)                              # func(x=0, y=0)
    -func(<positional_args>, <keyword_args>)           # func(0, y=0)
    -
    - - -

    Inside Function Definition

    def func(<nondefault_args>): ...                  # def func(x, y): ...
    -def func(<default_args>): ...                     # def func(x=0, y=0): ...
    -def func(<nondefault_args>, <default_args>): ...  # def func(x, y=0): ...
    +

    #Function

    Independent block of code that returns a value when called.

    def <func_name>(<nondefault_args>): ...                  # E.g. `def func(x, y): ...`.
    +def <func_name>(<default_args>): ...                     # E.g. `def func(x=0, y=0): ...`.
    +def <func_name>(<nondefault_args>, <default_args>): ...  # E.g. `def func(x, y=0): ...`.
     
    +
      -
    • Default values are evaluated when function is first encountered in the scope.
    • -
    • Any mutation of a mutable default value will persist between invocations!
    • +
    • Function returns None if it doesn't encounter 'return <obj/exp>' statement.
    • +
    • Run 'global <var_name>' inside the function before assigning to global variable.
    • +
    • Default values are evaluated when function is first encountered in the scope. Any mutation of a mutable default value will persist between invocations!
    -

    #Splat Operator

    Inside Function Call

    Splat expands a collection into positional arguments, while splatty-splat expands a dictionary into keyword arguments.

    args   = (1, 2)
    -kwargs = {'x': 3, 'y': 4, 'z': 5}
    -func(*args, **kwargs)
    -
    +

    Function Call

    <obj> = <function>(<positional_args>)                    # E.g. `func(0, 0)`.
    +<obj> = <function>(<keyword_args>)                       # E.g. `func(x=0, y=0)`.
    +<obj> = <function>(<positional_args>, <keyword_args>)    # E.g. `func(0, y=0)`.
    +
    +

    #Splat Operator

    Splat expands a collection into positional arguments, while splatty-splat expands a dictionary into keyword arguments.

    args, kwargs = (1, 2), {'z': 3}
    +func(*args, **kwargs)
    +
    -

    Is the same as:

    func(1, 2, x=3, y=4, z=5)
    +

    Is the same as:

    func(1, 2, z=3)
     
    -

    Inside Function Definition

    Splat combines zero or more positional arguments into a tuple, while splatty-splat combines zero or more keyword arguments into a dictionary.

    def add(*a):
    +

    Inside Function Definition

    Splat combines zero or more positional arguments into a tuple, while splatty-splat combines zero or more keyword arguments into a dictionary.

    def add(*a):
         return sum(a)
     
    @@ -631,39 +629,32 @@
    >>> add(1, 2, 3)
     6
     
    -

    Legal argument combinations:

    def f(*args): ...               # f(1, 2, 3)
    -def f(x, *args): ...            # f(1, 2, 3)
    -def f(*args, z): ...            # f(1, 2, z=3)
    +

    Allowed compositions of arguments and the ways they can be called:

    ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━┯━━━━━━━━━━━━━━┯━━━━━━━━━━━━━━┯━━━━━━━━━━━━━━━━┓
    +┃                           │  func(1, 2)  │ func(1, y=2) │ func(x=1, y=2) ┃
    +┠───────────────────────────┼──────────────┼──────────────┼────────────────┨
    +┃ func(x, *args, **kwargs): │      ✓       │      ✓       │       ✓        ┃
    +┃ func(*args, y, **kwargs): │              │      ✓       │       ✓        ┃
    +┃ func(*, x, **kwargs):     │              │              │       ✓        ┃
    +┗━━━━━━━━━━━━━━━━━━━━━━━━━━━┷━━━━━━━━━━━━━━┷━━━━━━━━━━━━━━┷━━━━━━━━━━━━━━━━┛
     
    -
    def f(**kwargs): ...            # f(x=1, y=2, z=3)
    -def f(x, **kwargs): ...         # f(x=1, y=2, z=3) | f(1, y=2, z=3)
    -
    -
    def f(*args, **kwargs): ...     # f(x=1, y=2, z=3) | f(1, y=2, z=3) | f(1, 2, z=3) | f(1, 2, 3)
    -def f(x, *args, **kwargs): ...  # f(x=1, y=2, z=3) | f(1, y=2, z=3) | f(1, 2, z=3) | f(1, 2, 3)
    -def f(*args, y, **kwargs): ...  # f(x=1, y=2, z=3) | f(1, y=2, z=3)
    -
    -
    def f(*, x, y, z): ...          # f(x=1, y=2, z=3)
    -def f(x, *, y, z): ...          # f(x=1, y=2, z=3) | f(1, y=2, z=3)
    -def f(x, y, *, z): ...          # f(x=1, y=2, z=3) | f(1, y=2, z=3) | f(1, 2, z=3)
    -
    -

    Other Uses

    <list>  = [*<coll.> [, ...]]    # Or: list(<collection>) [+ ...]
    -<tuple> = (*<coll.>, [...])     # Or: tuple(<collection>) [+ ...]
    -<set>   = {*<coll.> [, ...]}    # Or: set(<collection>) [| ...]
    -<dict>  = {**<dict> [, ...]}    # Or: <dict> | ...
    +

    Other Uses

    <list>  = [*<collection> [, ...]]  # Or: list(<coll>) [+ ...]
    +<tuple> = (*<collection>, [...])   # Or: tuple(<coll>) [+ ...]
    +<set>   = {*<collection> [, ...]}  # Or: set(<coll>) [| ...]
    +<dict>  = {**<dict> [, ...]}       # Or: <dict> | ...
     
    -
    head, *body, tail = <coll.>     # Head or tail can be omitted.
    +
    head, *body, tail = <collection>   # Head or tail can be omitted.
     

    #Inline

    Lambda

    <func> = lambda: <return_value>                     # A single statement function.
     <func> = lambda <arg_1>, <arg_2>: <return_value>    # Also allows default arguments.
     
    -

    Comprehensions

    <list> = [i+1 for i in range(10)]                   # Or: [1, 2, ..., 10]
    -<iter> = (i for i in range(10) if i > 5)            # Or: iter([6, 7, 8, 9])
    -<set>  = {i+5 for i in range(10)}                   # Or: {5, 6, ..., 14}
    -<dict> = {i: i*2 for i in range(10)}                # Or: {0: 0, 1: 2, ..., 9: 18}
    +

    Comprehensions

    <list> = [i+1 for i in range(10)]                   # Returns [1, 2, ..., 10].
    +<iter> = (i for i in range(10) if i > 5)            # Returns iter([6, 7, 8, 9]).
    +<set>  = {i+5 for i in range(10)}                   # Returns {5, 6, ..., 14}.
    +<dict> = {i: i*2 for i in range(10)}                # Returns {0: 0, 1: 2, ..., 9: 18}.
     
    >>> [l+r for l in 'abc' for r in 'abc']             # Inner loop is on the right side.
    @@ -672,9 +663,9 @@
     

    Map, Filter, Reduce

    from functools import reduce
     
    -
    <iter> = map(lambda x: x + 1, range(10))            # Or: iter([1, 2, ..., 10])
    -<iter> = filter(lambda x: x > 5, range(10))         # Or: iter([6, 7, 8, 9])
    -<obj>  = reduce(lambda out, x: out + x, range(10))  # Or: 45
    +
    <iter> = map(lambda x: x + 1, range(10))            # Returns iter([1, 2, ..., 10]).
    +<iter> = filter(lambda x: x > 5, range(10))         # Returns iter([6, 7, 8, 9]).
    +<obj>  = reduce(lambda out, x: out + x, range(10))  # Returns 45. Accepts 'initial'.
     

    Any, All

    <bool> = any(<collection>)                          # Is `bool(<el>)` True for any el?
     <bool> = all(<collection>)                          # True for all? Also True if empty.
    @@ -683,32 +674,40 @@
     

    Conditional Expression

    <obj> = <exp> if <condition> else <exp>             # Only one expression is evaluated.
     
    -
    >>> [a if a else 'zero' for a in (0, 1, 2, 3)]      # `any([0, '', [], None]) == False`
    +
    >>> [i if i else 'zero' for i in (0, 1, 2, 3)]      # `any([0, '', [], None]) == False`
     ['zero', 1, 2, 3]
     
    +

    And, Or

    <obj> = <exp> and <exp> [and ...]                   # Returns first false or last object.
    +<obj> = <exp> or <exp> [or ...]                     # Returns first true or last object.
    +
    + +

    Walrus Operator

    >>> [i for ch in '0123' if (i := int(ch)) > 0]      # Assigns to variable mid-sentence.
    +[1, 2, 3]
    +
    +

    Named Tuple, Enum, Dataclass

    from collections import namedtuple
    -Point = namedtuple('Point', 'x y')                  # Creates a tuple's subclass.
    +Point = namedtuple('Point', 'x y')                  # Creates tuple's subclass.
     point = Point(0, 0)                                 # Returns its instance.
    -
    -
    from enum import Enum
    -Direction = Enum('Direction', 'N E S W')            # Creates an enum.
    +from enum import Enum
    +Direction = Enum('Direction', 'N E S W')            # Creates Enum's subclass.
     direction = Direction.N                             # Returns its member.
    -
    -
    from dataclasses import make_dataclass
    +
    +from dataclasses import make_dataclass
     Player = make_dataclass('Player', ['loc', 'dir'])   # Creates a class.
     player = Player(point, direction)                   # Returns its instance.
    -
    -

    #Imports

    Mechanism that makes code in one file available to another file.

    import <module>            # Imports a built-in or '<module>.py'.
    -import <package>           # Imports a built-in or '<package>/__init__.py'.
    -import <package>.<module>  # Imports a built-in or '<package>/<module>.py'.
    +
    + +

    #Imports

    Mechanism that makes code in one file available to another file.

    import <module>                # Imports a built-in or '<module>.py'.
    +import <package>               # Imports a built-in or '<package>/__init__.py'.
    +import <package>.<module>      # Imports a built-in or '<package>/<module>.py'.
     
      -
    • Package is a collection of modules, but it can also define its own objects.
    • +
    • Package is a collection of modules, but it can also define its own functions, classes, etc.
    • On a filesystem this corresponds to a directory of Python files with an optional init script.
    • -
    • Running 'import <package>' does not automatically provide access to the package's modules unless they are explicitly imported in its init script.
    • +
    • Running 'import <package>' does not automatically provide access to the package's modules unless they are explicitly imported in the '<package>/__init__.py' script.
    • Directory of the file that is passed to python command serves as a root of local imports.
    • For relative imports use 'from .[…][<pkg/module>[.…]] import <obj>'.
    @@ -737,8 +736,7 @@ 30
      -
    • Partial is also useful in cases when a function needs to be passed as an argument because it enables us to set its arguments beforehand.
    • -
    • A few examples being: 'defaultdict(<func>)', 'iter(<func>, to_exc)' and dataclass's 'field(default_factory=<func>)'.
    • +
    • Partial is also useful in cases when a function needs to be passed as an argument because it enables us to set its arguments beforehand ('collections.defaultdict(<func>)', 'iter(<func>, to_exc)' and 'dataclasses.field(default_factory=<func>)').

    Non-Local

    If variable is being assigned to anywhere in the scope, it is regarded as a local variable, unless it is declared as a 'global' or a 'nonlocal'.

    def get_counter():
         i = 0
    @@ -754,10 +752,7 @@
     >>> counter(), counter(), counter()
     (1, 2, 3)
     
    -

    #Decorator

      -
    • A decorator takes a function, adds some functionality and returns it.
    • -
    • It can be any callable, but is usually implemented as a function that returns a closure.
    • -
    @decorator_name
    +

    #Decorator

    A decorator takes a function, adds some functionality and returns it. It can be any callable, but is usually implemented as a function that returns a closure.

    @decorator_name
     def function_that_gets_passed_to_decorator():
         ...
     
    @@ -811,7 +806,7 @@
      -
    • Using only '@debug' to decorate the add() function would not work here, because debug would then receive the add() function as a 'print_result' argument. Decorators can however manually check if the argument they received is a function and act accordingly.
    • +
    • Using only '@debug' to decorate the add() function would not work here, because debug would then receive the add() function as a 'print_result' argument. Decorators can how­ever manually check if the argument they received is a function and act accordingly.

    #Class

    A template for creating user-defined objects.

    class MyClass:
         def __init__(self, a):
    @@ -833,51 +828,54 @@
     (1, '1', 'MyClass(1)')
     
      -
    • Return value of str() should be readable and of repr() unambiguous.
    • -
    • If only repr() is defined, it will also be used for str().
    • -
    • Methods decorated with '@staticmethod' do not receive 'self' nor 'cls' as their first argument.
    • +
    • Methods whose names start and end with two underscores are called special methods. They are executed when object is passed to a built-in function or used as an operand, for example, 'print(a)' calls 'a.__str__()' and 'a + b' calls 'a.__add__(b)'.
    • +
    • Methods decorated with '@staticmethod' receive neither 'self' nor 'cls' argument.
    • +
    • Return value of str() special method should be readable and of repr() unambiguous. If only repr() is defined, it will also be used for str().

    Expressions that call the str() method:

    print(<obj>)
     f'{<obj>}'
     logging.warning(<obj>)
     csv.writer(<file>).writerow([<obj>])
    -raise Exception(<obj>)
     

    Expressions that call the repr() method:

    print/str/repr([<obj>])
     print/str/repr({<obj>: <obj>})
     f'{<obj>!r}'
    -Z = dataclasses.make_dataclass('Z', ['a']); print/str/repr(Z(<obj>))
    ->>> <obj>
    +Z = make_dataclass('Z', ['a']); print/str/repr(Z(<obj>))
     
    -

    Inheritance

    class Person:
    +

    Subclass

      +
    • Inheritance is a mechanism that enables a class to extend some other class (that is, subclass to extend its parent), and by doing so inherit all of its methods and attributes.
    • +
    • Subclass can then add its own methods and attributes or override inherited ones by reusing their names.
    • +
    class Person:
         def __init__(self, name):
             self.name = name
    +    def __repr__(self):
    +        return f'Person({self.name!r})'
    +    def __lt__(self, other):
    +        return self.name < other.name
     
     class Employee(Person):
         def __init__(self, name, staff_num):
             super().__init__(name)
             self.staff_num = staff_num
    +    def __repr__(self):
    +        return f'Employee({self.name!r}, {self.staff_num})'
     
    -

    Multiple inheritance:

    class A: pass
    -class B: pass
    -class C(A, B): pass
    -
    -

    MRO determines the order in which parent classes are traversed when searching for a method or an attribute:

    -
    >>> C.mro()
    -[<class 'C'>, <class 'A'>, <class 'B'>, <class 'object'>]
    -
    +
    >>> people = {Person('Ann'), Employee('Bob', 0)}
    +>>> sorted(people)
    +[Person('Ann'), Employee('Bob', 0)]
    +

    Type Annotations

    • They add type hints to variables, arguments and functions ('def f() -> <type>:').
    • Hints are used by type checkers like mypy, data validation libraries such as Pydantic and lately also by Cython compiler. However, they are not enforced by CPython interpreter.
    from collections import abc
     
    -<name>: <type> [| ...] [= <obj>]                              # `|` since 3.10.
    -<name>: list/set/abc.Iterable/abc.Sequence[<type>] [= <obj>]  # Since 3.9.
    -<name>: dict/tuple[<type>, ...] [= <obj>]                     # Since 3.9.
    +<name>: <type> [| ...] [= <obj>]
    +<name>: list/set/abc.Iterable/abc.Sequence[<type>] [= <obj>]
    +<name>: tuple/dict[<type>, ...] [= <obj>]
     
    @@ -894,12 +892,12 @@
    • Objects can be made sortable with 'order=True' and immutable with 'frozen=True'.
    • For object to be hashable, all attributes must be hashable and 'frozen' must be True.
    • -
    • Function field() is needed because '<attr_name>: list = []' would make a list that is shared among all instances. Its 'default_factory' argument can be any callable.
    • +
    • Function field() is needed because '<attr_name>: list = []' would make a list that is shared among all instances. Its 'default_factory' argument accepts any callable object.
    • For attributes of arbitrary type use 'typing.Any'.
    -
    Point = make_dataclass('Point', ['x', 'y'])
    -Point = make_dataclass('Point', [('x', float), ('y', float)])
    -Point = make_dataclass('Point', [('x', float, 0), ('y', float, 0)])
    +
    P = make_dataclass('P', ['x', 'y'])
    +P = make_dataclass('P', [('x', float), ('y', float)])
    +P = make_dataclass('P', [('x', float, 0), ('y', float, 0)])
     

    Property

    Pythonic way of implementing getters and setters.

    class Person:
         @property
    @@ -917,7 +915,7 @@
     >>> person.name
     'Guido van Rossum'
     
    -

    Slots

    Mechanism that restricts objects to attributes listed in 'slots', reduces their memory footprint.

    class MyClassWithSlots:
    +

    Slots

    Mechanism that restricts objects to attributes listed in 'slots'.

    class MyClassWithSlots:
         __slots__ = ['a']
         def __init__(self):
             self.a = 1
    @@ -929,9 +927,8 @@
     

    #Duck Types

    A duck type is an implicit type that prescribes a set of special methods. Any object that has those methods defined is considered a member of that duck type.

    Comparable

      -
    • If eq() method is not overridden, it returns 'id(self) == id(other)', which is the same as 'self is other'.
    • -
    • That means all user-defined objects compare not equal by default.
    • -
    • Only the left side object has eq() method called, unless it returns NotImplemented, in which case the right object is consulted. False is returned if both return NotImplemented.
    • +
    • If eq() method is not overridden, it returns 'id(self) == id(other)', which is the same as 'self is other'. That means all user-defined objects compare not equal by default (because id() returns object's memory address that is guaranteed to be unique).
    • +
    • Only the left side object has eq() method called, unless it returns NotImplemented, in which case the right object is consulted. Result is False if both return NotImplemented.
    • Ne() automatically works on any object that has eq() defined.
    class MyComparable:
         def __init__(self, a):
    @@ -946,9 +943,8 @@
     
     
     

    Hashable

      -
    • Hashable object needs both hash() and eq() methods and its hash value should never change.
    • -
    • Hashable objects that compare equal must have the same hash value, meaning default hash() that returns 'id(self)' will not do.
    • -
    • That is why Python automatically makes classes unhashable if you only implement eq().
    • +
    • Hashable object needs both hash() and eq() methods and its hash value must not change.
    • +
    • Hashable objects that compare equal must have the same hash value, meaning default hash() that returns 'id(self)' will not do. That is why Python automatically makes classes unhashable if you only implement eq().
    class MyHashable:
         def __init__(self, a):
             self._a = a
    @@ -965,7 +961,7 @@
     
     
     

    Sortable

      -
    • With 'total_ordering' decorator, you only need to provide eq() and one of lt(), gt(), le() or ge() special methods and the rest will be automatically generated.
    • +
    • With 'total_ordering' decorator, you only need to provide eq() and one of lt(), gt(), le() or ge() special methods (used by <, >, <=, >=) and the rest will be automatically generated.
    • Functions sorted() and min() only require lt() method, while max() only requires gt(). However, it is best to define them all so that confusion doesn't arise in other contexts.
    • When two lists, strings or dataclasses are compared, their values get compared in order until a pair of unequal values is found. The comparison of this two values is then returned. The shorter sequence is considered smaller in case of all values being equal.
    • To sort collection of strings in proper alphabetical order pass 'key=locale.strxfrm' to sorted() after running 'locale.setlocale(locale.LC_COLLATE, "en_US.UTF-8")'.
    • @@ -989,7 +985,8 @@

      Iterator

      • Any object that has methods next() and iter() is an iterator.
      • Next() should return next item or raise StopIteration exception.
      • -
      • Iter() should return 'self', i.e. unmodified object on which it was called.
      • +
      • Iter() should return an iterator of remaining items, i.e. 'self'.
      • +
      • Any object that has iter() method can be used in a for loop.
      class Counter:
           def __init__(self):
               self.i = 0
      @@ -1006,13 +1003,13 @@
       (1, 2, 3)
       

      Python has many different iterator objects:

        -
      • Sequence iterators returned by the iter() function, such as list_iterator and set_iterator.
      • +
      • Sequence iterators returned by the iter() function, such as list_iterator, etc.
      • Objects returned by the itertools module, such as count, repeat and cycle.
      • Generators returned by the generator functions and generator expressions.
      • File objects returned by the open() function, etc.

      Callable

        -
      • All functions and classes have a call() method, hence are callable.
      • -
      • Use 'callable(<obj>)' or 'isinstance(<obj>, collections.abc.Callable)' to check if object is callable.
      • +
      • All functions and classes have a call() method that is executed when they are called.
      • +
      • Use 'callable(<obj>)' or 'isinstance(<obj>, collections.abc.Callable)' to check if object is callable. You can also call the object and check if it raised TypeError.
      • When this cheatsheet uses '<function>' as an argument, it means '<callable>'.
      class Counter:
           def __init__(self):
      @@ -1031,8 +1028,8 @@
       

      Context Manager

      • With statements only work on objects that have enter() and exit() special methods.
      • -
      • Enter() should lock the resources and optionally return an object.
      • -
      • Exit() should release the resources.
      • +
      • Enter() should lock the resources and optionally return an object (file, lock, etc.).
      • +
      • Exit() should release the resources (for example close a file, release a lock, etc.).
      • Any exception that happens inside the with block is passed to the exit() method.
      • The exit() method can suppress the exception by returning a true value.
      class MyOpen:
      @@ -1074,7 +1071,7 @@
       

      Collection

      • Only required methods are iter() and len(). Len() should return the number of items.
      • -
      • This cheatsheet actually means '<iterable>' when it uses '<collection>'.
      • +
      • This cheatsheet actually means '<iterable>' when it uses the '<collection>'.
      • I chose not to use the name 'iterable' because it sounds scarier and more vague than 'collection'. The main drawback of this decision is that the reader could think a certain function doesn't accept iterators when it does, since iterators are the only built-in objects that are iterable but are not collections.
      class MyCollection:
           def __init__(self, a):
      @@ -1089,8 +1086,7 @@
       
       
       

      Sequence

        -
      • Only required methods are getitem() and len().
      • -
      • Getitem() should return an item at the passed index or raise IndexError.
      • +
      • Only required methods are getitem() and len(). Getitem() should return the item at the passed index or raise IndexError (it may also support negative indices and/or slices).
      • Iter() and contains() automatically work on any object that has getitem() defined.
      • Reversed() automatically works on any object that has getitem() and len() defined. It returns reversed iterator of object's items.
      class MySequence:
      @@ -1118,8 +1114,8 @@
       
       
       

      ABC Sequence

        -
      • It's a richer interface than the basic sequence.
      • -
      • Extending it generates iter(), contains(), reversed(), index() and count().
      • +
      • It's a richer interface than the basic sequence that also requires just getitem() and len().
      • +
      • Extending it generates iter(), contains(), reversed(), index() and count() special methods.
      • Unlike 'abc.Iterable' and 'abc.Collection', it is not a duck type. That is why 'issubclass(MySequence, abc.Sequence)' would return False even if MySequence had all the methods defined. It however recognizes list, tuple, range, str, bytes, bytearray, array, memoryview and deque, since they are registered as Sequence's virtual subclasses.
      from collections import abc
       
      @@ -1153,8 +1149,8 @@
       
       
      class <enum_name>(Enum):
           <member_name> = auto()              # Increment of the last numeric value or 1.
      -    <member_name> = <value>             # Values don't have to be hashable.
      -    <member_name> = <el_1>, <el_2>      # Values can be collections (this is a tuple).
      +    <member_name> = <value>             # Values don't have to be hashable or unique.
      +    <member_name> = <el_1>, <el_2>      # Values can be collections. This is a tuple.
       
      • Methods receive the member they were called on as the 'self' argument.
      • @@ -1163,16 +1159,16 @@
        <member> = <enum>.<member_name>         # Returns a member. Raises AttributeError.
         <member> = <enum>['<member_name>']      # Returns a member. Raises KeyError.
         <member> = <enum>(<value>)              # Returns a member. Raises ValueError.
        -<str>    = <member>.name                # Returns member's name.
        -<obj>    = <member>.value               # Returns member's value.
        +<str>    = <member>.name                # Returns the member's name.
        +<obj>    = <member>.value               # Returns the member's value.
         
        -
        <list>   = list(<enum>)                 # Returns enum's members.
        -<list>   = [a.name for a in <enum>]     # Returns enum's member names.
        -<list>   = [a.value for a in <enum>]    # Returns enum's member values.
        +
        <list>   = list(<enum>)                 # Returns a list of enum's members.
        +<list>   = <enum>._member_names_        # Returns a list of member names.
        +<list>   = [m.value for m in <enum>]    # Returns a list of member values.
         
        -
        <enum>   = type(<member>)               # Returns member's enum.
        -<iter>   = itertools.cycle(<enum>)      # Returns endless iterator of members.
        -<member> = random.choice(list(<enum>))  # Returns a random member.
        +
        <enum>   = type(<member>)               # Returns an enum. Also <memb>.__class__.
        +<iter>   = itertools.cycle(<enum>)      # Returns an endless iterator of members.
        +<member> = random.choice(list(<enum>))  # Randomly selects one of the members.
         

        Inline

        Cutlery = Enum('Cutlery', 'FORK KNIFE SPOON')
         Cutlery = Enum('Cutlery', ['FORK', 'KNIFE', 'SPOON'])
        @@ -1205,8 +1201,8 @@
         
        • Code inside the 'else' block will only be executed if 'try' block had no exceptions.
        • Code inside the 'finally' block will always be executed (unless a signal is received).
        • -
        • All variables that are initialized in executed blocks are also visible in all subsequent blocks, as well as outside the try statement (only function block delimits scope).
        • -
        • To catch signals use 'signal.signal(signal_number, <func>)'.
        • +
        • All variables that are initialized in executed blocks are also visible in all subsequent blocks, as well as outside the try statement (only the function block delimits scope).
        • +
        • To catch signals use 'signal.signal(signal_number, handler_function)'.

        Catching Exceptions

        except <exception>: ...
         except <exception> as <name>: ...
        @@ -1215,10 +1211,10 @@
         
          -
        • Also catches subclasses of the exception.
        • -
        • Use 'traceback.print_exc()' to print the full error message to stderr.
        • -
        • Use 'print(<name>)' to print just the cause of the exception (its arguments).
        • -
        • Use 'logging.exception(<str>)' to log the passed message, followed by the full error message of the caught exception. For details see Logging.
        • +
        • Also catches subclasses, e.g. 'IndexError' is caught by 'except LookupError:'.
        • +
        • Use 'traceback.print_exc()' to print the full error message to standard error stream.
        • +
        • Use 'print(<name>)' to print just the cause of the exception (that is, its arguments).
        • +
        • Use 'logging.exception(<str>)' to log the passed message, followed by the full error message of the caught exception. For details about setting up the logger see Logging.
        • Use 'sys.exc_info()' to get exception type, object, and traceback of caught exception.

        Raising Exceptions

        raise <exception>
        @@ -1235,31 +1231,31 @@
         exc_type  = <name>.__class__
         filename  = <name>.__traceback__.tb_frame.f_code.co_filename
         func_name = <name>.__traceback__.tb_frame.f_code.co_name
        -line      = linecache.getline(filename, <name>.__traceback__.tb_lineno)
        +line_str  = linecache.getline(filename, <name>.__traceback__.tb_lineno)
         trace_str = ''.join(traceback.format_tb(<name>.__traceback__))
        -error_msg = ''.join(traceback.format_exception(type(<name>), <name>, <name>.__traceback__))
        +error_msg = ''.join(traceback.format_exception(*sys.exc_info()))
         

        Built-in Exceptions

        BaseException
        - ├── SystemExit                   # Raised by the sys.exit() function.
        - ├── KeyboardInterrupt            # Raised when the user hits the interrupt key (ctrl-c).
        + ├── SystemExit                   # Raised by the sys.exit() function (see #Exit for details).
        + ├── KeyboardInterrupt            # Raised when the user hits the interrupt key (control-c).
          └── Exception                    # User-defined exceptions should be derived from this class.
               ├── ArithmeticError         # Base class for arithmetic errors such as ZeroDivisionError.
               ├── AssertionError          # Raised by `assert <exp>` if expression returns false value.
               ├── AttributeError          # Raised when object doesn't have requested attribute/method.
        -      ├── EOFError                # Raised by input() when it hits an end-of-file condition.
        +      ├── EOFError                # Is raised by input() when it hits an end-of-file condition.
               ├── LookupError             # Base class for errors when a collection can't find an item.
        -      │    ├── IndexError         # Raised when a sequence index is out of range.
        -      │    └── KeyError           # Raised when a dictionary key or set element is missing.
        +      │    ├── IndexError         # Raised when index of a sequence (list/str) is out of range.
        +      │    └── KeyError           # Raised when a dictionary key or a set element is missing.
               ├── MemoryError             # Out of memory. May be too late to start deleting variables.
               ├── NameError               # Raised when nonexistent name (variable/func/class) is used.
               │    └── UnboundLocalError  # Raised when local name is used before it's being defined.
        -      ├── OSError                 # Errors such as FileExistsError/TimeoutError (see #Open).
        -      │    └── ConnectionError    # Errors such as BrokenPipeError/ConnectionAbortedError.
        -      ├── RuntimeError            # Raised by errors that don't fall into other categories.
        +      ├── OSError                 # Errors such as FileExistsError, TimeoutError (see #Open).
        +      │    └── ConnectionError    # Errors such as BrokenPipeError and ConnectionAbortedError.
        +      ├── RuntimeError            # Is raised by errors that don't fit into other categories.
               │    ├── NotImplementedEr…  # Can be raised by abstract methods or by unfinished code.
        -      │    └── RecursionError     # Raised when the maximum recursion depth is exceeded.
        -      ├── StopIteration           # Raised when an empty iterator is passed to next().
        +      │    └── RecursionError     # Raised if max recursion depth is exceeded (3k by default).
        +      ├── StopIteration           # Raised when exhausted (empty) iterator is passed to next().
               ├── TypeError               # When an argument of the wrong type is passed to function.
               └── ValueError              # When argument has the right type but inappropriate value.
         
        @@ -1274,8 +1270,8 @@ ┗━━━━━━━━━━━┷━━━━━━━━━━━━┷━━━━━━━━━━━━┷━━━━━━━━━━━━┛
        -

        Useful built-in exceptions:

        raise TypeError('Argument is of the wrong type!')
        -raise ValueError('Argument has the right type but an inappropriate value!')
        +

        Useful built-in exceptions:

        raise TypeError('Passed argument is of the wrong type!')
        +raise ValueError('Argument has the right type but its value is off!')
         raise RuntimeError('I am too lazy to define my own exception!')
         
        @@ -1284,9 +1280,9 @@

        #Exit

        Exits the interpreter by raising SystemExit exception.

        import sys
        -sys.exit()                        # Exits with exit code 0 (success).
        -sys.exit(<int>)                   # Exits with the passed exit code.
        -sys.exit(<obj>)                   # Prints to stderr and exits with 1.
        +sys.exit()                     # Exits with exit code 0 (success).
        +sys.exit(<int>)                # Exits with the passed exit code.
        +sys.exit(<obj>)                # Prints to stderr and exits with 1.
         
        @@ -1294,7 +1290,7 @@
        -
      • Use 'file=sys.stderr' for messages about errors.
      • +
      • Use 'file=sys.stderr' for messages about errors so they can be processed separately.
      • Stdout and stderr streams hold output in a buffer until they receive a string containing '\n' or '\r', buffer reaches 4096 characters, 'flush=True' is used, or program exits.

      Pretty Print

      from pprint import pprint
      @@ -1311,7 +1307,7 @@
       
      • Reads a line from the user input or pipe if present (trailing newline gets stripped).
      • If argument is passed, it gets printed to the standard output before input is read.
      • -
      • EOFError is raised if user hits EOF (ctrl-d/ctrl-z⏎) or input stream gets exhausted.
      • +
      • EOFError is raised if user hits EOF (ctrl-d/ctrl-z⏎) or if stream is already exhausted.

      #Command Line Arguments

      import sys
       scripts_path = sys.argv[0]
      @@ -1319,27 +1315,27 @@
       

      Argument Parser

      from argparse import ArgumentParser, FileType
      -p = ArgumentParser(description=<str>)                             # Returns a parser.
      +p = ArgumentParser(description=<str>)                             # Returns a parser object.
       p.add_argument('-<short_name>', '--<name>', action='store_true')  # Flag (defaults to False).
       p.add_argument('-<short_name>', '--<name>', type=<type>)          # Option (defaults to None).
       p.add_argument('<name>', type=<type>, nargs=1)                    # Mandatory first argument.
       p.add_argument('<name>', type=<type>, nargs='+')                  # Mandatory remaining args.
      -p.add_argument('<name>', type=<type>, nargs='?/*')                # Optional argument/s.
      +p.add_argument('<name>', type=<type>, nargs='?')                  # Optional argument. Also *.
       args  = p.parse_args()                                            # Exits on parsing error.
       <obj> = args.<name>                                               # Returns `<type>(<arg>)`.
       
      • Use 'help=<str>' to set argument description that will be displayed in help message.
      • -
      • Use 'default=<obj>' to set option's or optional argument's default value.
      • -
      • Use 'type=FileType(<mode>)' for files. Accepts 'encoding', but 'newline' is None.
      • +
      • Use 'default=<obj>' to override None as option's or optional argument's default value.
      • +
      • Use 'type=FileType(<mode>)' for files. It accepts 'encoding', but 'newline' is None.

      #Open

      Opens a file and returns the corresponding file object.

      <file> = open(<path>, mode='r', encoding=None, newline=None)
       
        -
      • 'encoding=None' means that the default encoding is used, which is platform dependent. Best practice is to use 'encoding="utf-8"' whenever possible.
      • +
      • 'encoding=None' means that the default encoding is used, which is platform dependent. Best practice is to use 'encoding="utf-8"' until it becomes the default (Python 3.15).
      • 'newline=None' means all different end of line combinations are converted to '\n' on read, while on write all '\n' characters are converted to system's default line separator.
      • 'newline=""' means no conversions take place, but input is still broken into chunks by readline() and readlines() on every '\n', '\r' and '\r\n'.
      @@ -1357,9 +1353,9 @@
    • 'FileExistsError' can be raised when writing with 'x'.
    • 'IsADirectoryError' and 'PermissionError' can be raised by any.
    • 'OSError' is the parent class of all listed exceptions.
    • -

    File Object

    <file>.seek(0)                      # Moves to the start of the file.
    +

    File Object

    <file>.seek(0)                      # Moves current position to the start of file.
     <file>.seek(offset)                 # Moves 'offset' chars/bytes from the start.
    -<file>.seek(0, 2)                   # Moves to the end of the file.
    +<file>.seek(0, 2)                   # Moves current position to the end of file.
     <bin_file>.seek(±offset, origin)    # Origin: 0 start, 1 current position, 2 end.
     
    @@ -1367,12 +1363,12 @@ -
    <str/bytes> = <file>.read(size=-1)  # Reads 'size' chars/bytes or until EOF.
    +
    <str/bytes> = <file>.read(size=-1)  # Reads 'size' chars/bytes or until the EOF.
     <str/bytes> = <file>.readline()     # Returns a line or empty string/bytes on EOF.
    -<list>      = <file>.readlines()    # Returns a list of remaining lines.
    -<str/bytes> = next(<file>)          # Returns a line using buffer. Do not mix.
    +<list>      = <file>.readlines()    # Returns remaining lines. Also list(<file>).
    +<str/bytes> = next(<file>)          # Returns a line using a read-ahead buffer.
     
    -
    <file>.write(<str/bytes>)           # Writes a string or bytes object.
    +
    <file>.write(<str/bytes>)           # Writes a str or bytes object to write buffer.
     <file>.writelines(<collection>)     # Writes a coll. of strings or bytes objects.
     <file>.flush()                      # Flushes write buffer. Runs every 4096/8192 B.
     <file>.close()                      # Closes the file after flushing write buffer.
    @@ -1395,27 +1391,27 @@
     
    <str>  = os.getcwd()                # Returns working dir. Starts as shell's $PWD.
    -<str>  = os.path.join(<path>, ...)  # Joins two or more pathname components.
    +<str>  = os.path.join(<path>, ...)  # Uses os.sep to join strings or Path objects.
     <str>  = os.path.realpath(<path>)   # Resolves symlinks and calls path.abspath().
     
    -
    <str>  = os.path.basename(<path>)   # Returns final component of the path.
    -<str>  = os.path.dirname(<path>)    # Returns path without the final component.
    +
    <str>  = os.path.basename(<path>)   # Returns path's final component, i.e. file/dir.
    +<str>  = os.path.dirname(<path>)    # Returns path with its final component removed.
     <tup.> = os.path.splitext(<path>)   # Splits on last period of the final component.
     
    -
    <list> = os.listdir(path='.')       # Returns filenames located at the path.
    +
    <list> = os.listdir(path='.')       # Returns all filenames located at the path.
     <list> = glob.glob('<pattern>')     # Returns paths matching the wildcard pattern.
     
    -
    <bool> = os.path.exists(<path>)     # Or: <Path>.exists()
    -<bool> = os.path.isfile(<path>)     # Or: <DirEntry/Path>.is_file()
    -<bool> = os.path.isdir(<path>)      # Or: <DirEntry/Path>.is_dir()
    +
    <bool> = os.path.exists(<path>)     # Checks if path exists. Also <Path>.exists().
    +<bool> = os.path.isfile(<path>)     # Also <Path>.is_file() and <DirEntry>.is_file().
    +<bool> = os.path.isdir(<path>)      # Also <Path>.is_dir() and <DirEntry>.is_dir().
     
    -
    <stat> = os.stat(<path>)            # Or: <DirEntry/Path>.stat()
    -<num>  = <stat>.st_mtime/st_size/…  # Modification time, size in bytes, etc.
    +
    <stat> = os.stat(<path>)            # File's status. Also <Path/DirEntry>.stat().
    +<num>  = <stat>.st_mtime/st_size/…  # Returns modification time, size in bytes, etc.
     

    DirEntry

    Unlike listdir(), scandir() returns DirEntry objects that cache isfile, isdir, and on Windows also stat information, thus significantly increasing the performance of code that requires it.

    <iter> = os.scandir(path='.')       # Returns DirEntry objects located at the path.
    -<str>  = <DirEntry>.path            # Returns the whole path as a string.
    -<str>  = <DirEntry>.name            # Returns final component as a string.
    -<file> = open(<DirEntry>)           # Opens the file and returns a file object.
    +<str>  = <DirEntry>.path            # Is absolute if 'path' argument was absolute.
    +<str>  = <DirEntry>.name            # Returns path's final component as a string.
    +<file> = open(<DirEntry>)           # Opens the file and returns its file object.
     
    @@ -1424,52 +1420,52 @@ <Path> = <Path>.resolve() # Returns absolute path with resolved symlinks.
    -
    <Path> = Path()                     # Returns relative CWD. Also Path('.').
    +
    <Path> = Path()                     # Returns current working dir. Also Path('.').
     <Path> = Path.cwd()                 # Returns absolute CWD. Also Path().resolve().
     <Path> = Path.home()                # Returns user's home directory (absolute).
     <Path> = Path(__file__).resolve()   # Returns module's path if CWD wasn't changed.
     
    <Path> = <Path>.parent              # Returns Path without the final component.
    -<str>  = <Path>.name                # Returns final component as a string.
    -<str>  = <Path>.stem                # Returns final component w/o last extension.
    -<str>  = <Path>.suffix              # Returns last extension prepended with a dot.
    +<str>  = <Path>.name                # Returns path's final component as a string.
    +<str>  = <Path>.suffix              # Returns name's last extension, e.g. '.gz'.
    +<str>  = <Path>.stem                # Returns name without the last extension.
     <tup.> = <Path>.parts               # Returns all path's components as strings.
     
    <iter> = <Path>.iterdir()           # Returns directory contents as Path objects.
     <iter> = <Path>.glob('<pattern>')   # Returns Paths matching the wildcard pattern.
     
    -
    <str>  = str(<Path>)                # Returns path as a string.
    -<file> = open(<Path>)               # Also <Path>.read/write_text/bytes(<args>).
    +
    <str>  = str(<Path>)                # Returns path as string. Also <Path>.as_uri().
    +<file> = open(<Path>)               # Also <Path>.read_text/write_bytes(<args>).
     

    #OS Commands

    import os, shutil, subprocess
     
    -
    os.chdir(<path>)                    # Changes the current working directory.
    -os.mkdir(<path>, mode=0o777)        # Creates a directory. Permissions are in octal.
    -os.makedirs(<path>, mode=0o777)     # Creates all path's dirs. Also `exist_ok=False`.
    +
    os.chdir(<path>)                 # Changes the current working directory (CWD).
    +os.mkdir(<path>, mode=0o777)     # Creates a directory. Permissions are in octal.
    +os.makedirs(<path>, mode=0o777)  # Creates all path's dirs. Also `exist_ok=False`.
     
    -
    shutil.copy(from, to)               # Copies the file. 'to' can exist or be a dir.
    -shutil.copy2(from, to)              # Also copies creation and modification time.
    -shutil.copytree(from, to)           # Copies the directory. 'to' must not exist.
    +
    shutil.copy(from, to)            # Copies the file. 'to' can exist or be a dir.
    +shutil.copy2(from, to)           # Also copies creation and modification time.
    +shutil.copytree(from, to)        # Copies the directory. 'to' must not exist.
     
    -
    os.rename(from, to)                 # Renames/moves the file or directory.
    -os.replace(from, to)                # Same, but overwrites file 'to' even on Windows.
    -shutil.move(from, to)               # Rename() that moves into 'to' if it's a dir.
    +
    os.rename(from, to)              # Renames or moves the file or directory 'from'.
    +os.replace(from, to)             # Same, but overwrites file 'to' even on Windows.
    +shutil.move(from, to)            # Rename() that moves into 'to' if it's a dir.
     
    -
    os.remove(<path>)                   # Deletes the file.
    -os.rmdir(<path>)                    # Deletes the empty directory.
    -shutil.rmtree(<path>)               # Deletes the directory.
    +
    os.remove(<path>)                # Deletes the file. Or `pip3 install send2trash`.
    +os.rmdir(<path>)                 # Deletes the empty directory or raises OSError.
    +shutil.rmtree(<path>)            # Deletes the directory and all of its contents.
     
      -
    • Paths can be either strings, Paths, or DirEntry objects.
    • -
    • Functions report OS related errors by raising either OSError or one of its subclasses.
    • +
    • Paths can be either strings, Path objects, or DirEntry objects.
    • +
    • Functions report OS related errors by raising OSError or one of its subclasses.
    -

    Shell Commands

    <pipe> = os.popen('<commands>')     # Executes commands in sh/cmd. Returns combined stdout.
    -<str>  = <pipe>.read(size=-1)       # Reads 'size' chars or until EOF. Also readline/s().
    -<int>  = <pipe>.close()             # Returns None if last command exited with returncode 0.
    +

    Shell Commands

    <pipe> = os.popen('<commands>')  # Executes commands in sh/cmd. Returns combined stdout.
    +<str>  = <pipe>.read(size=-1)    # Reads 'size' chars or until EOF. Also readline/s().
    +<int>  = <pipe>.close()          # Returns None if last command exited with returncode 0.
     
    -

    Sends '1 + 1' to the basic calculator and captures its output:

    >>> subprocess.run('bc', input='1 + 1\n', capture_output=True, text=True)
    +

    Sends "1 + 1" to the basic calculator and captures its output:

    >>> subprocess.run('bc', input='1 + 1\n', capture_output=True, text=True)
     CompletedProcess(args='bc', returncode=0, stdout='2\n', stderr='')
     
    @@ -1482,8 +1478,8 @@

    #JSON

    Text file format for storing collections of strings and numbers.

    import json
    -<str>  = json.dumps(<list/dict>)    # Converts collection to JSON string.
    -<coll> = json.loads(<str>)          # Converts JSON string to collection.
    +<str>  = json.dumps(<list/dict>)  # Converts collection to JSON string.
    +<coll> = json.loads(<str>)        # Converts JSON string to collection.
     
    @@ -1498,8 +1494,8 @@

    #Pickle

    Binary file format for storing Python objects.

    import pickle
    -<bytes>  = pickle.dumps(<object>)   # Converts object to bytes object.
    -<object> = pickle.loads(<bytes>)    # Converts bytes object to object.
    +<bytes>  = pickle.dumps(<object>)  # Converts object to bytes object.
    +<object> = pickle.loads(<bytes>)   # Converts bytes object to object.
     
    @@ -1517,33 +1513,34 @@
    -

    Read

    <reader> = csv.reader(<file>)       # Also: `dialect='excel', delimiter=','`.
    -<list>   = next(<reader>)           # Returns next row as a list of strings.
    -<list>   = list(<reader>)           # Returns a list of remaining rows.
    -
    - +
    <file>   = open(<path>, newline='')       # Opens the CSV (text) file for reading.
    +<reader> = csv.reader(<file>)             # Also: `dialect='excel', delimiter=','`.
    +<list>   = next(<reader>)                 # Returns next row as a list of strings.
    +<list>   = list(<reader>)                 # Returns a list of all remaining rows.
    +
      -
    • File must be opened with a 'newline=""' argument, or newlines embedded inside quoted fields will not be interpreted correctly!
    • -
    • To print the spreadsheet to the console use Tabulate library.
    • -
    • For XML and binary Excel files (xlsx, xlsm and xlsb) use Pandas library.
    • -
    • Reader accepts any iterator of strings, not just files.
    • +
    • Without the 'newline=""' argument, every '\r\n' sequence that is embedded inside a quoted field will get converted to '\n'! For details about newline argument see Open.
    • +
    • To print the spreadsheet to the console use either Tabulate or PrettyTable library.
    • +
    • For XML and binary Excel files (extensions xlsx, xlsm and xlsb) use Pandas library.
    • +
    • Reader accepts any iterator (or collection) of strings, not just text files.
    -

    Write

    <writer> = csv.writer(<file>)       # Also: `dialect='excel', delimiter=','`.
    -<writer>.writerow(<collection>)     # Encodes objects using `str(<el>)`.
    -<writer>.writerows(<coll_of_coll>)  # Appends multiple rows.
    +

    Write

    <file>   = open(<path>, 'w', newline='')  # Opens the CSV (text) file for writing.
    +<writer> = csv.writer(<file>)             # Also: `dialect='excel', delimiter=','`.
    +<writer>.writerow(<collection>)           # Encodes each object using `str(<el>)`.
    +<writer>.writerows(<coll_of_coll>)        # Appends multiple rows to opened file.
     
      -
    • File must be opened with a 'newline=""' argument, or '\r' will be added in front of every '\n' on platforms that use '\r\n' line endings!
    • +
    • If file is opened without the 'newline=""' argument, '\r' will be added in front of every '\n' on platforms that use '\r\n' line endings (i.e., newlines may get doubled on Windows)!
    • Open existing file with 'mode="a"' to append to it or 'mode="w"' to overwrite it.

    Parameters

    • 'dialect' - Master parameter that sets the default values. String or a 'csv.Dialect' object.
    • -
    • 'delimiter' - A one-character string used to separate fields.
    • -
    • 'lineterminator' - How writer terminates rows. Reader looks for '\n', '\r' and '\r\n'.
    • +
    • 'delimiter' - A one-character string that separates fields (comma, tab, semicolon, etc.).
    • +
    • 'lineterminator' - Sets how writer terminates rows. Reader looks for '\n', '\r' and '\r\n'.
    • 'quotechar' - Character for quoting fields containing delimiters, quotechars, '\n' or '\r'.
    • -
    • 'escapechar' - Character for escaping quotechars.
    • -
    • 'doublequote' - Whether quotechars inside fields are/get doubled or escaped.
    • +
    • 'escapechar' - Character for escaping quotechars (not needed if doublequote is True).
    • +
    • 'doublequote' - Whether quotechars inside fields are/get doubled instead of escaped.
    • 'quoting' - 0: As necessary, 1: All, 2: All but numbers which are read as floats, 3: None.
    • 'skipinitialspace' - Is space character at the start of the field stripped by the reader.

    Dialects

    ┏━━━━━━━━━━━━━━━━━━┯━━━━━━━━━━━━━━┯━━━━━━━━━━━━━━┯━━━━━━━━━━━━━━┓
    @@ -1578,12 +1575,12 @@
     
    -

    Read

    <cursor> = <conn>.execute('<query>')           # Can raise a subclass of sqlite3.Error.
    -<tuple>  = <cursor>.fetchone()                 # Returns next row. Also next(<cursor>).
    +

    Read

    <cursor> = <conn>.execute('<query>')           # Can raise a subclass of the sqlite3.Error.
    +<tuple>  = <cursor>.fetchone()                 # Returns the next row. Also next(<cursor>).
     <list>   = <cursor>.fetchall()                 # Returns remaining rows. Also list(<cursor>).
     
    -

    Write

    <conn>.execute('<query>')                      # Can raise a subclass of sqlite3.Error.
    +

    Write

    <conn>.execute('<query>')                      # Can raise a subclass of the sqlite3.Error.
     <conn>.commit()                                # Saves all changes since the last commit.
     <conn>.rollback()                              # Discards all changes since the last commit.
     
    @@ -1592,19 +1589,19 @@ <conn>.execute('<query>') # depending on whether any exception occurred.
    -

    Placeholders

    <conn>.execute('<query>', <list/tuple>)        # Replaces '?'s in query with values.
    -<conn>.execute('<query>', <dict/namedtuple>)   # Replaces ':<key>'s with values.
    -<conn>.executemany('<query>', <coll_of_coll>)  # Runs execute() multiple times.
    +

    Placeholders

    <conn>.execute('<query>', <list/tuple>)        # Replaces every question mark with an item.
    +<conn>.execute('<query>', <dict/namedtuple>)   # Replaces every :<key> with a matching value.
    +<conn>.executemany('<query>', <coll_of_coll>)  # Executes the query once for each collection.
     
    • Passed values can be of type str, int, float, bytes, None, or bool (stored as 1 or 0).
    • +
    • SQLite does not restrict columns to any type unless table is declared as strict.

    Example

    Values are not actually saved in this example because 'conn.commit()' is omitted!

    >>> conn = sqlite3.connect('test.db')
    ->>> conn.execute('CREATE TABLE person (person_id INTEGER PRIMARY KEY, name, height)')
    ->>> conn.execute('INSERT INTO person VALUES (NULL, ?, ?)', ('Jean-Luc', 187)).lastrowid
    -1
    ->>> conn.execute('SELECT * FROM person').fetchall()
    +>>> conn.execute('CREATE TABLE person (name TEXT, height INTEGER) STRICT')
    +>>> conn.execute('INSERT INTO person VALUES (?, ?)', ('Jean-Luc', 187))
    +>>> conn.execute('SELECT rowid, * FROM person').fetchall()
     [(1, 'Jean-Luc', 187)]
     
    @@ -1613,8 +1610,8 @@ from sqlalchemy import create_engine, text <engine> = create_engine('<url>') # Url: 'dialect://user:password@host/dbname'. <conn> = <engine>.connect() # Creates a connection. Also <conn>.close(). -<cursor> = <conn>.execute(text('<query>'), …) # `<dict>`. Replaces ':<key>'s with values. -with <conn>.begin(): ... # Exits the block with commit or rollback. +<cursor> = <conn>.execute(text('<query>'), …) # `<dict>`. Replaces every :<key> with value. +with <conn>.begin(): ... # Exits the block with a commit or rollback.
    @@ -1627,20 +1624,20 @@ ┃ oracle+oracledb │ oracledb │ www.pypi.org/project/oracledb ┃ ┗━━━━━━━━━━━━━━━━━┷━━━━━━━━━━━━━━┷━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
    -

    #Bytes

    A bytes object is an immutable sequence of single bytes. Mutable version is called bytearray.

    <bytes> = b'<str>'                       # Only accepts ASCII characters and \x00-\xff.
    -<int>   = <bytes>[index]                 # Returns an int in range from 0 to 255.
    -<bytes> = <bytes>[<slice>]               # Returns bytes even if it has only one element.
    +

    #Bytes

    A bytes object is an immutable sequence of single bytes. Mutable version is called bytearray.

    <bytes> = b'<str>'                       # Only accepts ASCII chars and [\x00-\xff].
    +<int>   = <bytes>[index]                 # Returns an integer in range from 0 to 255.
    +<bytes> = <bytes>[<slice>]               # Returns bytes even if it has one element.
     <bytes> = <bytes>.join(<coll_of_bytes>)  # Joins elements using bytes as a separator.
     
    -

    Encode

    <bytes> = bytes(<coll_of_ints>)          # Ints must be in range from 0 to 255.
    +

    Encode

    <bytes> = bytes(<coll_of_ints>)          # Integers must be in range from 0 to 255.
     <bytes> = bytes(<str>, 'utf-8')          # Encodes the string. Also <str>.encode().
     <bytes> = bytes.fromhex('<hex>')         # Hex pairs can be separated by whitespaces.
     <bytes> = <int>.to_bytes(n_bytes, …)     # `byteorder='big/little', signed=False`.
     
    -

    Decode

    <list>  = list(<bytes>)                  # Returns ints in range from 0 to 255.
    +

    Decode

    <list>  = list(<bytes>)                  # Returns integers in range from 0 to 255.
     <str>   = str(<bytes>, 'utf-8')          # Returns a string. Also <bytes>.decode().
     <str>   = <bytes>.hex()                  # Returns hex pairs. Accepts `sep=<str>`.
     <int>   = int.from_bytes(<bytes>, …)     # `byteorder='big/little', signed=False`.
    @@ -1661,8 +1658,8 @@
     
  • System’s type sizes, byte order, and alignment rules are used by default.
  • from struct import pack, unpack
     
    -<bytes> = pack('<format>', <el_1> [, ...])  # Packs objects according to format string.
    -<tuple> = unpack('<format>', <bytes>)       # Use iter_unpack() to get iterator of tuples.
    +<bytes> = pack('<format>', <el_1> [, ...])  # Packs numbers according to format string.
    +<tuple> = unpack('<format>', <bytes>)       # Use iter_unpack() to get iter of tuples.
     
    @@ -1696,56 +1693,56 @@

    Format

    #Array

    List that can only hold numbers of a predefined type. Available types and their minimum sizes in bytes are listed above. Type sizes and byte order are always determined by the system, however bytes of each element can be reversed with byteswap() method.

    from array import array
    +

    #Array

    List that can only hold numbers of a predefined type. Available types and their minimum sizes in bytes are listed above. Type sizes and byte order are always determined by the sys­tem, however bytes of each element can be reversed (by calling the byteswap() method).

    from array import array
     
    -
    <array> = array('<typecode>', <coll_of_nums>)  # Creates array from collection of numbers.
    -<array> = array('<typecode>', <bytes>)         # Writes passed bytes to array's memory.
    -<array> = array('<typecode>', <array>)         # Treats passed array as a sequence of numbers.
    -<array>.fromfile(<file>, n_items)              # Appends file's contents to array's memory.
    +
    <array> = array('<typecode>' [, <coll>])  # Creates array. Accepts collection of numbers.
    +<array> = array('<typecode>', <bytes>)    # Copies passed bytes into the array's memory.
    +<array> = array('<typecode>', <array>)    # Treats passed array as a sequence of numbers.
    +<array>.fromfile(<file>, n_items)         # Appends file contents to the array's memory.
     
    -
    <bytes> = bytes(<array>)                       # Returns a copy of array's memory.
    -<file>.write(<array>)                          # Writes array's memory to the binary file.
    +
    <bytes> = bytes(<array>)                  # Returns a copy of array's memory as bytes.
    +<file>.write(<array>)                     # Appends array's memory to the binary file.
     
    -

    #Memory View

    A sequence object that points to the memory of another bytes-like object. Each element can reference a single or multiple consecutive bytes, depending on format. Order and number of elements can be changed with slicing.

    <mview> = memoryview(<bytes/bytearray/array>)  # Immutable if bytes is passed, else mutable.
    -<obj>   = <mview>[index]                       # Returns int or float. Bytes if format is 'c'.
    -<mview> = <mview>[<slice>]                     # Returns memoryview with rearranged elements.
    -<mview> = <mview>.cast('<typecode>')           # Only works between B/b/c and other types.
    -<mview>.release()                              # Releases memory buffer of the base object.
    +

    #Memory View

    A sequence object that points to the memory of another bytes-like object. Each element can reference a single or multiple consecutive bytes, depending on format. Order and number of elements can be changed with slicing.

    <mview> = memoryview(<bytes/array>)       # Returns mutable memoryview if array is passed.
    +<obj>   = <mview>[index]                  # Returns an int/float. Bytes if format is 'c'.
    +<mview> = <mview>[<slice>]                # Returns memoryview with rearranged elements.
    +<mview> = <mview>.cast('<typecode>')      # Only works between B/b/c and the other types.
    +<mview>.release()                         # Releases the memory buffer of the base object.
     
    -
    <bytes> = bytes(<mview>)                       # Returns a new bytes object. Also bytearray().
    -<bytes> = <bytes>.join(<coll_of_mviews>)       # Joins memoryviews using bytes as a separator.
    -<array> = array('<typecode>', <mview>)         # Treats memoryview as a sequence of numbers.
    -<file>.write(<mview>)                          # Writes `bytes(<mview>)` to the binary file.
    +
    <bytes> = bytes(<mview>)                  # Returns a new bytes object. Also bytearray().
    +<bytes> = <bytes>.join(<coll_of_mviews>)  # Joins memoryviews using bytes as a separator.
    +<array> = array('<typecode>', <mview>)    # Treats passed mview as a sequence of numbers.
    +<file>.write(<mview>)                     # Appends `bytes(<mview>)` to the binary file.
     
    -
    <list>  = list(<mview>)                        # Returns a list of ints, floats, or bytes.
    -<str>   = str(<mview>, 'utf-8')                # Treats memoryview as a bytes object.
    -<str>   = <mview>.hex()                        # Returns hex pairs. Accepts `sep=<str>`.
    +
    <list>  = list(<mview>)                   # Returns list of ints, floats or bytes objects.
    +<str>   = str(<mview>, 'utf-8')           # Treats passed memoryview as `bytes(<mview>)`.
    +<str>   = <mview>.hex()                   # Returns hexadecimal pairs. Also `sep=<str>`.
     

    #Deque

    List with efficient appends and pops from either side.

    from collections import deque
     
    -
    <deque> = deque(<collection>)                  # Use `maxlen=<int>` to set size limit.
    -<deque>.appendleft(<el>)                       # Opposite element is dropped if full.
    -<deque>.extendleft(<collection>)               # Passed collection gets reversed.
    -<deque>.rotate(n=1)                            # Last element becomes first.
    -<el> = <deque>.popleft()                       # Raises IndexError if deque is empty.
    +
    <deque> = deque(<collection>)     # Use `maxlen=<int>` to set size limit.
    +<deque>.appendleft(<el>)          # Opposite element is dropped if full.
    +<deque>.extendleft(<collection>)  # Prepends reversed coll. to the deque.
    +<deque>.rotate(n=1)               # Last element becomes the first one.
    +<el> = <deque>.popleft()          # Raises IndexError if deque is empty.
     
    -

    #Operator

    Module of functions that provide the functionality of operators. Functions are ordered and grouped by operator precedence, from least to most binding. Logical and arithmetic operators in lines 1, 3 and 5 are also ordered by precedence within their own group.

    import operator as op
    +

    #Operator

    Module of functions that provide the functionality of operators. Functions are grouped by operator precedence, from least to most binding. Functions and operators in first, third and fifth line are also ordered by precedence within a group.

    import operator as op
     
    <bool> = op.not_(<obj>)                                        # or, and, not (or/and missing)
     <bool> = op.eq/ne/lt/ge/is_/is_not/contains(<obj>, <obj>)      # ==, !=, <, >=, is, is not, in
    -<obj>  = op.or_/xor/and_(<int/set>, <int/set>)                 # |, ^, &
    -<int>  = op.lshift/rshift(<int>, <int>)                        # <<, >>
    -<obj>  = op.add/sub/mul/truediv/floordiv/mod(<obj>, <obj>)     # +, -, *, /, //, %
    -<num>  = op.neg/invert(<num>)                                  # -, ~
    -<num>  = op.pow(<num>, <num>)                                  # **
    +<obj>  = op.or_/xor/and_(<int/set>, <int/set>)                 # |, ^, & (sorted by precedence)
    +<int>  = op.lshift/rshift(<int>, <int>)                        # <<, >> (i.e. <int> << n_bits)
    +<obj>  = op.add/sub/mul/truediv/floordiv/mod(<obj>, <obj>)     # +, -, *, /, //, % (two groups)
    +<num>  = op.neg/invert(<num>)                                  # -, ~ (negate and bitwise not)
    +<num>  = op.pow(<num>, <num>)                                  # ** (pow() accepts 3 arguments)
     <func> = op.itemgetter/attrgetter/methodcaller(<obj> [, ...])  # [index/key], .name, .name([…])
     
    elementwise_sum  = map(op.add, list_a, list_b)
    @@ -1755,33 +1752,32 @@ 

    Format

    'x < y < z' gets converted to '(x < y) and (y < z)'. +
  • Comparisons can be chained: 'x < y < z' gets converted to '(x < y) and (y < z)'.
  • -

    #Match Statement

    Executes the first block with matching pattern. Added in Python 3.10.

    match <object/expression>:
    +

    #Match Statement

    Executes the first block with matching pattern.

    match <object/expression>:
         case <pattern> [if <condition>]:
             <code>
         ...
     
    -

    Patterns

    <value_pattern> = 1/'abc'/True/None/math.pi        # Matches the literal or a dotted name.
    +

    Patterns

    <value_pattern> = 1/'abc'/True/None/math.pi        # Matches the literal or attribute's value.
     <class_pattern> = <type>()                         # Matches any object of that type (or ABC).
     <wildcard_patt> = _                                # Matches any object. Useful in last case.
     <capture_patt>  = <name>                           # Matches any object and binds it to name.
     <as_pattern>    = <pattern> as <name>              # Binds match to name. Also <type>(<name>).
    -<or_pattern>    = <pattern> | <pattern> [| ...]    # Matches any of the patterns.
    -<sequence_patt> = [<pattern>, ...]                 # Matches sequence with matching items.
    -<mapping_patt>  = {<value_pattern>: <patt>, ...}   # Matches dictionary with matching items.
    -<class_pattern> = <type>(<attr_name>=<patt>, ...)  # Matches object with matching attributes.
    +<or_pattern>    = <pattern> | <pattern> [| ...]    # Matches if any of listed patterns match.
    +<sequence_patt> = [<pattern>, ...]                 # Matches a sequence. All items must match.
    +<mapping_patt>  = {<value_pattern>: <patt>, ...}   # Matches a dict if it has matching items.
    +<class_pattern> = <type>(<attr_name>=<patt>, ...)  # Matches object that has matching attrbs.
     
      -
    • Sequence pattern can also be written as a tuple.
    • +
    • Sequence pattern can also be written as a tuple, either with or without the brackets.
    • Use '*<name>' and '**<name>' in sequence/mapping patterns to bind remaining items.
    • Sequence pattern must match all items of the collection, while mapping pattern does not.
    • -
    • Patterns can be surrounded with brackets to override precedence ('|' > 'as' > ',').
    • -
    • Built-in types allow a single positional pattern that is matched against the entire object.
    • -
    • All names that are bound in the matching case, as well as variables initialized in its block, are visible after the match statement.
    • +
    • Patterns can be surrounded with brackets to override their precedence: '|' > 'as' > ','. For example, '[1, 2]' is matched by the 'case 1|2, 2|3 as x if x == 2:' block.
    • +
    • All names that are bound in the matching case, as well as variables initialized in its body, are visible after the match statement (only function block delimits scope).

    Example

    >>> from pathlib import Path
     >>> match Path('/home/gto/python-cheatsheet/README.md'):
    @@ -1796,25 +1792,25 @@ 

    Format

    log.basicConfig(filename=<path>, level='DEBUG') # Configures the root logger (see Setup). -log.debug/info/warning/error/critical(<str>) # Sends message to the root logger. -<Logger> = log.getLogger(__name__) # Returns logger named after the module. -<Logger>.<level>(<str>) # Sends message to the logger. +log.debug/info/warning/error/critical(<str>) # Sends passed message to the root logger. +<Logger> = log.getLogger(__name__) # Returns a logger named after the module. +<Logger>.<level>(<str>) # Sends the message. Same levels as above. <Logger>.exception(<str>) # Error() that appends caught exception.

    Setup

    log.basicConfig(
    -    filename=None,                                # Logs to stderr or appends to file.
    +    filename=None,                                # Prints to stderr or appends to file.
         format='%(levelname)s:%(name)s:%(message)s',  # Add '%(asctime)s' for local datetime.
    -    level=log.WARNING,                            # Drops messages with lower priority.
    +    level=log.WARNING,                            # Drops messages with a lower priority.
         handlers=[log.StreamHandler(sys.stderr)]      # Uses FileHandler if filename is set.
     )
     
    -
    <Formatter> = log.Formatter('<format>')           # Creates a Formatter.
    -<Handler> = log.FileHandler(<path>, mode='a')     # Creates a Handler. Also `encoding=None`.
    -<Handler>.setFormatter(<Formatter>)               # Adds Formatter to the Handler.
    -<Handler>.setLevel(<int/str>)                     # Processes all messages by default.
    -<Logger>.addHandler(<Handler>)                    # Adds Handler to the Logger.
    -<Logger>.setLevel(<int/str>)                      # What is sent to its/ancestors' handlers.
    +
    <Formatter> = log.Formatter('<format>')           # Formats messages according to format.
    +<Handler> = log.FileHandler(<path>, mode='a')     # Appends to file. Also `encoding=None`.
    +<Handler>.setFormatter(<Formatter>)               # Only outputs bare messages by default.
    +<Handler>.setLevel(<int/str>)                     # Prints/saves every message by default.
    +<Logger>.addHandler(<Handler>)                    # Logger can have more than one handler.
    +<Logger>.setLevel(<int/str>)                      # What's sent to its/ancestors' handlers.
     <Logger>.propagate = <bool>                       # Cuts off ancestors' handlers if False.
     
      @@ -1838,18 +1834,18 @@

      Format

      #Introspection

      <list> = dir()                      # Local names of variables, functions, classes and modules.
      -<dict> = vars()                     # Dict of local names and their objects. Also locals().
      +<dict> = vars()                     # Dict of local names and their objects. Same as locals().
       <dict> = globals()                  # Dict of global names and their objects, e.g. __builtin__.
       
      <list> = dir(<obj>)                 # Returns names of object's attributes (including methods).
       <dict> = vars(<obj>)                # Returns dict of writable attributes. Also <obj>.__dict__.
      -<bool> = hasattr(<obj>, '<name>')   # Checks if object possesses attribute with passed name.
      -value  = getattr(<obj>, '<name>')   # Returns object's attribute or raises AttributeError.
      +<bool> = hasattr(<obj>, '<name>')   # Checks if object possesses attribute of the passed name.
      +value  = getattr(<obj>, '<name>')   # Returns the object's attribute or raises AttributeError.
       setattr(<obj>, '<name>', value)     # Sets attribute. Only works on objects with __dict__ attr.
       delattr(<obj>, '<name>')            # Deletes attribute from __dict__. Also `del <obj>.<name>`.
       
      -
      <Sig>  = inspect.signature(<func>)  # Returns a Signature object of the passed function.
      +
      <Sig>  = inspect.signature(<func>)  # Returns Signature object of the passed function or class.
       <dict> = <Sig>.parameters           # Returns dict of Parameters. Also <Sig>.return_annotation.
       <memb> = <Param>.kind               # Returns ParameterKind member (Parameter.KEYWORD_ONLY, …).
       <type> = <Param>.annotation         # Returns Parameter.empty if missing. Also <Param>.default.
      @@ -1861,75 +1857,75 @@ 

      Format

      Thread

      <Thread> = Thread(target=<function>)           # Use `args=<collection>` to set the arguments.
       <Thread>.start()                               # Starts the thread. Also <Thread>.is_alive().
      -<Thread>.join()                                # Waits for the thread to finish executing.
      +<Thread>.join()                                # Waits until the thread has finished executing.
       
      • Use 'kwargs=<dict>' to pass keyword arguments to the function.
      • -
      • Use 'daemon=True', or the program will not be able to exit while the thread is alive.
      • +
      • Use 'daemon=True', or the program won't be able to exit while the thread is alive.
      -

      Lock

      <lock> = Lock/RLock()                          # RLock can only be released by acquirer.
      -<lock>.acquire()                               # Waits for the lock to be available.
      -<lock>.release()                               # Makes the lock available again.
      +

      Lock

      <lock> = Lock/RLock()                          # RLock can only be released by acquirer thread.
      +<lock>.acquire()                               # Blocks (waits) until lock becomes available.
      +<lock>.release()                               # Releases the lock so it can be acquired again.
       
      -

      Or:

      with <lock>:                                   # Enters the block by calling acquire() and
      -    ...                                        # exits it with release(), even on error.
      +

      Or:

      with <lock>:                                   # Enters the block by calling method acquire().
      +    ...                                        # Exits it by calling release(), even on error.
       

      Semaphore, Event, Barrier

      <Semaphore> = Semaphore(value=1)               # Lock that can be acquired by 'value' threads.
       <Event>     = Event()                          # Method wait() blocks until set() is called.
      -<Barrier>   = Barrier(n_times)                 # Wait() blocks until it's called n times.
      +<Barrier>   = Barrier(<int>)                   # Wait() blocks until it's called integer times.
       
      -

      Queue

      <Queue> = queue.Queue(maxsize=0)               # A thread-safe first-in-first-out queue.
      -<Queue>.put(<el>)                              # Blocks until queue stops being full.
      -<Queue>.put_nowait(<el>)                       # Raises queue.Full exception if full.
      -<el> = <Queue>.get()                           # Blocks until queue stops being empty.
      -<el> = <Queue>.get_nowait()                    # Raises queue.Empty exception if empty.
      +

      Queue

      <Queue> = queue.Queue(maxsize=0)               # A first-in-first-out queue. It's thread safe.
      +<Queue>.put(<obj>)                             # The call blocks until queue stops being full.
      +<Queue>.put_nowait(<obj>)                      # Raises queue.Full exception if queue is full.
      +<obj> = <Queue>.get()                          # The call blocks until queue stops being empty.
      +<obj> = <Queue>.get_nowait()                   # Raises queue.Empty exception if it is empty.
       
      -

      Thread Pool Executor

      <Exec> = ThreadPoolExecutor(max_workers=None)  # Or: `with ThreadPoolExecutor() as <name>: ...`
      +

      Thread Pool Executor

      <Exec> = ThreadPoolExecutor(max_workers=None)  # Also `with ThreadPoolExecutor() as <name>: …`.
       <iter> = <Exec>.map(<func>, <args_1>, ...)     # Multithreaded and non-lazy map(). Keeps order.
       <Futr> = <Exec>.submit(<func>, <arg_1>, ...)   # Creates a thread and returns its Future obj.
      -<Exec>.shutdown()                              # Blocks until all threads finish executing.
      +<Exec>.shutdown()                              # Waits for all the threads to finish executing.
       
      <bool> = <Future>.done()                       # Checks if the thread has finished executing.
      -<obj>  = <Future>.result(timeout=None)         # Waits for thread to finish and returns result.
      +<obj>  = <Future>.result(timeout=None)         # Raises TimeoutError after 'timeout' seconds.
       <bool> = <Future>.cancel()                     # Cancels or returns False if running/finished.
       <iter> = as_completed(<coll_of_Futures>)       # `next(<iter>)` returns next completed Future.
       
      • Map() and as_completed() also accept 'timeout'. It causes futures.TimeoutError when next() is called/blocking. Map() times from original call and as_completed() from first call to next(). As_completed() fails if next() is called too late, even if all threads are done.
      • -
      • Exceptions that happen inside threads are raised when map iterator's next() or Future's result() are called. Future's exception() method returns exception object or None.
      • +
      • Exceptions that happen inside threads are raised when map iterator's next() or Future's result() are called. Future's exception() method returns an exception object or None.
      • ProcessPoolExecutor provides true parallelism but: everything sent to/from workers must be pickable, queues must be sent using executor's 'initargs' and 'initializer' parameters, and executor should only be reachable via 'if __name__ == "__main__": ...'.

      #Coroutines

        -
      • Coroutines have a lot in common with threads, but unlike threads, they only give up control when they call another coroutine and they don’t use as much memory.
      • -
      • Coroutine definition starts with 'async' and its call with 'await'.
      • +
      • Coroutines have a lot in common with threads, but unlike threads, they only give up control when they call another coroutine and they don’t consume as much memory.
      • +
      • Coroutine definition starts with 'async' keyword and its call with 'await' keyword.
      • Use 'asyncio.run(<coroutine>)' to start the first/main coroutine.
      import asyncio as aio
       
      <coro> = <async_function>(<args>)          # Creates a coroutine by calling async def function.
      -<obj>  = await <coroutine>                 # Starts the coroutine and returns its result.
      -<task> = aio.create_task(<coroutine>)      # Schedules the coroutine for execution.
      +<obj>  = await <coroutine>                 # Starts the coroutine. Returns its result or None.
      +<task> = aio.create_task(<coroutine>)      # Schedules it for execution. Always keep the task.
       <obj>  = await <task>                      # Returns coroutine's result. Also <task>.cancel().
       
      <coro> = aio.gather(<coro/task>, ...)      # Schedules coros. Returns list of results on await.
       <coro> = aio.wait(<tasks>, return_when=…)  # `'ALL/FIRST_COMPLETED'`. Returns (done, pending).
      -<iter> = aio.as_completed(<coros/tasks>)   # Iter of coros that return next result on await.
      +<iter> = aio.as_completed(<coros/tasks>)   # Iter of coros. Each returns next result on await.
       

      Runs a terminal game where you control an asterisk that must avoid numbers:

      import asyncio, collections, curses, curses.textpad, enum, random
       
      -P = collections.namedtuple('P', 'x y')     # Position
      -D = enum.Enum('D', 'n e s w')              # Direction
      -W, H = 15, 7                               # Width, Height
      +P = collections.namedtuple('P', 'x y')     # Position (x and y coordinates).
      +D = enum.Enum('D', 'n e s w')              # Direction (north, east, etc.).
      +W, H = 15, 7                               # Width and height of the field.
       
       def main(screen):
      -    curses.curs_set(0)                     # Makes cursor invisible.
      +    curses.curs_set(0)                     # Makes the cursor invisible.
           screen.nodelay(True)                   # Makes getch() non-blocking.
           asyncio.run(main_coroutine(screen))    # Starts running asyncio code.
       
      @@ -1937,7 +1933,7 @@ 

      Format

      '*': P(0, 0)} | {id_: P(W//2, H//2) for id_ in range(10)} ai = [random_controller(id_, moves) for id_ in range(10)] - mvc = [human_controller(screen, moves), model(moves, state), view(state, screen)] + mvc = [controller(screen, moves), model(moves, state), view(state, screen)] tasks = [asyncio.create_task(coro) for coro in ai + mvc] await asyncio.wait(tasks, return_when=asyncio.FIRST_COMPLETED) @@ -1947,7 +1943,7 @@

      Format

      await asyncio.sleep(random.triangular(0.01, 0.65)) -async def human_controller(screen, moves): +async def controller(screen, moves): while True: key_mappings = {258: D.s, 259: D.n, 260: D.w, 261: D.e} if d := key_mappings.get(screen.getch()): @@ -1988,10 +1984,10 @@

      Format

      import matplotlib.pyplot as plt plt.plot/bar/scatter(x_data, y_data [, label=<str>]) # Also plt.plot(y_data). -plt.legend() # Adds a legend. -plt.title/xlabel/ylabel(<str>) # Adds a title or label. +plt.legend() # Adds a legend of labels. +plt.title/xlabel/ylabel(<str>) # Adds title or axis label. plt.show() # Also plt.savefig(<path>). -plt.clf() # Clears the plot. +plt.clf() # Clears the plot (figure).

      #Table

      Prints a CSV spreadsheet to the console:

      # $ pip3 install tabulate
      @@ -2004,7 +2000,7 @@ 

      Format

      #Console App

      Runs a basic file explorer in the console:

      # $ pip3 install windows-curses
       import curses, os
      -from curses import A_REVERSE, KEY_UP, KEY_DOWN, KEY_LEFT, KEY_RIGHT, KEY_ENTER
      +from curses import A_REVERSE, KEY_UP, KEY_DOWN, KEY_LEFT, KEY_RIGHT
       
       def main(screen):
           ch, first, selected, paths = 0, 0, 0, os.listdir()
      @@ -2017,9 +2013,9 @@ 

      Format

      and (selected > 0) selected += (ch == KEY_DOWN) and (selected < len(paths)-1) - first = min(first, selected) - first = max(first, selected - (height-1)) - if ch in [KEY_LEFT, KEY_RIGHT, KEY_ENTER, ord('\n'), ord('\r')]: + first -= (first > selected) + first += (first < selected-(height-1)) + if ch in [KEY_LEFT, KEY_RIGHT, ord('\n')]: new_dir = '..' if ch == KEY_LEFT else paths[selected] if os.path.isdir(new_dir): os.chdir(new_dir) @@ -2030,27 +2026,25 @@

      Format

      #GUI App

      A weight converter GUI application:

      # $ pip3 install PySimpleGUI
      +

      #GUI App

      Runs a desktop app for converting weights from metric units into pounds:

      # $ pip3 install PySimpleGUI
       import PySimpleGUI as sg
       
      -text_box = sg.Input(default_text='100', enable_events=True, key='-QUANTITY-')
      -dropdown = sg.InputCombo(['g', 'kg', 't'], 'kg', readonly=True, enable_events=True, k='-UNIT-')
      -label    = sg.Text('100 kg is 220.462 lbs.', key='-OUTPUT-')
      -button   = sg.Button('Close')
      -window   = sg.Window('Weight Converter', [[text_box, dropdown], [label], [button]])
      +text_box = sg.Input(default_text='100', enable_events=True, key='QUANTITY')
      +dropdown = sg.InputCombo(['g', 'kg', 't'], 'kg', readonly=True, enable_events=True, k='UNIT')
      +label    = sg.Text('100 kg is 220.462 lbs.', key='OUTPUT')
      +window   = sg.Window('Weight Converter', [[text_box, dropdown], [label], [sg.Button('Close')]])
       
       while True:
           event, values = window.read()
           if event in [sg.WIN_CLOSED, 'Close']:
               break
           try:
      -        quantity = float(values['-QUANTITY-'])
      +        quantity = float(values['QUANTITY'])
           except ValueError:
               continue
      -    unit = values['-UNIT-']
      -    factors = {'g': 0.001, 'kg': 1, 't': 1000}
      -    lbs = quantity * factors[unit] / 0.45359237
      -    window['-OUTPUT-'].update(value=f'{quantity} {unit} is {lbs:g} lbs.')
      +    unit = values['UNIT']
      +    lbs = quantity * {'g': 0.001, 'kg': 1, 't': 1000}[unit] / 0.45359237
      +    window['OUTPUT'].update(value=f'{quantity} {unit} is {lbs:g} lbs.')
       window.close()
       
      @@ -2058,38 +2052,39 @@

      Format

      #Scraping

      Scrapes Python's URL and logo from its Wikipedia page:

      # $ pip3 install requests beautifulsoup4
       import requests, bs4, os
       
      -response   = requests.get('https://en.wikipedia.org/wiki/Python_(programming_language)')
      -document   = bs4.BeautifulSoup(response.text, 'html.parser')
      -table      = document.find('table', class_='infobox vevent')
      +get = lambda url: requests.get(url, headers={'User-Agent': 'cpc-bot'})
      +response = get('https://en.wikipedia.org/wiki/Python_(programming_language)')
      +document = bs4.BeautifulSoup(response.text, 'html.parser')
      +table = document.find('table', class_='infobox vevent')
       python_url = table.find('th', text='Website').next_sibling.a['href']
      -logo_url   = table.find('img')['src']
      -filename   = os.path.basename(logo_url)
      +logo_url = table.find('img')['src']
      +filename = os.path.basename(logo_url)
       with open(filename, 'wb') as file:
      -    file.write(requests.get(f'https:{logo_url}').content)
      -print(f'{python_url}, file://{os.path.abspath(filename)}')
      +    file.write(get(f'https:{logo_url}').content)
      +print(f'URL: {python_url}, logo: file://{os.path.abspath(filename)}')
       

      Selenium

      Library for scraping websites with dynamic content.

      # $ pip3 install selenium
       from selenium import webdriver
      -
      -<WebDrv> = webdriver.Chrome/Firefox/Safari/Edge()     # Opens a browser. Also <WebDrv>.quit().
      -<WebDrv>.get('<url>')                                 # Also <WebDrv>.implicitly_wait(seconds).
      -<str>  = <WebDrv>.page_source                         # Returns HTML of fully rendered page.
      -<El>   = <WebDrv/El>.find_element('css selector', …)  # '<tag>#<id>.<class>[<attr>="<val>"]…'.
      -<list> = <WebDrv/El>.find_elements('xpath', …)        # '//<tag>[@<attr>="<val>"]…'. See XPath.
      -<str>  = <El>.get_attribute(<str>)                    # Property if exists. Also <El>.text.
      -<El>.click/clear()                                    # Also <El>.send_keys(<str>).
       
      -

      XPath — also available in lxml, Scrapy, and browser's console via '$x("<xpath>")':

      <xpath>     = //<element>[/ or // <element>]          # /<child>, //<descendant>, /../<sibling>
      -<xpath>     = //<element>/following::<element>        # Next element. Also preceding/parent/…
      -<element>   = <tag><conditions><index>                # `<tag> = */a/…`, `<index> = [1/2/…]`.
      -<condition> = [<sub_cond> [and/or <sub_cond>]]        # For negation use `not(<sub_cond>)`.
      -<sub_cond>  = @<attr>[="<val>"]                       # `text()=`, `.=` match (complete) text.
      -<sub_cond>  = contains(@<attr>, "<val>")              # Is <val> a substring of attr's value?
      -<sub_cond>  = [//]<element>                           # Has matching child? Descendant if //.
      +
      <Drv> = webdriver.Chrome/Firefox/Safari/Edge()  # Opens the browser. Also <Driver>.quit().
      +<Drv>.implicitly_wait(seconds)                  # Sets timeout for find_element/s() methods.
      +<Drv>.get('<url>')                              # Blocks until browser fires the load event.
      +<str> = <Drv>.page_source                       # Returns HTML of the page's current state.
      +<El>  = <Drv/El>.find_element('xpath', <str>)   # Accepts '//<tag>[@<attr_name>="<val>"]…'.
      +<str> = <El>.get_attribute('<name>')            # Returns attribute or property if exists.
      +<El>.click/clear()                              # Also <El>.text and <El>.send_keys(<str>).
      +
      +

      XPath — also available in lxml, Scrapy, and browser's console via '$x("<xpath>")':

      <xpath>     = //<element>[/ or // <element>]    # E.g. …/child, …//descendant, …/../sibling.
      +<xpath>     = //<element>/following::<element>  # Next element. Also preceding::, parent::.
      +<element>   = <tag><conditions><index>          # Tag accepts */a/…. Use [1/2/…] for index.
      +<condition> = [<sub_cond> [and/or <sub_cond>]]  # Use not(<sub_cond>) to negate condition.
      +<sub_cond>  = @<attr>[="<val>"]                 # `text()=` and `.=` match (complete) text.
      +<sub_cond>  = contains(@<attr>, "<val>")        # Is <val> a substring of attribute's value?
      +<sub_cond>  = [//]<element>                     # Has matching child? Descendant if //<el>.
       

      #Web App

      Flask is a micro web framework/server. If you just want to open a html file in a web browser use 'webbrowser.open(<path>)' instead.

      # $ pip3 install flask
      @@ -2105,12 +2100,12 @@ 

      Format

      Waitress and a HTTP server such as Nginx for better security.
    • Debug mode restarts the app whenever script changes and displays errors in the browser.
    -

    Static Request

    @app.route('/img/<path:filename>')
    +

    Serving Files

    @app.route('/img/<path:filename>')
     def serve_file(filename):
         return fl.send_from_directory('DIRNAME', filename)
     
    -

    Dynamic Request

    @app.route('/<sport>')
    +

    Serving HTML

    @app.route('/<sport>')
     def serve_html(sport):
         return fl.render_template_string('<h1>{{title}}</h1>', title=sport)
     
    @@ -2121,7 +2116,7 @@

    Format

    'fl.request.args[<str>]' returns parameter from query string (URL part right of '?').
  • 'fl.session[<str>] = <obj>' stores session data. It requires secret key to be set at the startup with 'app.secret_key = <str>'.
  • -

    REST Request

    @app.post('/<sport>/odds')
    +

    Serving JSON

    @app.post('/<sport>/odds')
     def serve_json(sport):
         team = fl.request.form['team']
         return {'team': team, 'odds': [2.09, 3.74, 3.68]}
    @@ -2192,7 +2187,7 @@ 

    Format

    <view> = <array>.reshape(<shape>) # Also `<array>.shape = <shape>`. <array> = <array>.flatten() # Also `<view> = <array>.ravel()`. -<view> = <array>.transpose() # Or: <array>.T +<view> = <array>.transpose() # Flips the table over its diagonal.

    <array> = np.copy/abs/sqrt/log/int64(<array>)           # Returns new array of the same shape.
     <array> = <array>.sum/max/mean/argmax/all(axis)         # Aggregates specified dimension.
    @@ -2222,8 +2217,7 @@ 

    Format

    ':' returns a slice of all dimension's indices. Omitted dimensions default to ':'. -
  • Python converts 'obj[i, j]' to 'obj[(i, j)]'. This makes '<2d>[row_i, col_i]' and '<2d>[row_indices]' indistinguishable to NumPy if tuple of indices is passed!
  • -
  • Indexing with a slice and 1d object works the same as when using two slices (lines 4, 6, 7).
  • +
  • Python converts 'obj[i, j]' to 'obj[(i, j)]'. This makes '<2d>[row_i, col_i]' and '<2d>[row_indices]' indistinguishable to NumPy if tuple of two indices is passed!
  • 'ix_([1, 2], [3, 4])' returns '[[1], [2]]' and '[[3, 4]]'. Due to broadcasting rules, this is the same as using '[[1, 1], [2, 2]]' and '[[3, 4], [3, 4]]'.
  • Any value that is broadcastable to the indexed shape can be assigned to the selection.
  • @@ -2272,7 +2266,7 @@

    Format

    <Image> = Image.new('<mode>', (width, height)) # Creates new image. Also `color=<int/tuple>`. <Image> = Image.open(<path>) # Identifies format based on file's contents. <Image> = <Image>.convert('<mode>') # Converts image to the new mode (see Modes). -<Image>.save(<path>) # Selects format based on extension (PNG/JPG…). +<Image>.save(<path>) # Accepts `quality=<int>` if extension is jpg. <Image>.show() # Displays image in default preview app.

    <int/tup> = <Image>.getpixel((x, y))            # Returns pixel's value (its color).
    @@ -2288,9 +2282,9 @@ 

    Format

    # Use <array>.clip(0, 255) to clip the values.

    Modes

      -
    • 'L' - Lightness (greyscale image). Each pixel is an int between 0 and 255.
    • -
    • 'RGB' - Red, green, blue (true color image). Each pixel is a tuple of three ints.
    • -
    • 'RGBA' - RGB with alpha. Low alpha (i.e. forth int) makes pixel more transparent.
    • +
    • 'L' - Lightness (greyscale image). Each pixel is an integer between 0 and 255.
    • +
    • 'RGB' - Red, green, blue (true color image). Each pixel is a tuple of three integers.
    • +
    • 'RGBA' - RGB with alpha. Low alpha (i.e. fourth int) makes pixel more transparent.
    • 'HSV' - Hue, saturation, value. Three ints representing color in HSV color space.

    Examples

    Creates a PNG image of a rainbow gradient:

    WIDTH, HEIGHT = 100, 100
     n_pixels = WIDTH * HEIGHT
    @@ -2312,12 +2306,12 @@ 

    Format

    Image Draw

    from PIL import ImageDraw
     <Draw> = ImageDraw.Draw(<Image>)                # Object for adding 2D graphics to the image.
    -<Draw>.point((x, y))                            # Draws a point. Truncates floats into ints.
    -<Draw>.line((x1, y1, x2, y2 [, ...]))           # To get anti-aliasing use Image's resize().
    +<Draw>.point((x, y))                            # Draws a point. Also `fill=<int/tuple/str>`.
    +<Draw>.line((x1, y1, x2, y2 [, ...]))           # For anti-aliasing use <Image>.resize((w, h)).
     <Draw>.arc((x1, y1, x2, y2), deg1, deg2)        # Draws in clockwise dir. Also pieslice().
     <Draw>.rectangle((x1, y1, x2, y2))              # Also rounded_rectangle(), regular_polygon().
    -<Draw>.polygon((x1, y1, x2, y2, ...))           # Last point gets connected to the first.
    -<Draw>.ellipse((x1, y1, x2, y2))                # To rotate use Image's rotate() and paste().
    +<Draw>.polygon((x1, y1, x2, y2, ...))           # Last point gets connected to the first one.
    +<Draw>.ellipse((x1, y1, x2, y2))                # To rotate use <Image>.rotate(anticlock_deg).
     <Draw>.text((x, y), <str>, font=<Font>)         # `<Font> = ImageFont.truetype(<path>, size)`.
     
    @@ -2409,14 +2403,13 @@

    Format

    Adds noise to the WAV file:

    from random import uniform
     samples_f, params = read_wav_file('test.wav')
     samples_f = (f + uniform(-0.05, 0.05) for f in samples_f)
    -write_to_wav_file('test.wav', samples_f, params)
    +write_to_wav_file('test.wav', samples_f, p=params)
     

    Plays the WAV file:

    # $ pip3 install simpleaudio
     from simpleaudio import play_buffer
     with wave.open('test.wav') as file:
    -    p = file.getparams()
    -    frames = file.readframes(-1)
    +    frames, p = file.readframes(-1), file.getparams()
         play_buffer(frames, p.nchannels, p.sampwidth, p.framerate).wait_done()
     
    @@ -2428,34 +2421,35 @@

    Format

    #Synthesizer

    Plays Popcorn by Gershon Kingsley:

    # $ pip3 install simpleaudio
    -import array, itertools as it, math, simpleaudio
    -
    -F  = 44100
    -P1 = '71♩,69♪,,71♩,66♪,,62♩,66♪,,59♩,,,71♩,69♪,,71♩,66♪,,62♩,66♪,,59♩,,,'
    -P2 = '71♩,73♪,,74♩,73♪,,74♪,,71♪,,73♩,71♪,,73♪,,69♪,,71♩,69♪,,71♪,,67♪,,71♩,,,'
    -get_pause   = lambda seconds: it.repeat(0, int(seconds * F))
    -sin_f       = lambda i, hz: math.sin(i * 2 * math.pi * hz / F)
    -get_wave    = lambda hz, seconds: (sin_f(i, hz) for i in range(int(seconds * F)))
    -get_hz      = lambda note: 440 * 2 ** ((int(note[:2]) - 69) / 12)
    -get_sec     = lambda note: 1/4 if '♩' in note else 1/8
    -get_samples = lambda note: get_wave(get_hz(note), get_sec(note)) if note else get_pause(1/8)
    -samples_f   = it.chain.from_iterable(get_samples(n) for n in (P1+P2).split(','))
    -samples_i   = array.array('h', (int(f * 30000) for f in samples_f))
    -simpleaudio.play_buffer(samples_i, 1, 2, F).wait_done()
    +import itertools as it, math, array, simpleaudio
    +
    +def play_notes(notes, bpm=132, f=44100):
    +    get_pause   = lambda n_beats: it.repeat(0, int(n_beats * 60/bpm * f))
    +    sin_f       = lambda i, hz: math.sin(i * 2 * math.pi * hz / f)
    +    get_wave    = lambda hz, n_beats: (sin_f(i, hz) for i in range(int(n_beats * 60/bpm * f)))
    +    get_hz      = lambda note: 440 * 2 ** ((int(note[:2]) - 69) / 12)
    +    get_nbeats  = lambda note: 1/2 if '♩' in note else 1/4 if '♪' in note else 1
    +    get_samples = lambda n: get_wave(get_hz(n), get_nbeats(n)) if n else get_pause(1/4)
    +    samples_f   = it.chain.from_iterable(get_samples(n) for n in notes.split(','))
    +    samples_i   = array.array('h', (int(fl * 5000) for fl in samples_f))
    +    simpleaudio.play_buffer(samples_i, 1, 2, f).wait_done()
    +
    +play_notes('83♩,81♪,,83♪,,78♪,,74♪,,78♪,,71♪,,,,83♪,,81♪,,83♪,,78♪,,74♪,,78♪,,71♪,,,,'
    +           '83♩,85♪,,86♪,,85♪,,86♪,,83♪,,85♩,83♪,,85♪,,81♪,,83♪,,81♪,,83♪,,79♪,,83♪,,,,')
     
    -

    #Pygame

    Opes a window and draws a square that can be moved with arrow keys:

    # $ pip3 install pygame
    +

    #Pygame

    Opens a window and draws a square that can be moved with arrow keys:

    # $ pip3 install pygame
     import pygame as pg
     
     pg.init()
     screen = pg.display.set_mode((500, 500))
     rect = pg.Rect(240, 240, 20, 20)
     while not pg.event.get(pg.QUIT):
    -    deltas = {pg.K_UP: (0, -20), pg.K_RIGHT: (20, 0), pg.K_DOWN: (0, 20), pg.K_LEFT: (-20, 0)}
         for event in pg.event.get(pg.KEYDOWN):
    -        dx, dy = deltas.get(event.key, (0, 0))
    -        rect = rect.move((dx, dy))
    +        dx = (event.key == pg.K_RIGHT) - (event.key == pg.K_LEFT)
    +        dy = (event.key == pg.K_DOWN) - (event.key == pg.K_UP)
    +        rect = rect.move((dx * 20, dy * 20))
         screen.fill(pg.Color('black'))
         pg.draw.rect(screen, pg.Color('white'), rect)
         pg.display.flip()
    @@ -2464,18 +2458,18 @@ 

    Format

    Rect

    Object for storing rectangular coordinates.

    <Rect> = pg.Rect(x, y, width, height)           # Creates Rect object. Truncates passed floats.
    -<int>  = <Rect>.x/y/centerx/centery/…           # Top, right, bottom, left. Allows assignments.
    -<tup.> = <Rect>.topleft/center/…                # Topright, bottomright, bottomleft. Same.
    -<Rect> = <Rect>.move((delta_x, delta_y))        # Use move_ip() to move in-place.
    +<int>  = <Rect>.x/y/centerx/centery/…           # `top/right/bottom/left`. Allows assignments.
    +<tup.> = <Rect>.topleft/center/…                # `topright/bottomright/bottomleft/size`. Same.
    +<Rect> = <Rect>.move((delta_x, delta_y))        # Use move_ip() to move the rectangle in-place.
     
    -
    <bool> = <Rect>.collidepoint((x, y))            # Checks if rectangle contains the point.
    -<bool> = <Rect>.colliderect(<Rect>)             # Checks if the two rectangles overlap.
    +
    <bool> = <Rect>.collidepoint((x, y))            # Checks whether rectangle contains the point.
    +<bool> = <Rect>.colliderect(<Rect>)             # Checks whether the two rectangles overlap.
     <int>  = <Rect>.collidelist(<list_of_Rect>)     # Returns index of first colliding Rect or -1.
     <list> = <Rect>.collidelistall(<list_of_Rect>)  # Returns indices of all colliding rectangles.
     
    -

    Surface

    Object for representing images.

    <Surf> = pg.display.set_mode((width, height))   # Opens new window and returns its surface.
    +

    Surface

    Object for representing images.

    <Surf> = pg.display.set_mode((width, height))   # Opens a new window and returns its surface.
     <Surf> = pg.Surface((width, height))            # New RGB surface. RGBA if `flags=pg.SRCALPHA`.
     <Surf> = pg.image.load(<path/file>)             # Loads the image. Format depends on source.
     <Surf> = pg.surfarray.make_surface(<np_array>)  # Also `<np_arr> = surfarray.pixels3d(<Surf>)`.
    @@ -2483,22 +2477,22 @@ 

    Format

    <Surf>.fill(color) # Tuple, Color('#rrggbb[aa]') or Color(<name>). +
    <Surf>.fill(color)                              # Pass tuple of ints or pg.Color('<name/hex>').
     <Surf>.set_at((x, y), color)                    # Updates pixel. Also <Surf>.get_at((x, y)).
     <Surf>.blit(<Surf>, (x, y))                     # Draws passed surface at specified location.
     
    -
    from pygame.transform import scale, ...
    -<Surf> = scale(<Surf>, (width, height))         # Returns scaled surface.
    -<Surf> = rotate(<Surf>, anticlock_degrees)      # Returns rotated and scaled surface.
    -<Surf> = flip(<Surf>, x_bool, y_bool)           # Returns flipped surface.
    +
    from pygame.transform import scale, rotate      # Also: flip, smoothscale, scale_by.
    +<Surf> = scale(<Surf>, (width, height))         # Scales the surface. `smoothscale()` blurs it.
    +<Surf> = rotate(<Surf>, angle)                  # Rotates the surface for counterclock degrees.
    +<Surf> = flip(<Surf>, flip_x=False)             # Mirrors the surface. Also `flip_y=False`.
     
    -
    from pygame.draw import line, ...
    -line(<Surf>, color, (x1, y1), (x2, y2), width)  # Draws a line to the surface.
    +
    from pygame.draw import line, arc, rect         # Also: ellipse, polygon, circle, aaline.
    +line(<Surf>, color, (x1, y1), (x2, y2))         # Draws a line to the surface. Also `width=1`.
     arc(<Surf>, color, <Rect>, from_rad, to_rad)    # Also ellipse(<Surf>, color, <Rect>, width=0).
     rect(<Surf>, color, <Rect>, width=0)            # Also polygon(<Surf>, color, points, width=0).
     
    <Font> = pg.font.Font(<path/file>, size)        # Loads TTF file. Pass None for default font.
    -<Surf> = <Font>.render(text, antialias, color)  # Background color can be specified at the end.
    +<Surf> = <Font>.render(text, antialias, color)  # Accepts background color as fourth argument.
     

    Sound

    <Sound> = pg.mixer.Sound(<path/file/bytes>)     # WAV file or bytes/array of signed shorts.
     <Sound>.play/stop()                             # Also set_volume(<float>) and fadeout(msec).
    @@ -2568,7 +2562,7 @@ 

    Format

    85, 168, 255)) mario.facing_left = mario.spd.x < 0 if mario.spd.x else mario.facing_left is_airborne = D.s not in get_boundaries(mario.rect, tiles) - image_index = 4 if is_airborne else (next(mario.frame_cycle) if mario.spd.x else 6) + image_index = 4 if is_airborne else next(mario.frame_cycle) if mario.spd.x else 6 screen.blit(images[image_index + (mario.facing_left * 9)], mario.rect) for t in tiles: is_border = t.x in [0, (W-1)*16] or t.y in [0, (H-1)*16] @@ -2610,15 +2604,16 @@

    Format

    # Use pd.to_datetime(<S>) to get S of datetimes. <S> = <S>.dt.to_period('y/m/d/h') # Quantizes datetimes into Period objects.

    -
    <S>.plot.line/area/bar/pie/hist()              # Generates a plot. `plt.show()` displays it.
    +
    <S>.plot.line/area/bar/pie/hist()              # Generates a plot. Accepts `title=<str>`.
    +plt.show()                                     # Displays the plot. Also plt.savefig(<path>).
     
      -
    • Also '<S>.quantile(<float/coll>)' and 'pd.cut(<S>, bins=<int/coll>)'.
    • -
    • Indexing objects can't be tuples because 'obj[x, y]' is converted to 'obj[(x, y)]'.
    • -
    • Pandas uses NumPy types like 'np.int64'. Series is converted to 'float64' if we assign np.nan to any item. Use '<S>.astype(<str/type>)' to get converted Series.
    • -
    • Series will silently overflow if we run 'pd.Series([100], dtype="int8") + 100'!
    • +
    • Use 'print(<S>.to_string())' to print a Series that has more than sixty items.
    • +
    • Use '<S>.index' to get collection of keys and '<S>.index = <coll>' to update them.
    • +
    • Only pass a list or Series to loc/iloc because 'obj[x, y]' is converted to 'obj[(x, y)]' and '<S>.loc[key_1, key_2]' is how you retrieve a value from a multi-indexed Series.
    • +
    • Pandas uses NumPy types like 'np.int64'. Series is converted to 'float64' if np.nan is assigned to any item. Use '<S>.astype(<str/type>)' to get converted Series.
    -

    Series — Aggregate, Transform, Map:

    <el> = <S>.sum/max/mean/idxmax/all/count()     # Or: <S>.agg(lambda <S>: <el>)
    +

    Series — Aggregate, Transform, Map:

    <el> = <S>.sum/max/mean/std/idxmax/count()     # Or: <S>.agg(lambda <S>: <el>)
     <S>  = <S>.rank/diff/cumsum/ffill/interpol…()  # Or: <S>.agg/transform(lambda <S>: <S>)
     <S>  = <S>.isna/fillna/isin([<el/coll>])       # Or: <S>.agg/transform/map(lambda <el>: <el>)
     
    @@ -2639,9 +2634,6 @@

    Format

    '<S>[key_1, key_2]' to get its values. -

    DataFrame

    Table with labeled rows and columns.

    >>> df = pd.DataFrame([[1, 2], [3, 4]], index=['a', 'b'], columns=['x', 'y']); df
        x  y
     a  1  2
    @@ -2707,7 +2699,7 @@ 

    Format

    6 7 │ │ │ treated as a column. ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━┷━━━━━━━━━━━━━━━┷━━━━━━━━━━━━┷━━━━━━━━━━━━┷━━━━━━━━━━━━━━━━━━━━━━━━━━━┛

    -

    DataFrame — Aggregate, Transform, Map:

    <S>  = <DF>.sum/max/mean/idxmax/all/count()    # Or: <DF>.apply/agg(lambda <S>: <el>)
    +

    DataFrame — Aggregate, Transform, Map:

    <S>  = <DF>.sum/max/mean/std/idxmax/count()    # Or: <DF>.apply/agg(lambda <S>: <el>)
     <DF> = <DF>.rank/diff/cumsum/ffill/interpo…()  # Or: <DF>.apply/agg/transform(lambda <S>: <S>)
     <DF> = <DF>.isna/fillna/isin([<el/coll>])      # Or: <DF>.applymap(lambda <el>: <el>)
     
    @@ -2743,18 +2735,19 @@

    Format

    File Formats

    <S/DF> = pd.read_json/pickle(<path/url/file>)  # Also io.StringIO(<str>), io.BytesIO(<bytes>).
     <DF>   = pd.read_csv/excel(<path/url/file>)    # Also `header/index_col/dtype/usecols/…=<obj>`.
     <list> = pd.read_html(<path/url/file>)         # Raises ImportError if webpage has zero tables.
    -<S/DF> = pd.read_parquet/feather/hdf(<path…>)  # Read_hdf() accepts `key=<s/df_name>` argument.
    -<DF>   = pd.read_sql('<table/query>', <conn>)  # Pass SQLite3/Alchemy connection (see #SQLite).
    +<S/DF> = pd.read_parquet/feather/hdf(<path…>)  # Function read_hdf() accepts `key=<s/df_name>`.
    +<DF>   = pd.read_sql('<table/query>', <conn>)  # Pass SQLite3/Alchemy connection. See #SQLite.
     
    -
    <DF>.to_json/csv/html/parquet/latex(<path>)    # Returns a string/bytes if path is omitted.
    -<DF>.to_pickle/excel/feather/hdf(<path>)       # To_hdf() requires `key=<s/df_name>` argument.
    +
    <DF>.to_json/csv/html/latex/parquet(<path>)    # Returns a string/bytes if path is omitted.
    +<DF>.to_pickle/excel/feather/hdf(<path>)       # Method to_hdf() requires `key=<s/df_name>`.
     <DF>.to_sql('<table_name>', <connection>)      # Also `if_exists='fail/replace/append'`.
     
    • '$ pip3 install "pandas[excel]" odfpy lxml pyarrow' installs dependencies.
    • -
    • Read_csv() only parses dates of columns that were specified by 'parse_dates' argument. It automatically tries to detect the format, but it can be helped with 'date_format' or 'dayfirst' arguments. Both dates and datetimes get stored as pd.Timestamp objects.
    • -
    • If 'parse_dates' and 'index_col' are the same column, we get a DF with DatetimeIndex. Its 'resample("y/m/d/h")' method returns a Resampler object that is similar to GroupBy.
    • +
    • Csv functions use the same dialect as standard library's csv module (e.g. 'sep=","').
    • +
    • Read_csv() only parses dates of columns that are listed in 'parse_dates'. It automatically tries to detect the format, but it can be helped with 'date_format' or 'dayfirst' arguments.
    • +
    • We get a dataframe with DatetimeIndex if 'parse_dates' argument includes 'index_col'. Its 'resample("y/m/d/h")' method returns Resampler object that is similar to GroupBy.

    GroupBy

    Object that groups together rows of a dataframe based on the value of the passed column.

    <GB> = <DF>.groupby(col_key/s)                 # Splits DF into groups based on passed column.
     <DF> = <GB>.apply/filter(<func>)               # Filter drops a group if func returns False.
    @@ -2764,7 +2757,7 @@ 

    Format

    <DF> = <GB>.sum/max/mean/idxmax/all() # Or: <GB>.agg(lambda <S>: <el>) +
    <DF> = <GB>.sum/max/mean/std/idxmax/count()    # Or: <GB>.agg(lambda <S>: <el>)
     <DF> = <GB>.rank/diff/cumsum/ffill()           # Or: <GB>.transform(lambda <S>: <S>)
     <DF> = <GB>.fillna(<el>)                       # Or: <GB>.transform(lambda <S>: <S>)
     
    @@ -2791,18 +2784,18 @@

    Format

    import plotly.express as px, pandas as pd

    -
    <Fig> = px.line(<DF>, x=col_key, y=col_key)            # Or: px.line(x=<list>, y=<list>)
    -<Fig>.update_layout(margin=dict(t=0, r=0, b=0, l=0))   # Also `paper_bgcolor='rgb(0, 0, 0)'`.
    -<Fig>.write_html/json/image('<path>')                  # <Fig>.show() displays the plot.
    +
    <Fig> = px.line(<DF> [, y=col_key/s [, x=col_key]])   # Also px.line(y=<list> [, x=<list>]).
    +<Fig>.update_layout(paper_bgcolor='#rrggbb')          # Also `margin=dict(t=0, r=0, b=0, l=0)`.
    +<Fig>.write_html/json/image('<path>')                 # Use <Fig>.show() to display the plot.
     
    -
    <Fig> = px.area/bar/box(<DF>, x=col_key, y=col_key)    # Also `color=col_key`.
    -<Fig> = px.scatter(<DF>, x=col_key, y=col_key)         # Also `color/size/symbol=col_key`.
    -<Fig> = px.scatter_3d(<DF>, x=col_key, y=col_key, …)   # `z=col_key`. Also color/size/symbol.
    -<Fig> = px.histogram(<DF>, x=col_key [, nbins=<int>])  # Number of bins depends on DF size.
    +
    <Fig> = px.area/bar/box(<DF>, x=col_key, y=col_keys)  # Also `color=col_key`. All are optional.
    +<Fig> = px.scatter(<DF>, x=col_key, y=col_keys)       # Also `color/size/symbol=col_key`. Same.
    +<Fig> = px.scatter_3d(<DF>, x=col_key, y=col_key, …)  # `z=col_key`. Also color, size, symbol.
    +<Fig> = px.histogram(<DF>, x=col_keys, y=col_key)     # Also color, nbins. All are optional.
     
    -

    Displays a line chart of total coronavirus deaths per million grouped by continent:

    covid = pd.read_csv('https://raw.githubusercontent.com/owid/covid-19-data/8dde8ca49b'
    +

    Displays a line chart of total COVID-19 deaths per million grouped by continent:

    covid = pd.read_csv('https://raw.githubusercontent.com/owid/covid-19-data/8dde8ca49b'
                         '6e648c17dd420b2726ca0779402651/public/data/owid-covid-data.csv',
    -                    usecols=['iso_code', 'date', 'total_deaths', 'population'])
    +                    usecols=['iso_code', 'date', 'population', 'total_deaths'])
     continents = pd.read_csv('https://gto76.github.io/python-cheatsheet/web/continents.csv',
                              usecols=['Three_Letter_Country_Code', 'Continent_Name'])
     df = pd.merge(covid, continents, left_on='iso_code', right_on='Three_Letter_Country_Code')
    @@ -2810,13 +2803,13 @@ 

    Format

    'Total Deaths per Million'] = df.total_deaths * 1e6 / df.population df = df[df.date > '2020-03-14'] df = df.rename({'date': 'Date', 'Continent_Name': 'Continent'}, axis='columns') -px.line(df, x='Date', y='Total Deaths per Million', color='Continent').show() +px.line(df, x='Date', y='Total Deaths per Million', color='Continent')

    -

    Displays a multi-axis line chart of total coronavirus cases and changes in prices of Bitcoin, Dow Jones and gold:

    # $ pip3 install pandas lxml selenium plotly
    -import pandas as pd, selenium.webdriver, plotly.graph_objects as go
    +

    Displays a multi-axis line chart of total COVID-19 cases and changes in prices of Bitcoin, Dow Jones and gold:

    # $ pip3 install pandas lxml selenium plotly
    +import pandas as pd, selenium.webdriver, io, plotly.graph_objects as go
     
     def main():
         covid, (bitcoin, gold, dow) = get_covid_cases(), get_tickers()
    @@ -2824,24 +2817,25 @@ 

    Format

    def get_covid_cases(): - url = 'https://covid.ourworldindata.org/data/owid-covid-data.csv' - df = pd.read_csv(url, usecols=['location', 'date', 'total_cases'], parse_dates=['date']) - df = df[df.location == 'World'] + url = 'https://catalog.ourworldindata.org/garden/covid/latest/compact/compact.csv' + df = pd.read_csv(url, parse_dates=['date']) + df = df[df.country == 'World'] s = df.set_index('date').total_cases return s.rename('Total Cases') def get_tickers(): with selenium.webdriver.Chrome() as driver: + driver.implicitly_wait(10) symbols = {'Bitcoin': 'BTC-USD', 'Gold': 'GC=F', 'Dow Jones': '%5EDJI'} - for name, symbol in symbols.items(): - yield get_ticker(driver, name, symbol) + return [get_ticker(driver, name, symbol) for name, symbol in symbols.items()] def get_ticker(driver, name, symbol): url = f'https://finance.yahoo.com/quote/%3Cspan%20class="hljs-subst">{symbol}/history/' driver.get(url + '?period1=1579651200&period2=9999999999') if buttons := driver.find_elements('xpath', '//button[@name="reject"]'): buttons[0].click() - dataframes = pd.read_html(driver.page_source, parse_dates=['Date']) + html = io.StringIO(driver.page_source) + dataframes = pd.read_html(html, parse_dates=['Date']) s = dataframes[0].set_index('Date').Open return s.rename(name) @@ -2876,32 +2870,30 @@

    Format

    #Appendix

    Cython

    Library that compiles Python-like code into C.

    # $ pip3 install cython
    -import pyximport; pyximport.install()  # Module that runs imported Cython scripts.
    -import <cython_script>                 # Script must be saved with '.pyx' extension.
    -<cython_script>.main()                 # Main() isn't automatically executed.
    +import pyximport; pyximport.install()                # Module that runs Cython scripts.
    +import <cython_script>                               # Script must have '.pyx' extension.
     
    -

    Definitions:

      -
    • All 'cdef' definitions are optional, but they contribute to the speed-up.
    • -
    • Also supports C pointers via '*' and '&', structs, unions, and enums.
    • -
    cdef <ctype/type> <var_name> [= <obj>]
    -cdef <ctype>[n_elements] <var_name> [= <coll_of_nums>]
    -cdef <ctype/type/void> <func_name>(<ctype/type> <arg_name>): ...
    +

    All 'cdef' definitions are optional, but they contribute to the speed-up:

    cdef <type> <var_name> [= <obj/var>]                 # Either Python or C type variable.
    +cdef <ctype> *<pointer_name> [= &<var>]              # Use <pointer>[0] to get the value.
    +cdef <ctype>[size] <array_name> [= <coll/array>]     # Also `<ctype>[:] <mview> = <array>`.
    +cdef <ctype> *<array_name> [= <coll/array/pointer>]  # E.g. `<<ctype> *> malloc(n_bytes)`.
     
    - -
    cdef class <class_name>:
    -    cdef public <ctype/type> <attr_name>
    -    def __init__(self, <ctype/type> <arg_name>):
    -        self.<attr_name> = <arg_name>
    +
    cdef <type> <func_name>(<type> [*]<arg_name>): ...   # Omitted types default to `object`.
    +
    +
    cdef class <class_name>:                             # Also `cdef struct <struct_name>:`.
    +    cdef public <type> [*]<attr_name>                # Also `... <ctype> [*]<field_name>`.
    +    def __init__(self, <type> <arg_name>):           # Also `cdef __dealloc__(self):`.
    +        self.<attr_name> = <arg_name>                # Also `... free(<array/pointer>)`.
     
    -

    Virtual Environments

    System for installing libraries directly into project's directory.

    $ python3 -m venv NAME      # Creates virtual environment in current directory.
    -$ source NAME/bin/activate  # Activates it. On Windows run `NAME\Scripts\activate`.
    -$ pip3 install LIBRARY      # Installs the library into active environment.
    -$ python3 FILE              # Runs the script in active environment. Also `./FILE`.
    -$ deactivate                # Deactivates the active virtual environment.
    +

    Virtual Environments

    System for installing libraries directly into project's directory.

    $ python3 -m venv NAME         # Creates virtual environment in current directory.
    +$ source NAME/bin/activate     # Activates it. On Windows run `NAME\Scripts\activate`.
    +$ pip3 install LIBRARY         # Installs the library into active environment.
    +$ python3 FILE                 # Runs the script in active environment. Also `./FILE`.
    +$ deactivate                   # Deactivates the active virtual environment.
     
    @@ -2942,7 +2934,7 @@

    Format