Python 3
Quick Reference

This is a one-(long-)page overview of the Python 3 (3.12.3) programming language in the form of commented lines of code.

See also the old Python 2 quick reference.

Contents

Python Script Execution

Shebang

#!/usr/bin/env python3
#!/usr/bin/env python3.12
With one of the above shebangs as the first line of the script, most Unix/Linux systems will automatically pass the script to the latest installed version 3.x.x or 3.12.x Python interpreter when the script is run as a command. This also works on Windows if the script is passed to (or the typical script extension .py is associated with) the Python launcher py.exe.

Syntax

Statement Boundaries

a = 3                   # Statements are usually separated by newlines
print(a)                # ...
b = 'hi'; print(b)      # (Non-compound) statements may be separated by semicolon
c = 4;                  # and optionally terminated by semicolon
                        # but using semicolons is discouraged (PEP 8)
d = 2 * a + \
    c                   # Any statement can be split across multiple lines
                        # using backslash + newline (nothing allowed between the two)
s = 'hel\
  lo'                   # s = 'hel  lo' (any indention is included in string)
s = 'hel' \
    'lo'                # s = 'hello' (adjacent string literals are concatenated)
d = 2 * (a +            # No backslash needed inside parentheses/brackets/...
    c)
print('hel'             # Prints 'hello' (adjacent string literals are concatenated;
    'lo')               # no backslash needed inside parentheses)

Comments and Docstrings

# Comments start with the '#' and end at the end of the same line,
# so a comment spanning multiple lines must have '#' at the start
# of each line.
a = 3                   # Here's a comment following some code
b = a + \               # ILLEGAL comment after backslash
    2 +                 # and backslash WON'T WORK after comment either -> \
    3                   # Comment must be on last line when using backslashes

"""Another way to make a multiline comment
is to put it inside a multiline string which
is not assigned to anything."""

def f():
    # Here's a docstring for this function (similar for classes, modules):
    """When a string is the first statement of a function (or class
    or module), it becomes that object's docstring, accessible via
    the __doc__ attribute. All lines traditionally have the same
    indentation, and those spaces become part of the string.
    """
    return 3.14

print(f.__doc__)        # Prints f's docstring (or None if none): 'When a string ...'
help(f)                 # Prints information about f - including f's docstring

Compound Statement Format

if len(mylist) > 0:     # A compound statement contains a header
    sum = 0             # and a suite of statements with same indentation
    for x in mylist:    # and possibly nested compound statements
        print(x)
        sum += x
    print(sum)
else:                   # and possibly another header
    print('Empty list') # and its associated suite

def func(x):            # A compound statement cannot have an empty suite
    pass                # but 'pass' can be used to do nothing

if x < y: x = y; y = 0  # Single-line compound statement (discouraged)
                        # (cannot contain nested compound statements)

Keywords

import keyword
keyword.kwlist      # Returns the following list of "hard" Python keywords which cannot be
                    # used as names of user-defined objects:
                    # [
                    # 'False',      # Bool value
                    # 'None',       # None value
                    # 'True',       # Bool value
                    # 'and',        # Logical operator
                    # 'as',         # Used with 'except', 'import', 'with', 'case'
                    # 'assert',     # Assertion
                    # 'async',      # Asynchronous coroutine function definition
                    # 'await',      # Asynchronous coroutine handling
                    # 'break',      # Loop control
                    # 'class',      # Class definition
                    # 'continue',   # Loop control
                    # 'def',        # Function definition
                    # 'del',        # Deletion of names, attributes (see delattr), and
                    #               # container items (see __delitem__)
                    # 'elif',       # Used with 'if'
                    # 'else',       # Used with 'if', 'for', 'try', 'while'
                    # 'except',     # Used with 'try'
                    # 'finally',    # Used with 'try'
                    # 'for',        # Looping, also in comprehensions and
                    #               # generator expressions
                    # 'from',       # Used with 'import', 'raise', 'yield'
                    # 'global',     # Name scope control
                    # 'if',         # Conditional execution/expression or condition in
                    #               # comprehension, generator expression, or 'case'
                    # 'import',     # Module importing
                    # 'in',         # Membership testing operator, or used with 'for'
                    # 'is',         # Identity testing operator
                    # 'lambda',     # Anonymous lambda function expression
                    # 'nonlocal',   # Name scope control
                    # 'not',        # Logical operator
                    # 'or',         # Logical operator
                    # 'pass',       # Empty statement
                    # 'raise',      # Exception raising
                    # 'return',     # Return from a function
                    # 'try',        # Exception handling
                    # 'while',      # Looping
                    # 'with',       # Context management
                    # 'yield',      # Generator function control
                    # ]

keyword.softkwlist  # Returns the following list of "soft" Python keywords which may be
                    # used as names of user-defined objects outside contexts where a
                    # keyword is expected:
                    # [
                    # '_',          # Used with 'case'
                    # 'case',       # Used with 'match'
                    # 'match',      # Pattern matching
                    # 'type',       # Type alias definition for type hints
                    # ]

Expression Lists

# Expression list
t = 4, a + 1, 'hi'      # Right-hand side expression list becomes tuple (4, 2, 'hi')
                        # (assuming a = 1)
a, (b, c) = 3, (4, 5)   # Multiple assignments a=3, b=4, c=5 using expression lists
                        # (all right-hand expressions are evaluated before any assignments)
a, (b, c) = [3, (4, 5)] # Some alternatives equivalent to above; [] could also be ()
[a, (b, c)] = 3, (4, 5) # ...
a, *b, c = 1, 2, 3, 4   # Starred target (iterable unpacking); assigns a=1, b=[2,3], c=4
a, *b, c = range(5)     # Assigns a=0, b=[1,2,3], c=4
10,                     # Trailing comma is illegal/mandatory/optional if 0/1/more items
[4, a + 1, 'hi']        # List created from expression list
{4, a + 1, 'hi'}        # Set created from expression list
f(4, a + 1, 'hi')       # Function call using expression list as argument list
f(*range(2), **{'a':5}) # Iterable unpacking; same as f(0, 1, a=5)

Type Hints

Python provides syntax for annotating names (variables and functions) with their intended types, but Python itself does not enforce these types (and never will, according to PEP 484). IDEs and other tools (e.g. mypy) may use the type hints to do checking and assist developers in various ways.

See Types for more information on available types.

Note: mypy does not yet support the Python 3.12 syntax used below.

# Define f as a function that takes an integer and a string and returns a string
def f(n: int, s: str) -> str:
    r: str = n * s
    return r

MyList = list[tuple[str, float]]        # Defines MyList as an alias for a list of tuples
                                        # of string and float

a: MyList = [('A', 3), ('B', 4.1)]      # Defines 'a' to have type MyList and assigns a
                                        # valid object matching that type
b: tuple[int, ...] = (1, 2, 3)          # Defines 'b' to be a tuple of 0 or more integers
                                        # and assigns a valid object matching that type
d: dict[str, int | None] = {'A': 4, 'B': None}  # Defines 'd' to be a dict that maps from
                                                # string to either integer or None
# Import more type hinting support from 'typing' module
from typing import Any
# Import 'Sequence' type from abstract base class module
from collections.abc import Sequence

type MyType = tuple[str, Any]           # Defines MyType as an explicit alias for a tuple
                                        # of string and any type

# Define a generic function 'get' using a type variable T; the function takes an int and a
# sequence of T (which is constrained to being either an int or a MyType) and returns a T
def get[T: (int, MyType)](i: int, s: Sequence[T]) -> T:
    return s[i]

# Define a generic class 'G' using a type variable T (which is either an int or a MyType);
# the class works with any of T's possible types
class G[T: (int, MyType)]:
    def __init__(self, s: Sequence[T]) -> None:
        self.s: Sequence[T] = s
    def get(self, i: int) -> T:
        return self.s[i]

x: list[MyType] = [('A', 4), ('B', 'oo')]   # Defines 'x' to be a list of MyType and
                                            # assigns a valid object matching that type
get(0, x)                                   # Calls function 'get' with arguments matching
                                            # its parameter type hints; returns ('A', 4)
G(x).get(0)                                 # Creates an instance of class G using x as the
                                            # constructor argument (T is implicitly MyType)
                                            # and calls method 'get'; returns ('A', 4)
G[int]([4,5,6]).get(0)                      # Creates an instance of class G (with T
                                            # explicitly set to int) using a list of ints
                                            # as the constructor argument, calls method
                                            # 'get', and returns 4

Types

See also Type Hints.

Basic Types (Immutable)

Int
i = -2                  # Assigns an int (infinite range and precision)
type(i)                 # Returns class int
isinstance(i, int)      # Returns True
int('20')               # Returns 20 (an int)
int('20', 16)           # Returns 32 (second arg is base, 16 means hex)
i += 0xA + 0o12 + 0b10  # i is now 20 (-2 + 10 + 10 + 2); this is hex + octal + binary
012                     # ILLEGAL! (non-zero decimal numbers may not start with a zero -
                        # avoids confusion with obsolete octal format)
i.bit_length()          # Returns 5 (minimum number of bits needed to represent 20)
(5).bit_length()        # Returns 3 (5.bit_length() would fail because 5. means 5.0)
1_000_000 + 0b_1111_1111# Returns 1000255 (underscores improve readability)
Float
x = -3.4                # Assigns a float (range: sys.float_info.min to sys.float_info.max)
type(x)                 # Returns class float
isinstance(x, float)    # Returns True
float(3)                # Returns 3.0
float('2.5e6')          # Returns 2500000.0
float('inf')            # Returns positive infinity (greater than sys.float_info.max)
float('-inf')           # Returns negative infinity (less than sys.float_info.min)
float('nan')            # Returns the "not a number" (NaN) error value
0 * float('inf')        # Returns float('nan')
import math                 # Imports math module
float('inf') == math.inf    # Returns True
float('-inf') == -math.inf  # Returns True
math.isinf(float('-inf'))   # Returns True
math.isnan(float('nan'))    # Returns True
math.nan == math.nan        # Returns False; NaN is unequal to everything, incl. itself
math.sin(math.pi/2)     # Returns 1.0
math.sqrt(2)            # Returns 1.4142135623730951
1. + .2 + 3.4e-2        # Returns 1.234 (1. means 1.0, .2 means 0.2)
0.000_000_001           # Returns 1e-9 (underscores improve readability)
0.1 + 0.2 == 0.3        # Returns False because these values cannot be represented exactly
                        # in floating point binary; use the decimal.Decimal type if exact
                        # representations of (base-10) decimal values are needed, e.g. if
                        # D = decimal.Decimal then D('0.1') + D('0.2') == D('0.3') returns
                        # True
math.isclose(0.1 + 0.2, 0.3)    # Returns True because 0.1 + 0.2 is (very) close to 0.3
Complex
c = 1+2j                # Assigns a complex (pair of floats)
type(c)                 # Returns class complex
isinstance(c, complex)  # Returns True
c.real                  # 1.0
c.imag                  # 2.0
c.real = 3              # ILLEGAL! Raises AttributeError exception (complex is immutable)
c.conjugate()           # Returns (1-2j)
complex(1, 2)           # Returns (1+2j)
complex('1+2j')         # Returns (1+2j)
complex(1+2j, 10j)      # Returns (-9+2j) because 10j is multiplied by 1j and becomes -10
complex('infj')         # Same as complex(0, float('inf'))
complex('-infj')        # Same as complex(0, float('-inf'))
complex('inf-infj')     # Same as complex(float('inf'), float('-inf'))
complex('nanj')         # Same as complex(0, float('nan'))
import cmath                        # Imports cmath module (complex math)
complex('infj') == cmath.infj       # Returns True
complex('-infj') == -cmath.infj     # Returns True
cmath.isinf(complex('-infj'))       # Returns True
cmath.isnan(complex('nanj'))        # Returns True
abs(3+4j)               # Returns 5.0 (magnitude)
cmath.phase(3+4j)       # Returns 0.9272952180016122 (approx. 53 degrees in radians)
cmath.polar(3+4j)       # Returns (5.0, 0.9272952180016122)
cmath.rect(2, cmath.pi/4)           # Returns (1.4142135623730951+1.4142135623730951j)
cmath.sqrt(-1)                      # Returns 1j
0.1j + 0.2j == 0.3j                 # Returns False (see float type above for details)
cmath.isclose(0.1j + 0.2j, 0.3j)    # Returns True because 0.1j + 0.2j is close to 0.3j
Bool
b = True                # Assigns a bool (True or False)
type(b)                 # Returns class bool
isinstance(b, bool)     # Returns True
bool(0)                 # Returns False for 0 (else True)
bool('')                # Returns False for empty sequence type (else True)
bool(None)              # Returns False
int(True)               # Returns 1
int(False)              # Returns 0
3 + True                # Returns 4
False * 5               # Returns 0
None
x = None                # Assigns null object (nothing, unassigned argument/return value)
type(x)                 # Returns class with name 'NoneType'; None is the only instance of
                        # this class
isinstance(x, type(None)) # Returns True
bool(x)                 # Returns False
Slice
sl = slice(3)           # Assigns a slice object equivalent to the :3 in a[:3] (supports
                        # same parameters as range(), except negative start/stop values are
                        # counted from end); more under classes
type(sl)                # Returns class slice
isinstance(sl, slice)   # Returns True
print(sl)               # Prints 'slice(None, 3, None)'
'hello'[sl]             # Returns 'hel' (same as 'hello'[:3])
sl.start, sl.stop, sl.step  # Returns (None, 3, None)
sl.indices(len('hello'))    # Returns (0, 3, 1) (range()-style (start, stop, step) args)
sl.indices(len('hi'))       # Returns (0, 2, 1) (range is reduced to fit given length)
sl = slice(-2, None, -1)# Assigns a slice object equivalent to the -2::-1 in a[-2::-1]
'hello'[sl]             # Returns 'lleh' (same as 'hello'[-2::-1])
sl.indices(len('hello'))# Returns (3, -1, -1) (here, stop == None for slice() maps to
                        # stop == -1 for range())
Ellipsis
e = Ellipsis            # Assigns Ellipsis object (intended for extended slice syntax)
type(e)                 # Returns class with name 'ellipsis'; Ellipsis is the only instance
                        # of this class
isinstance(e, type(Ellipsis)) # Returns True
bool(e)                 # Returns True
e2 = ...                # Assigns Ellipsis object using alternative name '...'
e2 is e                 # Returns True (there is only one Ellipsis object)
NotImplemented
n = NotImplemented      # Assigns NotImplemented object (returned by methods such as
                        # __add__ when they can't handle the provided argument type; the
                        # interpreter will then try something else such as calling __radd__
                        # on the other operand)
type(n)                 # Returns class with name 'NotImplementedType'; NotImplemented is
                        # the only instance of this class
isinstance(n, type(NotImplemented)) # Returns True
bool(n)                 # Returns True
Object
o = object()            # Assigns a featureless object; object is the base of all
                        # classes/types (about classes/types and objects)
type(o)                 # Returns class object
isinstance(o, object)   # Returns True
isinstance(10, object)  # Returns True (int is a subclass of object)
Type
T = type('MyT', (), {}) # Assigns a type object; all classes/types are instances of type
                        # (about classes/types and objects)
type(T)                 # Returns class type
isinstance(T, type)     # Returns True
class T2: pass          # Assigns a type object using a class statement (normal approach)
type(T2)                # Returns class type

Basic Object Attributes

The following is a list of attributes (properties and methods) provided by instances of the basic types int, float, complex, bool, slice. (Some general object attributes and low-level attributes are omitted from this list).

Show attributes below:
o.__abs__()             # Special method implementing abs(o);
                        # applies to int,float,complex,bool
o.__add__(o2)           # Special method implementing o + o2;
                        # applies to int,float,complex,bool
o.__and__(o2)           # Special method implementing o & o2;
                        # applies to int,bool
o.__bool__()            # Special method implementing bool(o);
                        # applies to int,float,complex,bool
o.__ceil__()            # Special method implementing math.ceil(o);
                        # applies to int,float,bool
o.__complex__()         # Special method implementing complex(o);
                        # applies to complex
o.__divmod__(o2)        # Special method
                        # applies to int,float,bool
o.__eq__(o2)            # Special method
                        # applies to int,float,complex,bool,slice
o.__float__()           # Special method
                        # applies to int,float,bool
o.__floor__()           # Special method
                        # applies to int,float,bool
o.__floordiv__(o2)      # Special method
                        # applies to int,float,bool
o.__ge__(o2)            # Special method
                        # applies to int,float,bool,slice
o.__getformat__(t)      # TBD
                        # applies to float
o.__getnewargs__()      # TBD (pickle protocol)
                        # applies to int,float,complex,bool
o.__gt__(o2)            # Special method
                        # applies to int,float,bool,slice
o.__index__()           # Special method
                        # applies to int,bool
o.__int__()             # Special method
                        # applies to int,float,bool
o.__invert__()          # Special method
                        # applies to int,bool
o.__le__(o2)            # Special method
                        # applies to int,float,bool,slice
o.__lshift__(o2)        # Special method
                        # applies to int,bool
o.__lt__(o2)            # Special method
                        # applies to int,float,bool,slice
o.__mod__(o2)           # Special method
                        # applies to int,float,bool
o.__mul__(o2)           # Special method
                        # applies to int,float,complex,bool
o.__ne__(o2)            # Special method
                        # applies to int,float,complex,bool,slice
o.__neg__()             # Special method
                        # applies to int,float,complex,bool
o.__or__(o2)            # Special method
                        # applies to int,bool
o.__pos__()             # Special method
                        # applies to int,float,complex,bool
o.__pow__(o2)           # Special method
                        # applies to int,float,complex,bool
o.__radd__(o2)          # Special method
                        # applies to int,float,complex,bool
o.__rand__(o2)          # Special method
                        # applies to int,bool
o.__rdivmod__(o2)       # Special method
                        # applies to int,float,bool
o.__rfloordiv__(o2)     # Special method
                        # applies to int,float,bool
o.__rlshift__(o2)       # Special method
                        # applies to int,bool
o.__rmod__(o2)          # Special method
                        # applies to int,float,bool
o.__rmul__(o2)          # Special method
                        # applies to int,float,complex,bool
o.__ror__(o2)           # Special method
                        # applies to int,bool
o.__round__()           # Special method
                        # applies to int,float,bool
o.__rpow__(o2)          # Special method
                        # applies to int,float,complex,bool
o.__rrshift__(o2)       # Special method
                        # applies to int,bool
o.__rshift__(o2)        # Special method
                        # applies to int,bool
o.__rsub__(o2)          # Special method
                        # applies to int,float,complex,bool
o.__rtruediv__(o2)      # Special method
                        # applies to int,float,complex,bool
o.__rxor__(o2)          # Special method
                        # applies to int,bool
o.__sub__(o2)           # Special method
                        # applies to int,float,complex,bool
o.__truediv__(o2)       # Special method
                        # applies to int,float,complex,bool
o.__trunc__()           # Special method
                        # applies to int,float,bool
o.__xor__(o2)           # Special method
                        # applies to int,bool
o.as_integer_ratio()    # Returns 2-tuple of integers whose ratio is equal to o;
                        # (-1.5).as_integer_ratio() returns (-3,2); 0.1.as_integer_ratio()
                        # returns (3602879701896397, 36028797018963968)
                        # (but decimal.Decimal('0.1').as_integer_ratio() returns (1, 10));
                        # applies to int,float,bool
o.bit_count()           # Returns number of 1-bits in o; (10).bit_count() returns 2;
                        # applies to int,bool
o.bit_length()          # Returns minimum number of bits (disregarding sign) needed to
                        # represent o; (10).bit_length() returns 4;
                        # applies to int,bool
o.conjugate()           # Returns complex conjugate of o; (1+2j).conjugate() returns (1-2j);
                        # applies to int,float,complex,bool
o.denominator           # Is 1 (unless o is a fractions.Fraction);
                        # applies to int,bool
o.from_bytes(b,e,signed=s)  # Class method; returns object of same class as o converted
                        # from bytes object b with byte order e ('big', 'little',
                        # sys.byteorder) in two's complement if s is True (default False);
                        # int.from_bytes(b'\xfe\xff','little',signed=True) returns -2;
                        # applies to int,bool
o.fromhex(s)            # Class method; returns float value converted from hex number in
                        # string s; float.fromhex(' ff.8 ') returns 255.5;
                        # float.fromhex('0x1p10') returns 1024.0;
                        # applies to float
o.hex()                 # Returns hex string representing float o (in '0x#p#' format);
                        # 255.0.hex() returns '0x1.fe00000000000p+7';
                        # applies to float
o.imag                  # Imaginary part of o (which is 0 if o is not complex);
                        # (3+4j).imag returns 4; (3).imag returns 0; 4j.imag returns 4;
                        # applies to int,float,complex,bool
o.indices(n)            # Returns tuple of integers (start, stop, step) which can be passed
                        # to range() to get a sequence of indices corresponding to applying
                        # slice o to a sequence of length n;
                        # ii = slice(None,None,-1).indices(5) makes ii (4,-1,-1), and
                        # list(range(*ii)) returns [4,3,2,1,0];
                        # applies to slice
o.is_integer()          # Returns True if o is integral; 3.0.is_integer() returns True;
                        # applies to int,float,bool
o.numerator             # Is int(o) (unless o is a fractions.Fraction);
                        # applies to int,bool
o.real                  # Real part of o (which is o or int(o) if o is not complex);
                        # (3+4j).real returns 3; (3).real returns 3; 4j.real returns 0;
                        # applies to int,float,complex,bool
o.start                 # Read-only start value of slice o; slice(2,5).start returns 2;
                        # applies to slice
o.step                  # Read-only step value of slice o; slice(5,2,-1).step returns -1;
                        # applies to slice
o.stop                  # Read-only stop value of slice o; slice(5).stop returns 5;
                        # applies to slice
o.to_bytes(n,e,signed=s)# Returns n-byte bytes object representing o with byte order e
                        # ('big', 'little', sys.byteorder) and in two's complement if s is
                        # True (default False); raises OverflowError if o is out of range
                        # (-2).to_bytes(2,'little',signed=True) returns b'\xfe\xff';
                        # applies to int,bool

Container Types

# Container type detection
import collections.abc                  # Imports abstract base class module
isinstance(o, collections.abc.Container)# Returns True if o is container object (e.g. 'hi')

Sequence Types

Sequence Type Detection
import collections.abc                  # Imports abstract base class module
isinstance(o, collections.abc.Sequence) # Returns True if o is sequence object (e.g. 'hi')
String
s1 = 'Say "hi"\n\'ho\'' # Assigns a string (immutable): Say "hi"(newline)'ho'
s2 = "Say 'hi'\n\"ho\"" # Same as above, but with swapped single- and double-quotes
                        # (Single-quotes allow embedded unescaped double-quotes, and vice
                        # versa, otherwise no difference in behavior)
s3 = """Say "hi"
'ho'"""                 # Same as s1 above; triple-quotes allow embedded unescaped newlines
                        # (usually used for docstrings)
s4 = '''Say 'hi'
"ho"'''                 # Same as s2 above (but triple single-quotes are not often used)
s5 = r'\n\''            # Raw string of length 4: backslash, 'n', backslash, single-quote;
                        # backslashes are taken literally, but still escape quotes; often
                        # used for regular expressions
s6 = r"""\\
"""                     # Raw triple-quoted string of length 3: 2 backslashes and a newline
                        # (see s3 and s5 above)
s7 = '\xC6\u0E01\U0001F60E' # Assigns a string containing 3 Unicode characters; len(s7) is 3
s8 = r'\u0E01\n'        # \u escapes are not interpreted in raw strings; len(s8) is 8
                        # characters)
s9 = f'{2+5:03d}\n'     # Formatted string literal: '007'(newline)
s10 = fr'{2+5}\n'       # Formatted raw string literal: '7', backslash, 'n' (see s5 and s9
                        # above)
s11 = rf'{2+5}\n'       # Same as s10 above
u'hi'                   # Same as 'hi'; Python 3 ignores the u prefix (but ur'' and uf''
                        # are illegal)
'\a\b\f\n\r\t\v\'\"\\'  # Same as '\x07\x08\x0C\x0A\x0D\x09\x0B\x27\x22\x5C'
'\N{bel}\N{backspace}\N{form feed}\N{line feed}\N{carriage return}\
\N{horizontal tabulation}\N{vertical tabulation}\N{apostrophe}\N{quotation mark}\
\N{reverse solidus}'    # Same string as above (note that '\N{bel}' == '\x07', but
                        # '\N{bell}' == '\U0001F514')
'\0\7\10\011\3770'      # Octal escapes (max 3 digits); same as '\x00\x07\x08\x09\xFF\x30'
s = ('hel'              # Adjacent string literals are concatenated (s = 'hello');
    "lo")               # useful for splitting string literals and commenting each part
                        # (see statement boundaries for other ways of breaking strings)
type(s)                 # Returns class str
isinstance(s, str)      # Returns True
len(s)                  # Returns 5
'e' in s                # Returns True
s[0]                    # Returns 'h'
s[0] = 'a'              # ILLEGAL! Raises TypeError exception because string is immutable
s[-1]                   # Returns 'o'
s[1:3]                  # Returns 'el' (general slice syntax is [start:stop:step] with
                        # default values (if omitted) start=0, stop=end, step=1)
s[-2:]                  # Returns 'lo'
s[:-1]                  # Returns 'hell'
s[::-1]                 # Returns 'olleh'
s + ' there'            # Returns 'hello there'
s * 2                   # Returns 'hellohello'
str(4.1)                # Returns '4.1'
List
a1 = [3, 'hello']       # Assigns a list (mutable)
type(a1)                # Returns class list
isinstance(a1, list)    # Returns True
len(a1)                 # Returns 2
'hello' in a1           # Returns True
a1[-1]                  # Returns 'hello'
a1[0] = [4]             # Assigns list [4] to first item of list a1
a1.append('bye')        # a1 is now [[4], 'hello', 'bye']
a1.extend([10, 11])     # a1 is now [[4], 'hello', 'bye', 10, 11]
a1[-2:] = [9]           # a1 is now [[4], 'hello', 'bye', 9]
del a1[-2:]             # a1 is now [[4], 'hello']
a2 = a1                 # a2 now points to same list as a1
                        # (changes to a1 items affect a2 items)
a2 = a1[:]              # a2 is now a shallow copy of a1
                        # (changes to 1st level of a1 don't affect a2)
                        # (use a2 = copy.deepcopy(a1) to make a complete copy)
list('hello')           # Returns ['h', 'e', 'l', 'l', 'o']
Tuple
t = (3, 'hello')        # Assigns a tuple (immutable)
t = 3, 'hello'          # Same as above but using unenclosed expression list
type(t)                 # Returns class tuple
()                      # Returns () (empty tuple)
(4,)                    # Returns (4,) (single-item tuple)
(4)                     # Returns 4 (int, not tuple)
isinstance(t, tuple)    # Returns True
len(t)                  # Returns 2
'hello' in t            # Returns True
t[-1]                   # Returns 'hello'
t[-1] = 'a'             # ILLEGAL! Raises TypeError exception because tuple is immutable
tuple('hello')          # Returns ('h', 'e', 'l', 'l', 'o')
t2 = (4, [])            # Although tuples are immutable, they may contain mutable items
t2[-1].append(5)        # t2 is now (4, [5])
Namedtuple Types
import collections
NT = collections.namedtuple('Mynamedtuple', 'x,y,z')    # Assigns a new named tuple type;
                        # namedtuple itself is not a type but a function that makes a type
type(NT)                # Returns class type
nt = NT(4, 5, z=6)      # Assigns a named tuple of type NT (immutable; like tuple, but
                        # allows items to be accessed by name)
type(nt)                # Returns class with name 'Mynamedtuple'
isinstance(nt, NT)      # Returns True
nt.x                    # Returns 4 (unlike tuple)
nt[-1]                  # Returns 6 (like tuple)
5 in nt                 # Returns True (like tuple)
NT._make([6,7,8])       # Same as NT(6, 7, 8) or NT(*[6, 7, 8])
NT2 = collections.namedtuple('NT2', ('a','b','c'), defaults=(8,9))  # Assigns a new named
                        # tuple type with defaults for the last 2 items
nt2 = NT2(7)            # Assigns a named tuple of type NT2; values must be provided for
                        # all items that don't have defaults
nt2._asdict()           # Returns {'a':7, 'b':8, 'c':9}
Bytes
b1 = bytes(3)               # Assigns a bytes object b'\x00\x00\x00' (immutable)
b2 = bytes([65, 66, 67])    # Assigns a bytes object b'ABC' from a list (each value must be
                            # in the range 0-255)
b3 = bytes('\u0E01\u0E2E', 'utf_8')   # Assigns a bytes object b'\xe0\xb8\x81\xe0\xb8\xae'
                                      # from a string using the specified encoding
b4 = b'hi\n' + br'\n'       # Assigns a bytes object b'hi\n\\n' ('hi' + newline + backslash
                            # + 'n', see string type above) from ascii strings
list(b1)                    # Returns [0, 0, 0]
list(b2)                    # Returns [65, 66, 67]
list(b3)                    # Returns [224, 184, 129, 224, 184, 174]
list(b4)                    # Returns [104, 105, 10, 92, 110]
print(b2)                   # Prints "b'ABC'"
print(b2[0])                # Prints '65'
type(b1)                    # Returns class bytes
isinstance(b1, bytes)       # Returns True
len(b1)                     # Returns 3
len(b3)                     # Returns 6
b1 += b2                    # b1 is now b'\x00\x00\x00ABC' (b1 is reassigned, not mutated)
b2.decode()                 # Returns string 'ABC'
'ABC'.encode()              # Returns b'ABC'
Bytearray
b1 = bytearray(2)               # Assigns a bytearray (same as bytes, but mutable)
                                # containing 2 null bytes
b2 = bytearray([65, 66, 67])    # Assigns a bytearray from a list
                                # (each value must be in the range 0-255)
b3 = bytearray('\u0E01\u0E2E', 'utf_8')   # Assigns a bytearray from a string using the
                                          # specified encoding
b4 = bytearray(b'ABC')          # Assigns a bytearray from a bytes object (b4 == b2)
list(b1)                        # Returns [0, 0]
list(b2)                        # Returns [65, 66, 67]
list(b3)                        # Returns [224, 184, 129, 224, 184, 174]
print(b2)                       # Prints "bytearray(b'ABC')"
print(b2[0])                    # Prints '65'
type(b1)                        # Returns class bytearray
isinstance(b1, bytearray)       # Returns True
len(b1)                         # Returns 2
b1[0] = 72                      # b1 now contains 72, 0
b1[1:] = b'ey'                  # b1 now contains 72, 101, 121
b1 += b2                        # b1 now contains 72, 101, 121, 65, 66, 67
print(b1)                       # Prints "bytearray(b'HeyABC')"
Memoryview
b = bytes([4,5,6,7,8,9,254,255])    # Assigns a bytes object from a list
m = memoryview(b)       # Assigns a memoryview referencing object b
type(m)                 # Returns class memoryview
isinstance(m, memoryview)   # Returns True
m.readonly              # Returns True (because the referenced object b is immutable)
m.tolist()              # Returns [4, 5, 6, 7, 8, 9, 254, 255]
m[-1]                   # Returns 255 (last element)
m[1:4]                  # Returns a new memoryview referencing b[1:4]
m2 = m.cast('b', [2,4]) # New 2D memoryview referencing b (1-byte signed elements)
m2.tolist()             # Returns [[4, 5, 6, 7], [8, 9, -2, -1]]
m2[1,2]                 # Returns -2
m3 = m.cast('H')        # New memoryview referencing b (2-byte unsigned elements)
m3.tolist()             # Returns [1284, 1798, 2312, 65534] if platform is little-endian,
                        # corresponding to [0x0504, 0x0706, 0x0908, 0xfffe]
m4 = m.cast('i')        # New memoryview referencing b (4-byte signed elements)
m4.tolist()             # Returns [117835012, -128760] if platform is little-endian

ba = bytearray(b'hello')# Assigns a bytearray from a bytes object
m5 = memoryview(ba)     # Assigns a memoryview referencing object ba
m5.readonly             # Returns False (because the referenced object ba is mutable)
m5[1] = ord('u')        # ba is now bytearray(b'hullo')
Range
r = range(4)            # Assigns a range object (immutable) representing 0, 1, 2, 3 (from
                        # 0 to 4 (4 excluded)) without actually storing this sequence
type(r)                 # Returns class range
print(r)                # Prints 'range(0, 4)'; r is an unexpanded range object
print(list(r))          # Prints '[0, 1, 2, 3]'; list(r) expands r to a list
print(tuple(r))         # Prints '(0, 1, 2, 3)'; tuple(r) expands r to a tuple
isinstance(r, range)    # Returns True
len(r)                  # Returns 4
r[-1]                   # Returns 3
range(2, 5)             # Returns range object representing sequence 2, 3, 4 (from 2 to 5
                        # (5 excluded) with step 1)
range(1, -2, -1)        # Returns range object representing sequence 1, 0, -1 (from 1 to -2
                        # (-2 excluded) with step -1)

Other Container Types

Set
u = {3, 'hello'}        # Assigns a set (mutable; unordered unique items)
type(u)                 # Returns class set
set()                   # Returns empty set
{}                      # Returns empty dict, not set!
isinstance(u, set)      # Returns True
len(u)                  # Returns 2
'hello' in u            # Returns True
u.add(10)               # u is now {'hello', 10, 3} (undefined order)
u.remove(10)            # u is now {'hello', 3}
u.remove(10)            # Raises KeyError exception (no such item)
u.discard(3)            # u is now {'hello'}
u.discard(3)            # u is unchanged (did not contain 3)
set('hello')            # Returns {'l', 'o', 'h', 'e'} (only one 'l')
Frozenset
w = frozenset((3, 'hi'))    # Assigns a frozenset (like a set, but immutable, so can be
                            # used as a dictionary key or element of a set)
type(w)                     # Returns class frozenset
isinstance(w, frozenset)    # Returns True
Dict
d = {'a':10, 'b':5}     # Assigns a dictionary (mutable ordered mapping (preserves
                        # insertion order); unique keys; was unordered until Python 3.7)
type(d)                 # Returns class dict
{}                      # Returns {} (empty dict, not set)
isinstance(d, dict)     # Returns True
len(d)                  # Returns 2
'a' in d                # Returns True
d['a'] = 11             # d is now {'a': 11, 'b': 5}
dk = d.keys()           # Assigns a live view of d's keys
type(dk)                # Returns class with name 'dict_keys'
list(dk)                # Returns ['a', 'b']
dv = d.values()         # Assigns a live view of d's values
type(dv)                # Returns class with name 'dict_values'
list(dv)                # Returns [11, 5]
di = d.items()          # Assigns a live view of d's items
type(di)                # Returns class with name 'dict_items'
list(di)                # Returns [('a', 11), ('b', 5)]
del d['a']              # d is now {'b': 5}
list(dk)                # Returns ['b']
list(dv)                # Returns [5]
list(di)                # Returns [('b', 5)]
dict((('a',10),('b',5))) # Returns {'a': 10, 'b': 5}
dict(a=10, b=5)         # Returns {'a': 10, 'b': 5}
Defaultdict
import collections
dd = collections.defaultdict(int, {'a': 3}) # Assigns a defaultdict (same as dict, but the
                        # first arg is called with no args to provide the default value for
                        # nonexistent keys; here, int() returns the default value 0)
type(dd)                # Returns class collections.defaultdict
isinstance(dd, collections.defaultdict) # Returns True
dd['a']                 # Returns 3
dd['b']                 # Returns 0 (because that's what int() returns);
                        # dd is now defaultdict(int, {'a': 3, 'b': 0})
dd['c'] += 1            # dd is now defaultdict(int, {'a': 3, 'b': 0, 'c': 1})
OrderedDict
import collections
od = collections.OrderedDict([('a',10), ('b',11), ('c',12)]) # Assigns an OrderedDict (like
                            # dict but has more functionality and different performance)
type(od)                    # Returns class collections.OrderedDict
isinstance(od, collections.OrderedDict) # Returns True
od.move_to_end('a')         # od is now OrderedDict([('b',11), ('c',12), ('a',10)])
od.move_to_end('a',last=False)  # od is now OrderedDict([('a',10), ('b',11), ('c',12)])
od.popitem()                # Returns ('c',12); od is now OrderedDict([('a',10), ('b',11)])
od.popitem(last=False)      # Returns ('a',10); od is now OrderedDict([('b',11)])
Counter
import collections
c = collections.Counter('abaab')    # Assigns a Counter (like a dict, but the values are
                        # counts for the keys, i.e. {'a': 3, 'b': 2})
type(c)                 # Returns class collections.Counter
isinstance(c, collections.Counter)  # Returns True
c['a']                  # Returns 3
c['x']                  # Returns 0
c['c'] += 2             # c is now Counter({'a': 3, 'b': 2, 'c': 2})
c['b'] -= 6             # c is now Counter({'a': 3, 'b': -4, 'c': 2})
c.update(['a', 'c'])    # c is now Counter({'a': 4, 'b': -4, 'c': 3})
c.update({'a': 5})      # c is now Counter({'a': 9, 'b': -4, 'c': 3})
c.most_common(2)        # Returns [('a', 9), ('c', 3)] (2 most common items ordered with
                        # most common first)
c.most_common()         # Returns [('a', 9), ('c', 3), ('b', -4)] (all items ordered with
                        # most common first)
c + collections.Counter({'a':-1, 'b':4})    # Returns Counter({'a': 8, 'c': 3}); items with
                        # non-positive counts are discarded after arithmetic/set operations
                        # on Counter objects
Deque (double-ended queue)
import collections
q = collections.deque() # Assigns a deque (a bit like list but with faster adding/removing
                        # of items at both ends and slower indexing in the middle)
type(q)                 # Returns class collections.deque
isinstance(q, collections.deque)    # Returns True
q = collections.deque((3, 'hi'), maxlen=4)  # Assigns a new deque containing items 3 and
                        # 'hi' and restricted to max 4 items
q.append('right')       # q is now deque([3, 'hi', 'right'])
q.appendleft('left')    # q is now deque(['left', 3, 'hi', 'right'])
q.append('R')           # q is now deque([3, 'hi', 'right', 'R']); 'left' got pushed out
                        # due to maxlen=4
q.appendleft('left')    # q is now deque(['left', 3, 'hi', 'right']); 'R' got pushed out
                        # due to maxlen=4
if q:                   # If q is not empty...
    q.pop()             # Returns 'right'; q is now deque(['left', 3, 'hi'])
q.popleft()             # Returns 'left'; q is now deque([3, 'hi'])
q.extend([4,5,6])       # q is now deque(['hi', 4, 5, 6]); 3 got pushed out due to maxlen=4
q.extendleft([2,1])     # q is now deque([1, 2, 'hi', 4]); 5 and 6 got pushed out
q.rotate(-1)            # q is now deque([2, 'hi', 4, 1]); items rotated 1 place left

Container Object Attributes

The following is a list of attributes (properties and methods) provided by instances of the container types str, list, tuple, (types produced by) collections.namedtuple, bytes, bytearray, memoryview, range, set, frozenset, dict, collections.defaultdict, collections.OrderedDict, collections.Counter, collections.deque. (Some general object attributes and low-level attributes are omitted from this list).

Show attributes below:
o.__add__(o2)           # Special method implementing o + o2 (concatenation); 'hi' + 'ho'
                        # returns 'hiho'; for Counter the counts of equal keys are added
                        # (and then items with non-positive counts are removed);
                        # applies to str,list,tuple,namedtuple,bytes,bytearray,Counter,
                        # deque
o.__alloc__()           # Returns number of bytes allocated to o;
                        # bytearray(b'hi').__alloc__() returns 3;
                        # applies to bytearray
o.__and__(o2)           # Implements o & o2 (intersection); {1,'a',3} & {'a',3,4} returns
                        # {'a',3}; for Counter the intersection applies to the keys and the
                        # smallest count is chosen for equal keys (and then items with non-
                        # positive counts are removed);
                        # applies to set,frozenset,Counter
o.__bool__()            # TBD
                        # applies to range
o.__buffer__(flags)     # See buffer protocol special methods;
                        # applies to bytes,bytearray,memoryview
o.__bytes__()           # TBD
                        # applies to bytes
o.__class_getitem__(x)  # TBD
                        # applies to list,tuple,namedtuple,set,frozenset,dict,defaultdict,
                        # OrderedDict,Counter,deque
o.__contains__(x)       # Implements x in o (membership test); 'b' in 'abc' returns True;
                        # applies to str,list,tuple,namedtuple,bytes,bytearray,range,set,
                        # frozenset,dict,defaultdict,OrderedDict,Counter,deque
o.__copy__()            # Implements copy.copy(o) (shallow copy);
                        # applies to defaultdict,deque
o.__delitem__(x)        # Implements del o[x] (item deletion); if o = [4,5] then del o[0]
                        # makes o [5]; o.__delitem__(slice(x,y,z)) implements del o[x:y:z];
                        # applies to list,bytearray,dict,defaultdict,OrderedDict,Counter,
                        # deque
o.__enter__()           # TBD
                        # applies to memoryview
o.__eq__(o2)            # Implements o == o2;
                        # applies to str,list,tuple,namedtuple,bytes,bytearray,memoryview,
                        # range,set,frozenset,dict,defaultdict,OrderedDict,Counter,deque
o.__exit__()            # TBD
                        # applies to memoryview
o.__ge__(o2)            # Implements o >= o2;
                        # applies to str,list,tuple,namedtuple,bytes,bytearray,set,
                        # frozenset,Counter,deque
o.__getitem__(x)        # Implements o[x];
                        # o.__getitem__(slice(x,y,z)) implements o[x:y:z];
                        # applies to str,list,tuple,namedtuple,bytes,bytearray,memoryview,
                        # range,dict,defaultdict,OrderedDict,Counter,deque
o.__getnewargs__()      # TBD (pickle protocol)
                        # applies to str,tuple,namedtuple,bytes
o.__gt__(o2)            # Implements o > o2;
                        # applies to str,list,tuple,namedtuple,bytes,bytearray,set,
                        # frozenset,Counter,deque
o.__iadd__(o2)          # Implements o += o2 (in-place concatenation); if o = [1,2] then
                        # o += [3,4] makes o [1,2,3,4];
                        # applies to list,bytearray,Counter,deque
o.__iand__(o2)          # Implements o &= o2 (in-place intersection);
                        # applies to set,Counter
o.__imul__(x)           # Implements o *= x (in-place repetition); if o = [1,2] then o *= 3
                        # makes o [1,2,1,2,1,2];
                        # applies to list,bytearray,deque
o.__ior__(o2)           # Implements o |= o2 (in-place union);
                        # applies to set,dict,defaultdict,OrderedDict,Counter
o.__isub__(o2)          # Implements o -= o2 (in-place difference);
                        # applies to set,Counter
o.__iter__()            # Implements iter(o); returns an iterator for iterable o;
                        # applies to str,list,tuple,namedtuple,bytes,bytearray,memoryview,
                        # range,set,frozenset,dict,defaultdict,OrderedDict,Counter,deque
o.__ixor__(o2)          # Implements o ^= o2 (in-place symmetric difference);
                        # applies to set
o.__le__(o2)            # Implements o <= o2;
                        # applies to str,list,tuple,namedtuple,bytes,bytearray,set,
                        # frozenset,Counter,deque
o.__len__()             # Implements len(o) (item count); len([5,6]) returns 2;
                        # applies to str,list,tuple,namedtuple,bytes,bytearray,memoryview,
                        # range,set,frozenset,dict,defaultdict,OrderedDict,Counter,deque
o.__lt__(o2)            # Implements o < o2;
                        # applies to str,list,tuple,namedtuple,bytes,bytearray,set,
                        # frozenset,Counter,deque
o.__match_args__        # TBD
                        # applies to namedtuple
o.__missing__(x)        # Implements o[x] if x is not one of o's keys; returns the default
                        # value for a missing key;
                        # applies to defaultdict,Counter
o.__mod__(x)            # Implements o % x (formatting); '%-3s%03u' % ('hi', 9) returns
                        # 'hi 009';
                        # applies to str,bytes,bytearray
o.__mul__(x)            # Implements o * x (repetition); [1,2] * 3 returns [1,2,1,2,1,2];
                        # applies to str,list,tuple,namedtuple,bytes,bytearray,deque
o.__ne__(o2)            # Implements o != o2;
                        # applies to str,list,tuple,namedtuple,bytes,bytearray,memoryview,
                        # range,set,frozenset,dict,defaultdict,OrderedDict,Counter,deque
o.__neg__()             # Implements -o (negation); for Counter each count is negated (and
                        # then items with non-positive counts are removed);
                        # applies to Counter
o.__or__(o2)            # Implements o | o2 (union);
                        # {1,'a',3} | {'a',3,4} returns {1,'a',3,4};
                        # {'x':2,'y':3} | {'x':1,'z':4} returns {'x':1,'y':3,'z':4};
                        # for Counter the union applies to the keys and the largest count
                        # is chosen for equal keys (and then items with non-positive counts
                        # are removed);
                        # applies to set,frozenset,dict,defaultdict,OrderedDict,Counter
o.__pos__()             # Implements +o; for Counter this removes items with non-positive
                        # counts;
                        # applies to Counter
o.__rand__(o2)          # Implements o2 & o (reverse intersection);
                        # applies to set,frozenset
o.__release_buffer__(b) # See buffer protocol special methods;
                        # applies to bytearray,memoryview
o.__reversed__()        # Implements reversed(o); returns a reverse order iterator for o;
                        # applies to list,range,dict,defaultdict,OrderedDict,Counter,deque
o.__rmod__(o2)          # Implements o2 % o (reverse formatting);
                        # applies to str,bytes,bytearray
o.__rmul__(x)           # Implements x * o (reverse repetition); 3 * [1,2] returns
                        # [1,2,1,2,1,2];
                        # applies to str,list,tuple,namedtuple,bytes,bytearray,deque
o.__ror__(o2)           # Implements o2 | o (reverse union);
                        # applies to set,frozenset,dict,defaultdict,OrderedDict,Counter
o.__rsub__(o2)          # Implements o2 - o (reverse difference);
                        # applies to set,frozenset
o.__rxor__(o2)          # Implements o2 ^ o (reverse symmetric difference);
                        # applies to set,frozenset
o.__setitem__(x, v)     # Implements o[x] = v;
                        # o.__setitem__(slice(x,y,z), v) implements o[x:y:z] = v;
                        # applies to list,bytearray,memoryview,dict,defaultdict,
                        # OrderedDict,Counter,deque
o.__sub__(o2)           # Implements o - o2 (difference); {1,'a',3} - {'a',3,4} returns {1};
                        # for Counter the counts of equal keys are subtracted (and then
                        # items with non-positive counts are removed);
                        # applies to set,frozenset,Counter
o.__xor__(o2)           # Implements o ^ o2 (symmetric difference); {1,'a',3} ^ {'a',3,4}
                        # returns {1,4};
                        # applies to set,frozenset
o._asdict()             # Returns a dict mapping o's field names to values; if o =
                        # namedtuple('NT', 'x,y')(3,4) then o._asdict() returns
                        # {'x':3, 'y':4};
                        # applies to namedtuple
o._field_defaults       # Dict mapping o's field names to default values; if o =
                        # namedtuple('NT', 'x,y,z', defaults=(2,3)) then o._field_defaults
                        # is {'y':2, 'z':3};
                        # applies to namedtuple
o._fields               # Tuple of o's field names; if o = namedtuple('NT', 'x,y') then
                        # o._fields is ('x', 'y');
                        # applies to namedtuple
o._make(o2)             # Class method; returns a new object of the same class as o and
                        # initializes the new object with the values in o2; if o =
                        # namedtuple('NT', 'x,y') then o._make([3,4]) returns a new named
                        # tuple o(x=3, y=4) - same as o(*[3,4]);
                        # applies to namedtuple
o._replace(**kwargs)    # Returns a copy of o with new values for the specified fields;
                        # if NT = namedtuple('NT', 'x,y,z') and o = NT(3,4,5) then
                        # o._replace(x=6,z=2) returns a new named tuple NT(6,4,2);
                        # applies to namedtuple
o.add(x)                # Puts x into o; if o = {3} then o.add(5) makes o {3,5};
                        # applies to set
o.append(x)             # Inserts x at end of o; if o = [3] then o.append(5) makes o [3,5];
                        # applies to list,bytearray,deque
o.appendleft(x)         # Inserts x at start of o; if o = deque([3]) then o.appendleft(5)
                        # makes o deque([5, 3]);
                        # applies to deque
o.c_contiguous          # Read-only bool which is True if o's memory layout is contiguous
                        # and C-style (last index of array increments fastest); always True
                        # if o is 1-dimensional and contiguous; if o =
                        # memoryview(b'abcd').cast('B',[2,2]) then o.c_contiguous is True;
                        # applies to memoryview
o.capitalize()          # Returns a copy of o with first character capitalized;
                        # 'hi ho'.capitalize() returns 'Hi ho';
                        # applies to str,bytes,bytearray
o.casefold()            # Returns a copy of o suitable for caseless comparisons;
                        # 'Heiß'.casefold() returns 'heiss';
                        # applies to str
o.cast(f,s)             # Returns a new memoryview of o's buffer with format f and shape s;
                        # if o = memoryview(bytes([1,2,3,255])) then
                        # o.cast('B', [2,2]).tolist() returns [[1,2], [3,255]] (unsigned),
                        # o.cast('h').tolist() returns [513, -253] (16-bit signed int),
                        # o.cast('i').tolist() returns [-16580095] (32-bit signed int)
                        # (last 2 examples assume little-endian platform);
                        # applies to memoryview
o.center(w,c)           # Returns a copy of o centered in a string of length w filled with
                        # c (space if no c); returns an unmodified copy if len(o) >= w;
                        # 'hi'.center(5,'*') returns '**hi*';
                        # applies to str,bytes,bytearray
o.clear()               # Makes o empty; if o = {3:9,5:25} then o.clear() makes o {};
                        # applies to list,bytearray,set,dict,defaultdict,OrderedDict,
                        # Counter,deque
o.contiguous            # Read-only bool which is True if o's memory layout is contiguous
                        # (the numpy module supports non-contiguous arrays);
                        # applies to memoryview
o.copy()                # Returns a shallow copy of o (similar to copy.copy(o) but doesn't
                        # call o.__copy__()); if o = {4:[5]} then o.copy() returns a new
                        # {4:[5]} referencing the same list [5] as o;
                        # applies to list,bytearray,set,frozenset,dict,defaultdict,
                        # OrderedDict,Counter,deque
o.count(x)              # Returns count of non-overlapping occurrences of x in o;
                        # 'hohohoho'.count('hoho') returns 2;
                        # applies to str,list,tuple,namedtuple,bytes,bytearray,range,deque
o.count(x,a,b)          # Returns count of non-overlapping occurrences of x in o[a:b];
                        # a,b are optional; 'hohohoho'.count('hoho',1) returns 1;
                        # 'hohohoho'.count('hoho',0,7) returns 1;
                        # applies to str,bytes,bytearray
o.decode(enc,err)       # Returns string equal to o decoded using encoding enc
                        # (default 'utf-8') and error scheme err (default 'strict')
                        # b'A\xc7\xbf'.decode() returns 'A\u01ff';
                        # b'A\xc0'.decode() raises UnicodeDecodeError;
                        # b'A\xc0'.decode('utf-8','ignore') returns 'A';
                        # b'A\xc0'.decode('utf-8','replace') returns 'A\ufffd';
                        # applies to bytes,bytearray
o.default_factory       # Function called by o to get default value for nonexistent keys;
                        # if o = defaultdict(int) then o.default_factory returns class int,
                        # o[5] returns 0 (from int()) and makes o defaultdict(int, {5:0}),
                        # o.default_factory = str makes o defaultdict(str, {5:0}), and o[6]
                        # returns '' (str()) and makes o defaultdict(str, {5:0, 6:''});
                        # applies to defaultdict
o.difference(o2,...)        # Same as o - set(o2) - ... (but doesn't call o.__sub__());
                            # {1,2,3,4}.difference({2,6},[7,3]) returns {1,4};
                            # applies to set,frozenset
o.difference_update(o2,...) # Same as o -= set(o2) | ... (but doesn't call o.__isub__());
                            # if o = {1,2,3,4} then o.difference_update({2,6},[7,3]) makes
                            # o {1,4};
                            # applies to set
o.discard(x)            # Removes item x from o; does nothing if x is not in o;
                        # if o = {3,5} then o.discard(5) makes o {3};
                        # applies to set
o.elements()            # Returns an iterator which returns each item of o repeated
                        # according to its count (items with non-positive counts are
                        # excluded); if o = Counter({'a': 4, 'b': -4, 'c': 3}) or o =
                        # Counter('acaacca') then ''.join(o.elements()) returns 'aaaaccc';
                        # applies to Counter
o.encode(enc,err)       # Returns bytes object equal to o encoded using encoding enc
                        # (default 'utf-8') and error scheme err (default 'strict')
                        # 'A\u01ff'.encode() returns b'A\xc7\xbf';
                        # 'A\u0080'.encode('ascii') raises UnicodeEncodeError;
                        # 'A\u0080'.encode('ascii','ignore') returns b'A';
                        # 'A\u0080'.encode('ascii','replace') returns b'A?';
                        # 'A\u0080'.encode('ascii','xmlcharrefreplace') returns b'A&#128;';
                        # 'A\u0080'.encode('ascii','backslashreplace') returns b'A\\x80';
                        # applies to str
o.endswith(x,a,b)       # Returns True if o[a:b] ends with x (or with an item in tuple x);
                        # 'abc'.endswith(('bc','z')) returns True;
                        # applies to str,bytes,bytearray
o.expandtabs(n)         # Returns a copy of o with tab characters replaced by up to n
                        # spaces (enough spaces to reach the next tab column);
                        # '\ta\tbc\td'.expandtabs(3) returns '   a  bc d';
                        # applies to str,bytes,bytearray
o.extend(o2)            # Same as o += o2 (but doesn't call o.__iadd__()); if o = [3,5]
                        # then o.extend([1,2]) makes o [3,5,1,2];
                        # applies to list,bytearray,deque
o.extendleft(o2)        # Inserts all items from o2 (in reverse order) at start of o; if o
                        # = deque([3,5]) then o.extendleft([1,2]) makes o deque([2,1,3,5]);
                        # applies to deque
o.f_contiguous          # Read-only bool which is True if o's memory layout is contiguous
                        # and Fortran-style (first index of array increments fastest; the
                        # numpy module supports Fortran-style arrays);
                        # always True if o is 1-dimensional and contiguous; if o =
                        # memoryview(b'abcd').cast('B',[2,2]) then o.f_contiguous is False;
                        # applies to memoryview
o.find(o2,a,b)          # Returns index of first occurrence of substring o2 in o[a:b] (a=0,
                        # b=len(o) if not given), or -1 if none found;
                        # 'abab'.find('ab') returns 0; 'abab'.find('ab',1,3) returns -1;
                        # applies to str,bytes,bytearray
o.format(...)           # Returns a string representing the arguments formatted according
                        # to the directives in o; see string formatting;
                        # '{:03d}{:4.1f}{x:>2.1s}'.format(6,7,x='AB') returns '006 7.0 A';
                        # applies to str
o.format_map(o2)        # Returns a string representing the values of dict o2 formatted
                        # according to the directives in o (which refer to o2's keys);
                        # '{x:03d}{y:4.1f}'.format_map(dict(x=6,y=7)) returns '006 7.0';
                        # applies to str
o.fromhex(s)            # Class method; returns object of same class as o containing bytes
                        # corresponding to pairs of hex digits in string s;
                        # bytes.fromhex('4142 43 0a') returns b'ABC\n';
                        # applies to bytes,bytearray
o.fromkeys(o2,x)        # Class method; returns object of same class as o containing all
                        # items of sequence o2 as keys with all values equal to x (default
                        # None); dict.fromkeys(('a','b'), 5) returns {'a':5, 'b':5};
                        # applies to dict,defaultdict,OrderedDict
o.get(k,d)              # Returns o[k], or d if k is not in o (d is None if omitted);
                        # {'a':6}.get('a',5) returns 6, {'a':6}.get('b',5) returns 5, and
                        # {'a':6}.get('b') returns None;
                        # applies to dict,defaultdict,OrderedDict,Counter
o.hex(sep,n)            # Returns string containing pairs of hex digits corresponding to
                        # bytes in o, with separator sep inserted every n bytes from the
                        # right (or left if n < 0); raises ValueError if sep is given and
                        # not a single character; sep == '' and n == 1 if not given;
                        # b'ABC\n'.hex() returns '4142430a';
                        # b'ABC'.hex(':') returns '41:42:43'
                        # b'ABC'.hex(':',2) returns '41:4243'
                        # b'ABC'.hex(':',-2) returns '4142:43'
                        # applies to bytes,bytearray,memoryview
o.index(x)              # Returns index of first occurrence of item x in o; raises
                        # ValueError if x is not in o;
                        # for str,bytes,bytearray: x may be a substring of o;
                        # [4,5,4].index(4) returns 0; 'abab'.index('ba') returns 1;
                        # applies to str,list,tuple,namedtuple,bytes,bytearray,range,deque
o.index(x,a,b)          # Returns index of first occurrence of item x in o[a:b] (a=0,
                        # b=len(o) if not given); raises ValueError if x is not in o[a:b];
                        # for str,bytes,bytearray: x may be a substring of o;
                        # [4,5,4].index(4) returns 0; 'abab'.index('ab',1) returns 2;
                        # applies to str,list,tuple,namedtuple,bytes,bytearray,deque
o.insert(i,x)           # Inserts item x in o at index i; same as o[i:i] = [x];
                        # if o = [4,5,6] then o.insert(2,'a') makes o [4,5,'a',6];
                        # applies to list,bytearray,deque
o.intersection(o2,...)        # Same as o & set(o2) & ... (but doesn't call o.__and__());
                              # {1,2,3,4}.intersection({3,2,6},[7,3]) returns {3};
                              # applies to set,frozenset
o.intersection_update(o2,...) # Same as o &= set(o2) & ... (but doesn't call o.__iand__());
                              # if o = {1,2,3,4} then o.intersection_update({2,6},[7,3])
                              # makes o {3};
                              # applies to set
o.isalnum()             # Returns True if o is not empty and contains only alphanumeric
                        # characters (alphabetic or numeric characters);
                        # for characters 'Aªข': 'A\u00aa\u0e02'.isalnum() returns True;
                        # for characters '2๒':  '2\u0e52'.isalnum() returns True;
                        # for character '²':    '\u00b2'.isalnum() returns True;
                        # for character '½':    '\u00bd'.isalnum() returns True;
                        # applies to str,bytes,bytearray
o.isalpha()             # Returns True if o is not empty and contains only alphabetic
                        # characters;
                        # for characters 'Aªข': 'A\u00aa\u0e02'.isalpha() returns True;
                        # '2'.isalpha() returns False; '@'.isalpha() returns False;
                        # applies to str,bytes,bytearray
o.isascii()             # Returns True if o does not contain non-ASCII characters
                        # (characters above '\x7F'); '\x00\x7f'.isascii() returns True;
                        # '\x80'.isascii() returns False; ''.isascii() returns True;
                        # applies to str,bytes,bytearray
o.isdecimal()           # Returns True if o is not empty and contains only decimal
                        # characters (a subset of digits);
                        # for characters '2๒': '2\u0e52'.isdecimal() returns True;
                        # for character '²':   '\u00b2'.isdecimal() returns False;
                        # for character '½':   '\u00bd'.isdecimal() returns False;
                        # applies to str
o.isdigit()             # Returns True if o is not empty and contains only digits
                        # (a subset of numeric characters);
                        # for characters '2๒': '2\u0e52'.isdigit() returns True;
                        # for character '²':   '\u00b2'.isdigit() returns True;
                        # for character '½':   '\u00bd'.isdigit() returns False;
                        # applies to str,bytes,bytearray
o.isdisjoint(o2)        # Returns True if o and o2 have no common items;
                        # {1,3}.isdisjoint([4,2]) returns True; {'A'}.isdisjoint('ABC')
                        # returns False;
                        # applies to set,frozenset
o.isidentifier()        # Returns True if o fulfills the rules for being a valid Python
                        # identifier; 'aBc_2'.isidentifier() returns True;
                        # applies to str
o.islower()             # Returns True if o contains at least 1 cased character and all
                        # cased characters are lowercase; 'ver2.0'.islower() returns True;
                        # applies to str,bytes,bytearray
o.isnumeric()           # Returns True if o is not empty and contains only numeric
                        # characters;
                        # for characters '2๒': '2\u0e52'.isnumeric() returns True;
                        # for character '²':   '\u00b2'.isnumeric() returns True;
                        # for character '½':   '\u00bd'.isnumeric() returns True;
                        # applies to str
o.isprintable()         # Returns True if o doesn't contain non-printable characters
                        # (separators other than the space character are considered non-
                        # printable in this context); 'hi ho'.isprintable() returns True;
                        # '\n'.isprintable() returns False; ''.isprintable() returns True;
                        # applies to str
o.isspace()             # Returns True if o is not empty and contains only whitespace
                        # characters; ' \t\n\r\f\v'.isspace() returns True;
                        # applies to str,bytes,bytearray
o.issubset(o2)          # Same as o <= o2 (but doesn't call o.__le__());
                        # {4,2}.issubset({1,2,3,4}) returns True;
                        # {5,2}.issubset({1,2,3,4}) returns False;
                        # applies to set,frozenset
o.issuperset(o2)        # Same as o >= o2 (but doesn't call o.__ge__());
                        # {1,2,3,4}.issuperset({4,2}) returns True;
                        # {1,2,3,4}.issuperset({5,2}) returns False;
                        # applies to set,frozenset
o.istitle()             # Returns True if o contains at least 1 uppercase character, no
                        # uppercase character follows a cased character, and any lowercase
                        # character follows a cased character;
                        # '2B|Not 2B'.istitle() returns True;
                        # applies to str,bytes,bytearray
o.isupper()             # Returns True if o contains at least 1 cased character and all
                        # cased characters are uppercase; 'VER2.0'.isupper() returns True;
                        # applies to str,bytes,bytearray
o.items()               # Returns a live view of o's items; if o = {4:5,6:7} and v =
                        # o.items() then list(v) returns [(4,5),(6,7)] and v will follow
                        # changes to o;
                        # applies to dict,defaultdict,OrderedDict,Counter
o.itemsize              # Read-only size in bytes of each item in o; if o =
                        # memoryview(array.array('h', [4,5,6])) then o.itemsize is 2;
                        # applies to memoryview
o.join(o2)              # Returns the object obtained by concatenating all items of o2
                        # using o as separator; '/'.join('abc') returns 'a/b/c';
                        # b'=='.join([b'x',b'42']) returns b'x==42';
                        # applies to str,bytes,bytearray
o.keys()                # Returns a live view of o's keys; if o = {4:5,6:7} and v = o.keys()
                        # then list(v) returns [4,6] and v will follow changes to o;
                        # applies to dict,defaultdict,OrderedDict,Counter
o.ljust(w,c)            # Returns a copy of o left-justified in a string of length w filled
                        # with c (space if no c); returns an unmodified copy if len(o) >= w;
                        # 'hi'.ljust(5,'*') returns 'hi***';
                        # applies to str,bytes,bytearray
o.lower()               # Returns a copy of o with all uppercase characters converted to
                        # lowercase; '2B|Not 2B'.lower() returns '2b|not 2b';
                        # applies to str,bytes,bytearray
o.lstrip(s)             # Returns a copy of o with first characters removed if present in
                        # string s (whitespace if no s); 'abcd'.lstrip('dba') returns 'cd';
                        # ' \t\na '.lstrip() returns 'a ';
                        # applies to str,bytes,bytearray
o.maketrans(d)          # Static method; returns dict d converted to a dict for use with
                        # o.translate(); str.maketrans({'A':'B'}) returns {65:'B'};
                        # applies to str
o.maketrans(f,t)        # Static method; returns a table for use with o.translate() mapping
                        # each character in f to the corresponding character in t;
                        # str.maketrans('AB', 'CD') returns {65:67, 66:68};
                        # bytes.maketrans(b'AB', b'CD') returns a 256-byte bytes object
                        # with each byte value equal to its index, except the bytes at
                        # indices 65 and 66 have values 67 and 68 instead of 65 and 66;
                        # applies to str,bytes,bytearray
o.maketrans(f,t,tnone)  # Static method; returns a dict for use with o.translate() mapping
                        # each character in f to the corresponding character in t, and each
                        # character in tnone to None;
                        # str.maketrans('AB', 'CD', 'E') returns {65:67, 66:68, 69:None};
                        # applies to str
o.maxlen                # Read-only maximum length of o (None if o has no maximum length);
                        # deque().maxlen returns None; deque(maxlen=5).maxlen returns 5;
                        # applies to deque
o.most_common(n)        # Returns a list of o's items and counts sorted by decreasing count;
                        # if n is given, the list only includes the n items with largest
                        # counts; if o = Counter({'a': 2, 'b': -4, 'c': 3}) then
                        # o.most_common() returns [('c', 3), ('a', 2), ('b', -4)], and
                        # o.most_common(1) returns [('c', 3)];
                        # applies to Counter
o.move_to_end(key,last) # Moves key to start (if last=False) or end (if last=True) of o;
                        # default is end if last is not given;
                        # if o = OrderedDict(a=4,b=5,c=6) then:
                        #   o.move_to_end(key='b')        makes o OrderedDict(a=4,c=6,b=5);
                        #   o.move_to_end('b',True)       makes o OrderedDict(a=4,c=6,b=5);
                        #   o.move_to_end('b',last=False) makes o OrderedDict(b=5,a=4,c=6);
                        # applies to OrderedDict
o.nbytes                # Read-only number of bytes in o (excl. gaps between non-contiguous
                        # parts); if o = memoryview(array.array('h', [1,2,3])) then
                        # o.nbytes is 6 (on platforms where a C short is 2 bytes);
                        # applies to memoryview
o.ndim                  # Read-only number of dimensions in o; if o =
                        # memoryview(b'abcd').cast('B',[1,4]) then o.ndim is 2 (1 row in
                        # the 1st dimension and 4 columns in the 2nd);
                        # applies to memoryview
o.obj                   # Read-only reference to o's underlying object; if b = bytearray()
                        # and o = memoryview(b) then o.obj is b;
                        # applies to memoryview
o.partition(o2)         # Returns (o[:i],o[i],o[i+1:]) where i is index of first occurrence
                        # of o2 in o, or (o[:],type(o)(),type(o)()) if o2 is not found in o;
                        # 'abba'.partition('b') returns ('a','b','ba');
                        # b'abba'.partition(b'x') returns (b'abba',b'',b'');
                        # applies to str,bytes,bytearray
o.pop()                 # Removes and returns last item in o (arbitrary item if o is a set);
                        # raises KeyError (set) or IndexError (others) if no item;
                        # if o = [4,5,6] then o.pop() returns 6 and makes o [4,5];
                        # applies to list,bytearray,set,deque
o.pop(i)                # Removes and returns item o[i]; raises IndexError (list, bytearray)
                        # or KeyError (others) if no item; if o = [4,5,6] then o.pop(1)
                        # returns 5 and makes o [4,6]; if o = {4:9,5:3,6:9} then o.pop(5)
                        # returns 3 and makes o {4:9,6:9};
                        # applies to list,bytearray,dict,defaultdict,OrderedDict,Counter
o.popitem()             # Removes and returns last added (key, o[key]) pair; raises
                        # KeyError if o is empty; if o = {3:9,5:25} then o.popitem() makes
                        # o {3:9} and returns (5,25);
                        # applies to dict,defaultdict,OrderedDict,Counter
o.popleft()             # Removes and returns first item in o; raises IndexError if no item;
                        # if o = deque([3,5]) then o.popleft() returns 3 and makes o
                        # deque([5]);
                        # applies to deque
o.readonly              # Read-only bool which is True if o is read-only;
                        # if o = memoryview(b'abc') then o.readonly is True;
                        # if o = memoryview(bytearray(3)) then o.readonly is False;
                        # applies to memoryview
o.release()             # Releases o's underlying object so it can no longer be accessed
                        # via o; if o = memoryview(b'abc') and o.release() is called then
                        # o.obj raises ValueError, and so does o[0], but o.release() may be
                        # called any number of times;
                        # applies to memoryview
o.remove(x)             # Removes item x from o; raises ValueError (KeyError for set) if x
                        # is not in o; if o = [3,5] then o.remove(5) makes o [3];
                        # applies to list,bytearray,set,deque
o.removeprefix(o2)      # Returns a copy of o with o2 removed if present at start of o;
                        # 'abcd'.removeprefix('ab') returns 'cd';
                        # 'abcd'.removeprefix('bc') returns 'abcd';
                        # applies to str,bytes,bytearray
o.removesuffix(o2)      # Returns a copy of o with o2 removed if present at end of o;
                        # 'abcd'.removesuffix('cd') returns 'ab';
                        # 'abcd'.removesuffix('bc') returns 'abcd';
                        # applies to str,bytes,bytearray
o.replace(s1,s2,n)      # Returns a copy of o with first n (or all if no n) occurrences of
                        # s1 replaced with s2; 'boohoo'.replace('oo','*') returns 'b*h*';
                        # 'boohoo'.replace('oo','*',1) returns 'b*hoo';
                        # applies to str,bytes,bytearray
o.reverse()             # Reverses the items of o; same as o[::-1] = o;
                        # if o = [4,5,6] then o.reverse() makes o [6,5,4];
                        # applies to list,bytearray,deque
o.rfind(o2,a,b)         # Same as o.find(), except last occurrence is chosen;
                        # 'abab'.rfind('ab') returns 2; 'abab'.rfind('ab',1,3) returns -1;
                        # applies to str,bytes,bytearray
o.rindex(x,a,b)         # Same as o.index(), except last occurrence is chosen;
                        # 'abab'.rindex('ab') returns 2; 'abab'.rindex('ab',0,3) returns 0;
                        # applies to str,bytes,bytearray
o.rjust(w,c)            # Returns a copy of o right-justified in a string of length w
                        # filled with c (space if no c); returns an unmodified copy if
                        # len(o) >= w; 'hi'.rjust(5,'*') returns '***hi';
                        # applies to str,bytes,bytearray
o.rotate(n)             # Rotates o's items n places to the right (left if n < 0; 1 place
                        # right if no n); if o = deque([1,2,3,4]) then o.rotate() makes o
                        # deque([4,1,2,3], o.rotate(-1) makes o deque([1,2,3,4]) again, and
                        # o.rotate(3) makes o deque([2,3,4,1]);
                        # applies to deque
o.rpartition(o2)        # Returns (o[:i],o[i],o[i+1:]) where i is index of last occurrence
                        # of o2 in o, or (type(o)(),type(o)(),o[:]) if o2 is not found in o;
                        # 'abba'.rpartition('b') returns ('ab','b','a');
                        # b'abba'.rpartition(b'x') returns (b'',b'',b'abba');
                        # applies to str,bytes,bytearray
o.rsplit(sep,maxsplit)  # Same as o.split(), except splitting is done from the right when
                        # maxsplit is given;
                        # 'fooboohoo'.rsplit('oo', 1) returns ['foobooh',''];
                        # applies to str,bytes,bytearray
o.rstrip(s)             # Returns a copy of o with last characters removed if present in
                        # string s (whitespace if no s); 'abcd'.rstrip('cda') returns 'ab';
                        # ' a \t\n'.rstrip() returns ' a';
                        # applies to str,bytes,bytearray
o.setdefault(k,d)       # Returns o[k] if it exists, otherwise sets o[k] = d (None if no d)
                        # and returns this value;
                        # if o = {2:6} then o.setdefault(2,8) returns 6 (o is unchanged),
                        # o.setdefault(3,8) makes o {2:6, 3:8} and returns 8, and
                        # o.setdefault(4) makes o {2:6, 3:8, 4:None} and returns None;
                        # applies to dict,defaultdict,OrderedDict,Counter
o.shape                 # Read-only tuple containing the number of items in o's dimensions;
                        # if o = memoryview(b'abcd').cast('B',[1,4]) then o.shape is (1,4);
                        # applies to memoryview
o.sort(key=kf,reverse=r)# Sorts the items of o using key function kf (kf(x) returns a
                        # comparison key for each item x, and is lambda x:x if not given);
                        # two items x and y are always compared using kf(x).__lt__(kf(y));
                        # if kf is a class with a suitable __lt__ method, kf(x) creates a
                        # new instance of this class as the comparison key for item x;
                        # sort order is reversed if r is True (r is False if not given);
                        # if o = [-6,5,10] then o.sort(reverse=True) makes o [10,5,-6], and
                        # o.sort(key=abs) makes o [5,-6,10];
                        # applies to list
o.split(sep,maxsplit)   # Returns a list of parts of o split at each occurrence of sep, or
                        # at each sequence of whitespace if sep is omitted or None;
                        # if maxsplit is given and not -1, only maxsplit splits are done;
                        # ' hi  ho '.split() returns ['hi','ho']; '  '.split() returns [];
                        # ' hi  ho '.split(' ') returns ['','hi','','ho',''];
                        # 'fooboohoo'.split('oo', 1) returns ['f','boohoo'];
                        # ''.split(' ') returns [''];
                        # applies to str,bytes,bytearray
o.splitlines(keepends)  # Returns a list of lines in o, splitting at any type of newline,
                        # and discarding newlines if keepends is omitted or False;
                        # 'hi\nho\r'.splitlines() returns ['hi','ho'];
                        # 'hi\r\nho\n\r'.splitlines(True) returns ['hi\r\n','ho\n','\r'];
                        # 'hi\nho'.splitlines(keepends=True) returns ['hi\n','ho'];
                        # '\n'.splitlines() returns ['']; ''.splitlines() returns [];
                        # applies to str,bytes,bytearray
o.start                 # Read-only start value of range o; range(10).start returns 0;
                        # applies to range
o.startswith(x,a,b)     # Returns True if o[a:b] starts with x (or with an item in tuple x);
                        # 'abc'.startswith(('ab','z')) returns True;
                        # applies to str,bytes,bytearray
o.step                  # Read-only step value of range o; range(10).step returns 1;
                        # applies to range
o.stop                  # Read-only stop value of range o; range(10).stop returns 10;
                        # applies to range
o.strides               # Read-only tuple containing the distance in bytes from the start
                        # of one item to the start of the next in each of o's dimensions;
                        # if o = memoryview(b'abcd').cast('H',[1,2]) then o.strides is
                        # (4,2);
                        # applies to memoryview
o.strip(s)              # Returns a copy of o with characters removed from both ends if
                        # present in string s (whitespace if no s);
                        # '0+a+b!'.strip('!+0') returns 'a+b';
                        # ' a b\t\n'.strip() returns 'a b';
                        # applies to str,bytes,bytearray
o.suboffsets            # TBD
                        # applies to memoryview
o.subtract(o2)          # Subtracts the counts of o2's items from the counts of o's
                        # corresponding items; if o = Counter({'a': 1}) then
                        # o.subtract({'a': 2, 'b': -3}) returns Counter({'a': -1, 'b': 3});
                        # applies to Counter
o.swapcase()            # Returns a copy of o with all lower-/uppercase characters
                        # converted to upper-/lowercase;
                        # '2B|Not 2B'.swapcase() returns '2b|nOT 2b';
                        # applies to str,bytes,bytearray
o.symmetric_difference(o2)          # Same as o ^ o2 (but doesn't call o.__xor__());
                                    # {2,3,4}.symmetric_difference({2,6} returns {3,4,6};
                                    # applies to set,frozenset
o.symmetric_difference_update(o2)   # Same as o ^= o2 (but doesn't call o.__ixor__());
                                    # if o = {2,3,4} then o.symmetric_difference_update(
                                    # {2,6}) makes o {3,4,6};
                                    # applies to set
o.title()               # Returns a copy of o with the first character of each sequence of
                        # cased characters converted to uppercase, the rest to lowercase;
                        # '2b|noT 2b'.title() returns '2B|Not 2B';
                        # "they're DAD's".title() returns "They'Re Dad'S";
                        # applies to str,bytes,bytearray
o.tobytes(order)        # Returns a bytes object containing the bytes of o in the requested
                        # order; the order may be (default is 'C' if not specified):
                        # 'C': C-style (last index of array increments fastest);
                        # 'F': Fortran-style (first index of array increments fastest);
                        # 'A': the same style as used by o;
                        # if o = memoryview(b'AbCd').cast('B',[2,2]) then o.tobytes()
                        # returns b'AbCd' and o.tobytes('F') returns b'ACbd';
                        # applies to memoryview
o.tolist()              # Returns a list (of lists if o is multidimensional) containing the
                        # data in o; if o = memoryview(b'abcd').cast('B',[2,2]) then
                        # o.tolist() returns [[97, 98], [99, 100]];
                        # applies to memoryview
o.toreadonly()          # Returns a read-only copy of o; if o = memoryview(bytearray(b'A'))
                        # and o2 = o.toreadonly() then o.readonly is False, o2.readonly is
                        # True, o2 == o, o2 is not o, o2[0] == o[0] == 65, o[0] = 66 makes
                        # o2[0] 66, and o2[0] = 67 raises TypeError (cannot modify
                        # read-only memory);
                        # applies to memoryview
o.total()               # Returns sum of all counts; Counter(a=2,b=3).total() returns 5;
                        # applies to Counter
o.translate(t)          # Returns a copy of o with characters replaced according to table t
                        # (o.maketrans() may be used to create t);
                        # 'Aargh'.translate(str.maketrans('Ag','Bt','h')) returns 'Bart';
                        # applies to str,bytes,bytearray
o.union(o2,...)         # Same as o | set(o2) | ... (but doesn't call o.__or__());
                        # {2,3,4}.union({2,6},[3,7]) returns {2,3,4,6,7};
                        # applies to set,frozenset
o.update(o2)            # Updates o with items from o2;
                        # for set: same as o |= set(o2) (but doesn't call o.__ior__());
                        # if o = {2,3,4} then o.update([2,6]) makes o {2,3,4,6};
                        # applies to set,dict,defaultdict,OrderedDict,Counter
o.update(o2,...)        # Updates o with items from o2, ...;
                        # same as o |= set(o2) | ... (but doesn't call o.__ior__());
                        # if o = {2,3,4} then o.update({2,6},[3,7]) makes o {2,3,4,6,7};
                        # applies to set
o.upper()               # Returns a copy of o with all lowercase characters converted to
                        # uppercase; '2B|Not 2B'.upper() returns '2B|NOT 2B';
                        # applies to str,bytes,bytearray
o.values()              # Returns a live view of o's values; if o = {4:5,6:7} and v =
                        # o.values() then list(v) returns [5,7] and v will follow changes
                        # to o;
                        # applies to dict,defaultdict,OrderedDict,Counter
o.zfill(w)              # Returns a copy of o right-justified in a string of length w
                        # filled with '0' (but a leading sign is left-justified); returns
                        # an unmodified copy if len(o) >= w;
                        # 'A'.zfill(4) returns '000A'; '-A'.zfill(4) returns '-00A';
                        # applies to str,bytes,bytearray

Iterator Types

# Iterator and iterable type detection
import collections.abc                  # Imports abstract base class module
isinstance(o, collections.abc.Iterator) # Returns True if o is an iterator object
isinstance(o, collections.abc.Iterable) # Returns True if o is an iterable object (e.g.
                        # container or iterator), in which case iter(o) returns an iterator
                        # for o

# Iterator types
si = iter('señor')      # Assigns a string iterator
sai = iter('hello')     # Assigns a faster string iterator for a pure ascii string
type(si)                # Returns class with name 'str_iterator'
type(sai)               # Returns class with name 'str_ascii_iterator'
next(si)                # Returns 's', i.e. next item; raises StopIteration exception
                        # when no more items; iterators normally can't be restarted (one
                        # exception is a file f which can be restarted with f.seek(0))
next(si, '.')           # Returns 'e'; returns '.' when no more items

li = iter([3, 'hi'])    # Assigns a list iterator
type(li)                # Returns class with name 'list_iterator'
next(li)                # Returns 3

ti = iter((3, 'hi'))    # Assigns a tuple iterator
type(ti)                # Returns class with name 'tuple_iterator'
next(ti)                # Returns 3

seti = iter({3, 'hi'})  # Assigns a set iterator (iteration order is unpredictable)
type(seti)              # Returns class with name 'set_iterator'
next(seti)              # Returns 3 or 'hi'

d = {'a':10, 'b':5}     # Assigns a dictionary
dki = iter(d)           # Assigns a dictionary key iterator (same as iter(d.keys()))
type(dki)               # Returns class with name 'dict_keyiterator'
next(dki)               # Returns 'a'
dvi = iter(d.values())  # Assigns a dictionary value iterator
type(dvi)               # Returns class with name 'dict_valueiterator'
next(dvi)               # Returns 10
dii = iter(d.items())   # Assigns a dictionary item iterator
type(dii)               # Returns class with name 'dict_itemiterator'
next(dii)               # Returns ('a', 10)

bi = iter(b'hello')     # Assigns a bytes iterator
type(bi)                # Returns class with name 'bytes_iterator'
next(bi)                # Returns 104 (ascii value of 'h')

bai = iter(bytearray([65, 0])) # Assigns a bytearray iterator
type(bai)               # Returns class with name 'bytearray_iterator'
next(bai)               # Returns 65

ri = iter(range(5))     # Assigns a range iterator
type(ri)                # Returns class with name 'range_iterator'
next(ri)                # Returns 0

from random import random,seed  # Imports functions random and seed from module random
seed(0, version=2)      # Sets the starting point for a well-defined pseudorandom sequence
ci = iter(random, 0.1)  # Assigns a callable iterator that calls function random until that
                        # function returns 0.1 (very unlikely); note that iter() with 2
                        # args is quite different from iter() with 1 arg
type(ci)                # Returns class with name 'callable_iterator'
next(ci)                # Returns 0.8444218515250481
next(ci)                # Returns 0.7579544029403025

se = enumerate('hello') # Assigns an enumerate object (iterator) for a string
type(se)                # Returns class enumerate
isinstance(se, enumerate)   # Returns True
next(se)                # Returns (0, 'h'); first item in this tuple is count
next(se)                # Returns (1, 'e')
xe = enumerate(ri, 100) # Assigns an enumerate object for a range iterator (see above)
                        # with count starting at 100; ri already iterated once above, so
                        # first xe iteration triggers second ri iteration
next(xe)                # Returns (100, 1)
next(xe)                # Returns (101, 2)
next(ri)                # Returns 3; xe uses ri to iterate, so ri is also at item 3

z = zip('ab',[3,4])     # Assigns a zip object (iterator)
type(z)                 # Returns class zip
isinstance(z, zip)      # Returns True
next(z)                 # Returns ('a', 3)
next(z)                 # Returns ('b', 4)
Generator objects (see generator expressions and generator functions) and files are also iterators.

More Types

"Everything is an object", so see these other object types:

Control Flow Manipulation

Conditional Execution

# 'If' statement
if x == 3:
    x = y               # Lines at same nesting level must have same indentation
    if not z:           # (see compound statement format)
        y += 1
elif y != 4:
    pass                # Does nothing (placeholder for empty statement list)
else:
    y = x
    # Single-line 'if'
    if z: x = 1; y = 2  # Semi-colon binds tighter than colon, so doesn't end the 'if'
    else: y = 1; x = 2
See also conditional expressions.

'Match' Statement

The basics:
# Define helper function to print info
def log(expr, pattern, **vars):
    print(f'{expr!r} matches "{pattern}"',
          *('and sets', ', '.join(f'{v}={vars[v]!r}' for v in vars)) * (len(vars) > 0))

# Define some variables for later use (x will be overridden by some case patterns)
x = 3.14
z = 5+7j

# Feed some values one by one into the match statement and see which case gets matched
for expr in ((2.0,1), [2,True], True, 1, 3, 4.0, 3.0, 4, (5,'A'), 'A', ('B',3.0),
             (1,2,3,4), ((1,2),(3,4)), ((1,2,3),4), [3.0,(4,True)], [3.0,[4,True]],
             (5,6,7), (5,7,9), (None,), ()):
    match expr:
        case 2, 1.0 as y:                               # int and float literal patterns
                                                        # will match any int/float/bool;
                                                        # a pattern sequence will match any
                                                        # tuple/list/... sequence;
                                                        # 'as y' assigns value matching 1.0
                                                        # to y
            log(expr, "2, 1.0 as y", y=y)
        case True:                                      # bool literal patterns only match
                                                        # bool values
            log(expr, "True")
        case int(3) | float(4):                         # int(3) matches only integer 3,
                                                        # etc; '|' means 'or'
            log(expr, "int(3) | float(4)")
            print(f'    (and y is still {y!r})')        # Previous assignments remain
        case int(x) | float(x), str(y):                 # int(x) matches any int and
                                                        # assigns it to x
            log(expr, "int(x) | float(x), str(y)", x=x, y=y)
        case (str(x) as y) | (('B' as x, float()) as y):    # float() matches any float, no
                                                            # assignment
            log(expr, "(str(x) as y) | (('B' as x, float()) as y)", x=x, y=y)
        case (1 | 'C') as x, *y, _ if len(y) < 3:    # *y matches zero or more values
                                                        # and assigns them to y; _ matches
                                                        # any single value, no assignment;
                                                        # 'if' condition is checked after
                                                        # pattern match and assignments
            log(expr, "(1 | 'C') as x, *y, _ if len(y) < 3", x=x, y=y)
        case (_, _), x, *_:                             # Tuple structure in pattern must
                                                        # match ditto in value
            log(expr, "(_, _), x, *_", x=x)
        case list([float(), tuple((int(x), bool(y)))]): # list()/tuple() in pattern only
                                                        # matches exactly those sequence
                                                        # types
            log(expr, "list([float(), tuple((int(x), bool(y)))])", x=x, y=y)
        case z.real, x, z.imag:                         # Objects accessed via '.' syntax
                                                        # behave like literals, they are
                                                        # never assignment targets;
            log(expr, "z.real, x, z.imag", x=x)
        case _,:                                        # This matches any single-element
                                                        # tuple
            log(expr, "_,")
        case _:                                         # This matches anything, even an
                                                        # empty tuple
            log(expr, "_")

# After the end of the match statement, any assigned variables retain their last values
print(f'At end: x={x!r}, y={y!r}')
(2.0, 1) matches "2, 1.0 as y" and sets y=1
[2, True] matches "2, 1.0 as y" and sets y=True
True matches "True"
1 matches "_"
3 matches "int(3) | float(4)"
    (and y is still True)
4.0 matches "int(3) | float(4)"
    (and y is still True)
3.0 matches "_"
4 matches "_"
(5, 'A') matches "int(x) | float(x), str(y)" and sets x=5, y='A'
'A' matches "(str(x) as y) | (('B' as x, float()) as y)" and sets x='A', y='A'
('B', 3.0) matches "(str(x) as y) | (('B' as x, float()) as y)" and sets x='B', y=('B', 3.0)
(1, 2, 3, 4) matches "(1 | 'C') as x, *y, _ if len(y) < 3" and sets x=1, y=[2, 3]
((1, 2), (3, 4)) matches "(_, _), x, *_" and sets x=(3, 4)
((1, 2, 3), 4) matches "_"
[3.0, (4, True)] matches "list([float(), tuple((int(x), bool(y)))])" and sets x=4, y=True
[3.0, [4, True]] matches "_"
(5, 6, 7) matches "z.real, x, z.imag" and sets x=6
(5, 7, 9) matches "_"
(None,) matches "_,"
() matches "_"
At end: x=6, y=True
A more useful example:
import enum             # Imports enumeration class support
import operator         # Imports function versions of standard operators
import itertools        # Imports iterator tools, e.g. chain.from_iterable used by make_rpn
import pprint           # Imports pretty printing support

# Define an integer enumeration class for the levels of the expression grammar, such that
# an increasing level value corresponds to an increasing depth in the grammar hierarchy
# (SUM + 1 == MORE_SUM, etc.)
Level = enum.IntEnum('Level', 'SUM MORE_SUM TERM MORE_TERM FACTOR POWER MORE_POWER ATOM')

# Define a recursive function that creates a tuple hierarchy from a list of tokens; each
# tuple in the hierarchy will contain an operator function followed by its arguments (which
# may in turn be tuples)
def make_tree(items, level=None):
    # Find a case pattern below that matches the (level, items) tuple
    match level, items:
        # Handle initial call in order to do final check before returning to caller
        case None, all_items:
            match make_tree(all_items, Level.SUM):
                case x,:
                    return x
                case _, *rest:
                    raise ValueError(f'Unexpected token here: {rest}')
        # Get 1st argument (by calling 2 levels down) of a possible sequence of infix
        # operations; then call 1 level down to handle actual operation
        case Level.SUM | Level.TERM | Level.POWER, all_items:
            return make_tree(make_tree(all_items, Level(level + 2)), Level(level + 1))
        # Handle another (possibly first) add/subtract operation if any - by calling next
        # level to get 2nd argument; then call same level again to check for more of such
        # operations
        case Level.MORE_SUM, (x, ('+' | '-' as op), *rest):
            y, *rest = make_tree(rest, Level.TERM)
            opfunc = operator.add if op == '+' else operator.sub
            return make_tree(((opfunc, x, y), *rest), Level.MORE_SUM)
        # Handle another (possibly first) multiply/divide operation if any - by calling
        # next level to get 2nd argument; then call same level again to check for more of
        # such operations
        case Level.MORE_TERM, (x, ('*' | '/' as op), *rest):
            y, *rest = make_tree(rest, Level.FACTOR)
            opfunc = operator.mul if op == '*' else operator.truediv
            return make_tree(((opfunc, x, y), *rest), Level.MORE_TERM)
        # Handle another (possibly first) prefix sign operation if any - by calling same
        # level again to get argument (which may include another sign operation)
        case Level.FACTOR, (('+' | '-' as op), *rest):
            x, *rest = make_tree(rest, Level.FACTOR)
            opfunc = operator.pos if op == '+' else operator.neg
            return (opfunc, x), *rest
        # If no more prefix sign operations, call next level to get non-signed argument
        case Level.FACTOR, all_items:
            return make_tree(all_items, Level.POWER)
        # Handle another (possibly first) power operation if any - by calling previous(!)
        # level to get 2nd argument (power operations are evaluated right to left and
        # arguments may include prefix operations)
        case Level.MORE_POWER, (x, '**', *rest):
            y, *rest = make_tree(rest, Level.FACTOR)
            return (operator.pow, x, y), *rest
        # Handle parentheses if any - by calling highest level to get parenthesized
        # content; then check for closing parenthesis
        case Level.ATOM, ('(', *rest):
            match make_tree(rest, Level.SUM):
                case x, ')', *rest:
                    return x, *rest
            raise ValueError('Missing ")"')
        # At any level, if none of the above cases is valid and the first item is a simple
        # number or an already finalized tuple, then just pass everything up the call tree
        case _, (int() | float() | tuple(), *_) as x:
            return x
        # If there are no more tokens at this point, it's an error
        case _, ():
            raise ValueError('Incomplete expression')
        # If none of the above cases is valid, something is wrong with the next token
        case _, all_items:
            raise ValueError(f'Unexpected token here: {all_items}')

# As an example of using an expression tree returned by make_tree, here's a recursive
# function to calculate the numerical value of such an expression tree; it simply applies
# the operator function (the first item) in each tuple to the arguments (the remaining
# items in the same tuple)
def calc_tree(tree):
    return tree[0](*map(calc_tree, tree[1:])) if isinstance(tree, tuple) else tree

# As a second example, here's a recursive function that produces a reverse polish notation
# representation of an expression tree; it uses chain.from_iterable to flatten the argument
# trees in each tuple, and places the operator name at the end
def make_rpn(tree):
    return (*itertools.chain.from_iterable(map(make_rpn, tree[1:])), tree[0].__name__) \
        if isinstance(tree, tuple) else (tree,)

# A list of tokens (e.g. produced by the get_tokens function defined in the
# string tokenizer example)
tokens = [
    '-', 4, '**', '+', 3, '**', 2, '/', '-', '+', 8, '+', '(', 7, '-', 5, ')', '*', 6.1]

# Call the 3 functions defined above and print their return values
print(f'''\
Tree:
{pprint.pformat(tree := make_tree(tokens), indent=2)}
-----
Calculated: {calc_tree(tree)}
Reference:  {eval(''.join(map(str, tokens)))}
-----
RPN:
{make_rpn(tree)}''')
Tree:
( <built-in function add>,
  ( <built-in function truediv>,
    ( <built-in function neg>,
      ( <built-in function pow>,
        4,
        (<built-in function pos>, (<built-in function pow>, 3, 2)))),
    (<built-in function neg>, (<built-in function pos>, 8))),
  (<built-in function mul>, (<built-in function sub>, 7, 5), 6.1))
-----
Calculated: 32780.2
Reference:  32780.2
-----
RPN:
(4, 3, 2, 'pow', 'pos', 'pow', 'neg', 8, 'pos', 'neg', 'truediv', 7, 5, 'sub', 6.1, 'mul', 'add')

Loops

# While loop
while x < 10:
    x += 1
    if x == 5:
        continue        # Skips rest of loop body and goes back to testing loop condition
    print(x)
    if x == y:
        break           # Exits loop and skips 'else' part
else:                   # Optional; does something if loop condition tested false,
                        # i.e. when no more iterations and no break
    print('no break')

# For loop
for x in alist:         # x becomes each item of alist in turn; alist is evaluated once
                        # (Note: alist can be any iterable type or iterator)
    print(x)
    for i in range(x):  # range(x) is a sequence of integers from 0 through x - 1
        print(i)
else:                   # Optional; does something when no more iterations and no break
    print('no break')
print(x,i)              # Loop variables remain visible and retain their last value after
                        # the loop

Exception Handling

import sys
# Loop through tuple of expression strings, and set expr to each one in turn
for expr in ('1/0', '[][1]', '{}[2]', '(3).a', ')', 'sys.exit(5)', '6'):
    print('Trying expr', expr)
    # Try (with all parts: 'except', 'else', 'finally')
    try:
        eval(expr)              # Evaluates expression, possibly raising exception
        # Continue here if no exception occurred above
        print('- no exception')
    except ZeroDivisionError:
        # Come here on ZeroDivisionError exception in 'try' part
        print('- div by 0')
    except (IndexError, KeyError, AttributeError) as e:
        # Come here on any of the listed exceptions in 'try' part - with e set to exception
        # object
        print('-', repr(e))
    except Exception as e:
        # Come here on any (not already handled) non-system-exiting exception in 'try' part
        # - with e set to exception object (compare with 'except:' in next example)
        print('- other:', repr(e))
        continue            # Skips rest of for-loop body (after executing 'finally' part)
    except SystemExit as e:
        # Come here on SystemExit exception in 'try' part - with e set to exception object
        # (SystemExit is a subclass of BaseException, not of Exception, see class diagram
        # under Exception Grouping)
        print('-', repr(e))
    else:                   # Optional 'else' part
        # Come here if no exception in 'try' part; same as putting code at end of 'try'
        # part, but exceptions in 'else' part are not handled
        print('- all OK')
        break               # Breaks out of for-loop (after executing 'finally' part)
    finally:                # Optional 'finally' part
        # Always come here as last step, even on unhandled exception or
        # return/break/continue (in try/except/else parts)
        print('- finally')
    print('- rest of loop body')
Trying expr 1/0
- div by 0
- finally
- rest of loop body
Trying expr [][1]
- IndexError('list index out of range')
- finally
- rest of loop body
Trying expr {}[2]
- KeyError(2)
- finally
- rest of loop body
Trying expr (3).a
- AttributeError("'int' object has no attribute 'a'")
- finally
- rest of loop body
Trying expr )
- other: SyntaxError("unmatched ')'", ('<string>', 1, 1, ')', 1, 1))
- finally
Trying expr sys.exit(5)
- SystemExit(5)
- finally
- rest of loop body
Trying expr 6
- no exception
- all OK
- finally
import sys
# Outer 'try' which catches any unhandled exception in inner 'try'
try:
    # Inner 'try' (with 'finally' part only)
    try:
        print('Raising exception')
        sys.exit()          # Raises SystemExit exception which is not handled by this
                            # 'try' (but by outer 'try')
        print('Unreachable')
    finally:                # 'finally' part is also executed on unhandled exception
        print('Inner finally')
except:
    # Come here on any (not already handled) exception in 'try' part; this will catch the
    # SystemExit exception in the inner 'try' part; note that unlike 'except Exception:'
    # this bare 'except:' catches all exceptions - also system-exiting ones
    print('Outer except')
Raising exception
Inner finally
Outer except
# Raise exceptions explicitly in different ways
for case in range(1, 8):
    print('Case', case)
    try:
        try:
            if case == 1:
                raise RuntimeError          # Raises exception using exception class
            elif case >= 2:
                raise RuntimeError('hi', 5) # Raises exception using exception object
        except Exception as e:              # Catches any exception and puts it in e
            print('-', repr(e))
            if case == 3:
                raise                                   # Raises same exception e again as
                                                        # though it had never been caught
                                                        # by the try-except
            elif case == 4:
                raise RuntimeError('ho')                # Raises new exception, but remem-
                                                        # bers that this happened while
                                                        # handling exception e
            elif case == 5:
                raise RuntimeError('ho') from None      # Raises new exception and forgets
                                                        # about exception e
            elif case == 6:
                raise RuntimeError('ho') from e         # Raises new exception and notes
                                                        # that it was caused by exception e
            elif case == 7:
                raise RuntimeError('ho') from KeyError('g') # Raises new exception and
                                                        # notes that it was caused by
                                                        # exception KeyError('g')
    except Exception as e:
        print('--', repr(e), 'with args', e.args, 'caused by', repr(e.__cause__))
Case 1
- RuntimeError()
Case 2
- RuntimeError('hi', 5)
Case 3
- RuntimeError('hi', 5)
-- RuntimeError('hi', 5) with args ('hi', 5) caused by None
Case 4
- RuntimeError('hi', 5)
-- RuntimeError('ho') with args ('ho',) caused by None
Case 5
- RuntimeError('hi', 5)
-- RuntimeError('ho') with args ('ho',) caused by None
Case 6
- RuntimeError('hi', 5)
-- RuntimeError('ho') with args ('ho',) caused by RuntimeError('hi', 5)
Case 7
- RuntimeError('hi', 5)
-- RuntimeError('ho') with args ('ho',) caused by KeyError('g')
If the outer try-except were removed from the above code, and if the loop were somehow able to restart at the next iteration every time it stopped due to an exception, then the output would be:
Case 1
- RuntimeError()
Case 2
- RuntimeError('hi', 5)
Case 3
- RuntimeError('hi', 5)
Traceback (most recent call last):
  File "<string>", line 9, in <module>
RuntimeError: ('hi', 5)
...restarting at next loop iteration...
Case 4
- RuntimeError('hi', 5)
Traceback (most recent call last):
  File "<string>", line 9, in <module>
RuntimeError: ('hi', 5)

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "<string>", line 17, in <module>
RuntimeError: ho
...restarting at next loop iteration...
Case 5
- RuntimeError('hi', 5)
Traceback (most recent call last):
  File "<string>", line 21, in <module>
RuntimeError: ho
...restarting at next loop iteration...
Case 6
- RuntimeError('hi', 5)
Traceback (most recent call last):
  File "<string>", line 9, in <module>
RuntimeError: ('hi', 5)

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "<string>", line 24, in <module>
RuntimeError: ho
...restarting at next loop iteration...
Case 7
- RuntimeError('hi', 5)
KeyError: 'g'

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "<string>", line 27, in <module>
RuntimeError: ho

Exception Grouping

Exception groups (added in Python 3.11) allow multiple exceptions to be raised at the same time, and the 'except*' syntax allows partial handling of such groups.

The UML diagram below shows how the exception grouping classes (red) relate to the normal top level exception classes (black). It also shows some examples of specific exception classes (gray). Arrows point from subclasses to the superclasses/bases they inherit from. See also about classes/types and objects

BaseException BaseExceptionGroup Exception ExceptionGroup SystemExit ValueError
try:
    try:
        # Raise a BaseExceptionGroup containing some exceptions and a nested ExceptionGroup
        # (ExceptionGroup is a subclass of both BaseExceptionGroup and Exception, and
        # BaseExceptionGroup is a subclass of BaseException, the latter is needed for system
        # exiting exceptions)
        eg = BaseExceptionGroup('Errors', [SystemExit(1),
                                           ExceptionGroup('Sub', [ValueError(2),
                                                                  TypeError(3)]),
                                           ValueError(4)])
        raise eg
    except* ValueError as e:
        # Come here if there are any ValueError exceptions in the (Base-)ExceptionGroup (or
        # in nested groups) - with e set to a copy of the group containing only those
        # ValueError exceptions (note the special 'except*' syntax which handles part of a
        # (Base-)ExceptionGroup while allowing the remaining part to propagate to other
        # 'except*' clauses or to outer code blocks if not handled)
        print('Val:', type(e).__name__, e.args)
    except* SystemExit as e:
        # Also come here if there are any SystemExit exceptions in the BaseExceptionGroup -
        # with e set to a copy of the BaseExceptionGroup containing only those SystemExit
        # exceptions
        print('Sys:', type(e).__name__, e.args)
    finally:
        # Always come here as last step, even if parts of the (Base-)ExceptionGroup are
        # unhandled
        print('Inner try done')
    # The unhandled TypeError exception above will propagate to the outer 'try' as an
    # ExceptionGroup containing only the TypeError exception, so the following print will
    # not be executed
    print('All parts of ExceptionGroup handled')
except BaseException as e:
    # Catch any remaining exception (group) here (since BaseExceptionGroup is a subclass of
    # BaseException, it can be handled by a normal 'except' clause)
    print('Any:', type(e).__name__, e.args)
Val: ExceptionGroup ('Errors', [ExceptionGroup('Sub', [ValueError(2)]), ValueError(4)])
Sys: BaseExceptionGroup ('Errors', [SystemExit(1)])
Inner try done
Any: ExceptionGroup ('Errors', [ExceptionGroup('Sub', [TypeError(3)])])
for excp in (AttributeError(1), SystemExit(2)):
    try:
        # Raise a simple non-group exception
        raise excp
    except* BaseException as e:
        # The 'except*' clause will wrap any simple non-group exception in an ExceptionGroup
        # (or BaseExceptionGroup if needed) and assign that to e, so e is always guaranteed
        # to be an ExceptionGroup (or BaseExceptionGroup)
        print('Any:', type(e).__name__, e.args)
Any: ExceptionGroup ('', (AttributeError(1),))
Any: BaseExceptionGroup ('', (SystemExit(2),))

Assertions

Assertion statements are meant for debugging and have no effect if optimizations are enabled when running the script (i.e. if the script is run with "python3 -O script.py").
assert 2 == 2                           # Does nothing because the expression is True
assert 2 == 3                           # Raises AssertionError exception because the
                                        # expression is False
assert 2 == 3, 'Oops'                   # Raises AssertionError('Oops') exception due to
                                        # the False expression

'With' Statement

The 'with' statement uses a context manager to guarantee that certain actions are taken when the 'with' block is exited (e.g. cleanup of resources that were taken into use by the context manager when the 'with' block was entered), even if the exit is caused by an exception.
# 'With' statement using context manager which suppresses exception handling on exit
import contextlib                               # Imports with-related utilities
with contextlib.suppress(ZeroDivisionError):    # Enters a context where ZeroDivisionError
                                                # exceptions are suppressed
    print('Calculating...')                     # Prints something
    print(1/0)                                  # Raises a ZeroDivisionError exception
                                                # which exits the block and context, but is
                                                # caught by the 'suppress' context manager,
                                                # which takes no further action
    print('No problem')                         # This line is never executed
print('Done')                                   # Execution continues here
Calculating...
Done
# 4 nested 'with' contexts which open streams and redirect stdout/stderr on entry, and
# close the streams and cancel the redirection on exit
import contextlib, io, sys                  # Imports various libraries
with io.StringIO() as out, \
     io.StringIO() as err:                  # Creates a (file-like) text stream named out
                                            # and enters a context where out is guaranteed
                                            # to be closed no matter how the context is
                                            # exited; then creates err in the same way in
                                            # a nested context
    with (contextlib.redirect_stdout(out),
          contextlib.redirect_stderr(err)): # Enters 2 nested contexts where sys.stdout and
                                            # sys.stderr are redirected to out and err,
                                            # respectively; note: the 'with' expression can
                                            # be parenthesized to avoid backslash line-
                                            # continuation
        print('Hi')                         # Prints 'Hi' to out instead of to sys.stdout
        print('Ho', file=sys.stderr)        # Prints 'Ho' to err instead of to sys.stderr
    print('Done')                           # Prints 'Done' to sys.stdout as usual
    print('Out:', out.getvalue(), end='')   # Prints text collected in out so far
    print('Err:', err.getvalue(), end='')   # Prints text collected in err so far

out.getvalue()                              # Raises ValueError exception because out was
                                            # closed when outer 'with' exited
Done
Out: Hi
Err: Ho
Traceback (most recent call last):
  File "<string>", line 22, in <module>
ValueError: I/O operation on closed file

Using Custom Context Manager Class

A custom class can be used to instantiate a context manager object for use in a 'with' statement. Any class that defines the __enter__() and __exit__() special methods can be used for this purpose.
import io

# Custom context manager class
class CM:
    def __init__(self, init_text='', *, suppress_excp=False): # Called to construct
                                                              # context manager instance
        print('  Creating context manager')
        self.init_text = init_text
        self.suppress_excp = suppress_excp
    def __enter__(self):                                # Called when 'with' enters context
        print('  Entering context')
        self.strm = io.StringIO(self.init_text)
        self.strm.seek(0, io.SEEK_END)
        return self.strm                                # Return value will be assigned to
                                                        # name after 'as' in 'with'
    def __exit__(self, excp_type, excp_val, traceback): # Called when 'with' exits context
        print('  Exiting context with text:', self.strm.getvalue())
        self.strm.close()
        if excp_type is None:                           # On normal exit, all args are None
            print('  - normal exit')                    # and return value is ignored
        else:                                           # Else, args have exception info
            print('  - exception raised: %r, %r, %r' %
                (excp_type, excp_val, traceback.tb_lineno))
            if self.suppress_excp:
                print('  - but suppressed')
            return self.suppress_excp                   # and returning True suppresses
                                                        # further exception handling
# Loop through 3 cases: normal exit, suppressed exception, unsuppressed exception
for i in range(3):                                  # i = 0, 1, 2
    print('i =', i)
    with CM('Hi', suppress_excp=(i==1)) as s:       # Creates instance of context mgr CM
                                                    # (suppressing exceptions when i is 1),
                                                    # calls this instance's __enter__(),
                                                    # and sets s to return value from
                                                    # __enter__()
        s.write(' ho')
        if i > 0:
            raise RuntimeError('Oops')              # Raises exception (and exits 'with'
                                                    # block) when i is 1 or 2
        s.write(' hum')
                                                    # Exits 'with' block normally
        # On any exit from 'with' block, context manager instance's __exit__() is called
i = 0
  Creating context manager
  Entering context
  Exiting context with text: Hi ho hum
  - normal exit
i = 1
  Creating context manager
  Entering context
  Exiting context with text: Hi ho
  - exception raised: <class 'RuntimeError'>, RuntimeError('Oops'), 38
  - but suppressed
i = 2
  Creating context manager
  Entering context
  Exiting context with text: Hi ho
  - exception raised: <class 'RuntimeError'>, RuntimeError('Oops'), 38
Traceback (most recent call last):
  File "<string>", line 38, in <module>
RuntimeError: Oops

Using Decorated Generator Function

It is also possible (and sometimes easier than defining a complete context manager class) to use a generator function to produce a context manager object for use in a 'with' statement. The 'contextlib' standard module provides a 'contextmanager' decorator for this purpose. When applied to a generator function, the 'contextmanager' decorator automatically adds the necessary __enter__() and __exit__() special methods to the returned generator object to make it usable as a context manager.

The code before the 'yield' statement in the generator function will be executed on context entry, and the code after the 'yield' statement will be executed on context exit. Any exception raised within the 'with' block will propagate to the 'yield' statement and can be handled by a 'try' statement at that point.

import contextlib, io

@contextlib.contextmanager                      # Adds __enter__ and __exit__ methods to
                                                # generator object returned from function
                                                # cm, so it can be used as context manager
def cm(init_text='', *, suppress_excp=False):
    print('  Entering context')
    strm = io.StringIO(init_text)
    strm.seek(0, io.SEEK_END)
    excp_val = None
    try:
        yield strm                          # Passes control back to 'with' statement
                                            # which assigns strm to name after 'as' and
                                            # executes body of 'with' block
                                            # When 'with' block ends or raises exception,
                                            # control comes back here
    except Exception as e:
        excp_val = e
        if not suppress_excp:
            raise
    finally:
        print('  Exiting context with text:', strm.getvalue())
        strm.close()
        if excp_val is None:
            print('  - normal exit')
        else:
            print('  - exception raised: %r' % excp_val)
            if suppress_excp:
                print('  - but suppressed')

# Loop through 3 cases: normal exit, suppressed exception, unsuppressed exception
for i in range(3):                                  # i = 0, 1, 2
    print('i =', i)
    with cm('Hi', suppress_excp=(i==1)) as s:       # Gets context mgr instance from cm()
                                                    # (suppressing exceptions when i is 1),
                                                    # calls this instance's __enter__()
                                                    # (which runs body of cm() until yield)
                                                    # and sets s to return value from
                                                    # __enter__() (i.e. yielded value)
        s.write(' ho')
        if i > 0:
            raise RuntimeError('Oops')              # Raises exception (and exits 'with'
                                                    # block) when i is 1 or 2
        s.write(' hum')
                                                    # Exits 'with' block normally
        # On any exit from 'with' block, context manager instance's __exit__() is called
        # (which causes execution of cm() to continue after yield if no exception, or
        # triggers exception handling at the point of the yield)
i = 0
  Entering context
  Exiting context with text: Hi ho hum
  - normal exit
i = 1
  Entering context
  Exiting context with text: Hi ho
  - exception raised: RuntimeError('Oops')
  - but suppressed
i = 2
  Entering context
  Exiting context with text: Hi ho
  - exception raised: RuntimeError('Oops')
Traceback (most recent call last):
  File "<string>", line 42, in <module>
RuntimeError: Oops

Script Termination

import sys              # Imports the sys module
sys.exit()              # Raises a SystemExit exception which terminates the script with
                        # exit code 0 (usually considered to mean success) -
                        # unless the exception is caught by a try statement
sys.exit(2)             # Same as above, but uses the specified exit code
sys.exit('Oops!')       # Same as above, but prints the specified string and uses
                        # exit code 1 (usually considered to mean failure)
sys.exit(obj)           # Same as above, but converts the specified object to a string,
                        # prints it, and uses exit code 1, unless obj is None, in which
                        # case nothing is printed and exit code is 0
exit()                  # Not recommended for use in scripts; intended for use in the
                        # interactive interpreter where it works like sys.exit()
quit()                  # Same as exit()

Input/Output

Script Arguments

import sys              # Imports the sys module
sys.argv                # Returns a list containing the script name and command line args
print(sys.argv[0])      # Prints script name (possibly full path)
if len(sys.argv) > 1:   # If there are any command line arguments, then ...
    print(sys.argv[1])  # ... print first one

Standard In/Out/Error

# Stdout
x = 10
d = {'a': 4, 'b': 5}
print('d =', d)         # Prints 'd = {'a': 4, 'b': 5}' + newline to stdout;
                        # args are separated by a space
print('x =', x, end=' ')# Prints 'x = 10 ' to stdout without newline (default end is '\n')
print('(decimal)')      # Prints '(decimal)', so whole line is 'x = 10 (decimal)'
print('x', x, sep=' = ')# Prints 'x = 10' + newline to stdout (default sep is ' ')

# Stderr
import sys                          # Imports sys module
print('x =', x, end='', file=sys.stderr)  # Prints 'x = 10' to stderr without newline;
                                          # (default file is sys.stdout)
print(' (decimal)', file=sys.stderr)  # Prints ' (decimal)'; whole line: 'x = 10 (decimal)'
sys.stderr.write('x = %d' % x)      # Prints 'x = 10' to stderr (never automatic newline)
sys.stderr.write(' (decimal)\n')    # Prints ' (decimal)' + newline;
                                    # whole line printed to stderr: 'x = 10 (decimal)'
a1 = ['hi\n', 'ho\n']               # List of strings incl. newlines
sys.stderr.writelines(a1)           # Prints 'hi' + newline + 'ho' + newline

# Stdin
q = input('Q: ')        # Prints 'Q: ' to stdout (without newline), then reads a line from
                        # stdin as a string, strips the newline, and assigns it to q
s = sys.stdin.read()    # Reads stdin until end of file and assigns the whole thing
                        # (incl. newlines) as a string to s
s = sys.stdin.readline()    # Reads one line from stdin as string incl. newline;
                            # returns empty string if no more lines
a = sys.stdin.readlines()   # Reads all lines from stdin as list of strings incl. newlines

Files

A file can be opened as either a text file or a binary file, and (unlike in Python 2) the class of the opened file object and the supported operations depend on whether the file was opened as text or binary.

Files Opened as Text

Text file operations deal with str objects (i.e. Unicode text) and provide automatic platform dependent newline conversion (i.e. Python newlines '\n' are converted to/from whatever the platform uses in text files, e.g. '\r\n' for MS Windows). The default newline conversion can be controlled/disabled via the newline keyword parameter to open().

The file pointer of a file opened in text mode should only be moved (using f.seek()) to positions previously returned by f.tell(), or to the start or end of the file. No assumptions should be made about the meaning of a value returned by f.tell(), except that it can be passed to f.seek() to get back to the same position in the file. In other words, f.tell() does not necessarily return the number of bytes from the start of the file.

import io                   # Import io module containing file related extras

# Creating/truncating + writing text file, and misc. file methods
fname = 'txtfile.txt'
f = open(fname, 'w')        # Creates/truncates and opens file in text mode for writing
                            # from position 0; optional newline parameter controls newline
                            # handling:
                            #   newline=None   ('\n' maps to os.linesep, this is default)
                            #   newline=''     (no conversion)
                            #   newline='\n'   ('\n' maps to '\n', i.e. no conversion)
                            #   newline='\r'   ('\n' maps to '\r', i.e. Mac style)
                            #   newline='\r\n' ('\n' maps to '\r\n', i.e. Windows style)
type(f)                     # Returns class io.TextIOWrapper (same for all text modes)
isinstance(f, io.TextIOWrapper) # Returns True
isinstance(f, io.TextIOBase)    # Returns True (superclass of all text files)
isinstance(f, io.IOBase)        # Returns True (superclass of both text and binary files)
f.tell()                    # Returns 0 (position of file pointer in some sense)
x = 10
print('x =', x, end=' ', file=f)    # Writes 'x = 10 ' to file without newline
print('m', file=f)                  # Writes 'm' + newline;
                                    # whole line: 'x = 10 m'
f.write('x = %d' % x)       # Writes 'x = 10' to file (never automatic newline) and returns
                            # 6 (number of chars written)
f.write(' m\n')             # Writes ' m' + newline and returns 3 (chars written);
                            # whole line: 'x = 10 m'
f.tell()                    # Returns 20 on Windows (position of file pointer in some sense)
a1 = ['hi\n', 'ho\n']       # List of strings incl. newlines
f.writelines(a1)            # Writes 'hi\n' + 'ho\n' to file
f.tell()                    # Returns 28 on Windows (position of file pointer in some sense)
f.close()                   # Closes file
                            # File now contains 'x = 10 m\r\nx = 10 m\r\nhi\r\nho\r\n' on
                            # Windows

# Exclusively creating text file (only if it doesn't already exist)
f = open(fname, 'x')        # Raises FileExistsError exception because file already exists;
                            # same as mode 'w' if file doesn't exist
                            # File still contains 'x = 10 m\r\nx = 10 m\r\nhi\r\nho\r\n' on
                            # Windows

# Reading text file, and misc. file methods
f = open(fname, 'r')        # Opens existing file in text mode for reading from pos 0
                            # ('r' is optional); optional newline parameter controls
                            # newline handling:
                            #   newline=None   (any newline maps to '\n' and separates lines,
                            #                  this is default)
                            #   newline=''     (no conversion, any newline separates lines)
                            #   newline='\n'   (no conversion, only '\n' separates lines)
                            #   newline='\r'   (no conversion, only '\r' separates lines)
                            #   newline='\r\n' (no conversion, only '\r\n' separates lines)
f.tell()                    # Returns 0 (position of file pointer in some sense)
s = f.read()                # Reads entire file as string
f.tell()                    # Returns 28 on Windows (position of file pointer in some sense)
f.seek(0)                   # Moves file pointer back to start of file and returns 0 (pos)
f.seek(20)                  # Moves file pointer to position 20 in some sense (we don't
                            # know what 20 means, but we previously got it from f.tell()
                            # before writing 'hi\nho\n', so we know it is that position)
s = f.read()                # Reads remainder of file from current position: 'hi\nho\n'
f.seek(0, io.SEEK_END)      # Moves file pointer to end of file and returns 28 on Windows
                            # (position of file pointer in some sense);
f.seek(-10, io.SEEK_END)    # Raises io.UnsupportedOperation exception because non-zero
                            # move from end of file is not supported for text files
f.seek(0, io.SEEK_CUR)      # Moves file pointer nowhere (no move) and returns 28 on Windows
f.seek(-10, io.SEEK_CUR)    # Raises io.UnsupportedOperation exception because non-zero
                            # move from current position is not supported for text files
f.seek(0)                   # Moves file pointer back to start of file and returns 0 (pos)
s = f.readline()            # Reads next line from file as string incl. newline;
                            # returns empty string if no more lines
f.tell()                    # Returns 10 on Windows (position of file pointer in some sense)
a = f.readlines()           # Reads all remaining lines from file as list of strings
                            # incl. newlines
f.tell()                    # Returns 28 on Windows (position of file pointer in some sense)
f.seek(0)                   # Moves file pointer back to start of file and returns 0 (pos)
s = next(f)                 # Same as f.readline() but raises StopIteration at end of file
                            # (files are iterators with additional control via f.seek())
for s in f: print(s, end='')# Iterates through remaining lines in file and prints each one
f.close()                   # Closes file
                            # File still contains 'x = 10 m\r\nx = 10 m\r\nhi\r\nho\r\n' on
                            # Windows

# 'With' statement (good practice)
with open(fname) as f:      # 'with' ensures that file is closed when the 'with' suite of
    s = f.read()            # statements ends or raises an exception
                            # File still contains 'x = 10 m\r\nx = 10 m\r\nhi\r\nho\r\n' on
                            # Windows

# Appending to text file (creating it if needed)
f = open(fname, 'a')        # (Creates and) opens file in text mode for appending
                            # (writing at end)
f.tell()                    # Returns 28 on Windows (position of file pointer in some sense
                            # - irrelevant for mode 'a')
print('hum', file=f)        # Appends 'hum\n' to end of file
f.tell()                    # Returns 33 on Windows (position of file pointer in some sense)
f.close()                   # Closes file
                            # File now contains 'x = 10 m\r\nx = 10 m\r\nhi\r\nho\r\nhum\r\n'
                            # on Windows

# Creating/truncating + writing & reading text file
f = open(fname, 'w+')       # Creates/truncates and opens file in text mode for writing
                            # and reading
f.tell()                    # Returns 0 (position of file pointer in some sense)
s = f.read()                # Reads entire file as string: '' because file was truncated
print('hi', file=f)         # Writes 'hi\n' to file
f.tell()                    # Returns 4 on Windows (position of file pointer in some sense)
f.close()                   # Closes file
                            # File now contains 'hi\r\n' on Windows

# Reading & writing text file
f = open(fname, 'r+')       # Opens existing file in text mode for reading and writing
                            # (no truncating) from start of file
s = f.read(1)               # Reads 1 char from file as string: 'h'
f.tell()                    # Returns 1 on Windows (position of file pointer in some sense)
f.seek(0, io.SEEK_CUR)      # Doesn't move file pointer but flushes line buffer (needed for
                            # text files when switching between reading and writing) and
                            # returns 1 on Windows (position of file pointer in some sense)
print('o', end='', file=f)  # Writes 'o' to file - overwriting previous 'i' in 'hi\n'
f.tell()                    # Returns 2 on Windows (position of file pointer in some sense)
f.seek(0)                   # Moves file pointer back to start of file and returns 0 (pos)
s = f.read()                # Reads entire file as string: 'ho\n'
f.close()                   # Closes file
                            # File now contains 'ho\r\n' on Windows

# Appending to & reading text file (creating it if needed)
f = open(fname, 'a+')       # (Creates and) opens file in text mode for appending and
                            # reading from end of file
f.tell()                    # Returns 4 on Windows (position of file pointer in some sense)
f.seek(0)                   # Moves file pointer back to start of file and returns 0 (pos)
s = f.read()                # Reads entire file as string: 'ho\n'
f.tell()                    # Returns 4 on Windows (position of file pointer in some sense)
f.seek(0)                   # Moves file pointer back to start of file (doesn't affect
                            # append operation)
print('hum', file=f)        # Appends 'hum\n' to end of file; file is now 'ho\nhum\n'
f.tell()                    # Returns 9 on Windows (position of file pointer in some sense)
f.read()                    # Reads and returns remainder of file: '' because file pointer
                            # was at end of file
f.tell()                    # Returns 9 on Windows (position of file pointer in some sense)
f.close()                   # Closes file
                            # File now contains 'ho\r\nhum\r\n' on Windows

Files Opened as Binary

Binary file operations deal with bytes-like objects and do not perform any newline conversion.

The file pointer of a file opened in binary mode can be moved (using f.seek()) to any byte position within the file, and f.tell() returns the number of bytes from the start of the file to the position of the file pointer.

import io                   # Import io module containing file related extras

# Creating/truncating + writing binary file, and misc. file methods
fname = 'binfile.txt'
f = open(fname, 'wb')       # Creates/truncates and opens file in binary mode for writing
                            # from position 0
type(f)                     # Returns class io.BufferedWriter
isinstance(f, io.BufferedWriter)# Returns True
isinstance(f, io.BufferedIOBase)# Returns True (superclass of all binary files)
isinstance(f, io.IOBase)        # Returns True (superclass of both text and binary files)
f.tell()                    # Returns 0 (position of file pointer in bytes)
x = 10
f.write(b'x = %d' % x)      # Writes 'x = 10' to file (no newline) and returns 6 (number of
                            # bytes written - always equal to size of argument)
f.write(b' (decimal)\n')    # Writes ' (decimal)' + newline and returns 11 (bytes written);
                            # whole line: 'x = 10 (decimal)'
a1 = [b'hi\n', b'ho\n']     # List of strings incl. newlines
f.writelines(a1)            # Writes 'hi\n' + 'ho\n' to file
f.tell()                    # Returns 23 (position of file pointer in bytes)
f.close()                   # Closes file
                            # File now contains b'x = 10 (decimal)\nhi\nho\n'

# Exclusively creating binary file (only if it doesn't already exist)
f = open(fname, 'xb')       # Raises FileExistsError exception because file already exists;
                            # same as mode 'wb' if file doesn't exist
                            # File still contains b'x = 10 (decimal)\nhi\nho\n'

# Reading binary file, and misc. file methods
f = open(fname, 'rb')       # Opens existing file in binary mode for reading from pos 0
type(f)                     # Returns class io.BufferedReader
isinstance(f, io.BufferedReader)# Returns True
isinstance(f, io.BufferedIOBase)# Returns True (superclass of all binary files)
f.tell()                    # Returns 0 (position of file pointer in bytes)
b = f.read()                # Reads entire file as bytes object
f.tell()                    # Returns 23 (position of file pointer in bytes)
f.seek(-10, io.SEEK_END)    # Moves file pointer 10 bytes back from end of file and returns
                            # 13 (new position of file pointer in bytes)
f.tell()                    # Returns 13 (position of file pointer in bytes)
f.seek(-10, io.SEEK_CUR)    # Moves file pointer 10 bytes back from current position and
                            # returns 3 (position of file pointer in bytes)
f.seek(10)                  # Moves file pointer 10 bytes forward from start of file and
                            # returns 10 (position of file pointer in bytes)
f.seek(0)                   # Moves file pointer back to start of file and returns 0 (pos)
b = f.readline()            # Reads next line from file as bytes object incl. newline;
                            # returns empty bytes object if no more lines
f.tell()                    # Returns 17 (position of file pointer in bytes)
a = f.readlines()           # Reads all remaining lines from file as list of bytes objects
                            # incl. newlines
f.tell()                    # Returns 23 (position of file pointer in bytes)
f.seek(0)                   # Moves file pointer back to start of file and returns 0 (pos)
b = next(f)                 # Same as f.readline() but raises StopIteration at end of file
                            # (files are iterators with additional control via f.seek())
for b in f: print(b.decode(), end='')   # Iterates through remaining lines in file and
                                        # prints each one (after decoding to str)
f.close()                   # Closes file
                            # File still contains b'x = 10 (decimal)\nhi\nho\n'

# 'With' statement (good practice)
with open(fname, 'rb') as f:# 'with' ensures that file is closed when the 'with' suite of
    b = f.read()            # statements ends or raises an exception
                            # File still contains b'x = 10 (decimal)\nhi\nho\n'

# Appending to binary file (creating it if needed)
f = open(fname, 'ab')       # (Creates and) opens file in binary mode for appending
                            # (writing at end)
type(f)                     # Returns class io.BufferedWriter
isinstance(f, io.BufferedWriter)# Returns True
isinstance(f, io.BufferedIOBase)# Returns True (superclass of all binary files)
f.tell()                    # Returns 23 (position of file pointer; irrelevant for mode 'a')
f.write(b'hum\n')           # Appends 'hum\n' to end of file and returns 4 (bytes written)
f.tell()                    # Returns 27 (position of file pointer in bytes)
f.close()                   # Closes file
                            # File now contains b'x = 10 (decimal)\nhi\nho\nhum\n'

# Creating/truncating + writing & reading binary file
f = open(fname, 'w+b')      # Creates/truncates and opens file in binary mode for writing
                            # and reading
type(f)                     # Returns class io.BufferedRandom
isinstance(f, io.BufferedRandom)# Returns True
isinstance(f, io.BufferedIOBase)# Returns True (superclass of all binary files)
b = f.read()                # Reads entire file as bytes object: b'' because file was
                            # truncated
f.write(b'hi\n')            # Writes 'hi\n' to file and returns 3 (bytes written)
f.tell()                    # Returns 3 (position of file pointer in bytes)
f.close()                   # Closes file
                            # File now contains b'hi\n'

# Reading & writing binary file
f = open(fname, 'r+b')      # Opens existing file in binary mode for reading and writing
                            # (no truncating) from start of file
type(f)                     # Returns class io.BufferedRandom
isinstance(f, io.BufferedRandom)# Returns True
isinstance(f, io.BufferedIOBase)# Returns True (superclass of all binary files)
b = f.read(1)               # Reads 1 byte from file as bytes object: b'h'
f.tell()                    # Returns 1 (position of file pointer in bytes)
f.write(b'o')               # Writes 'o' to file - overwriting previous 'i' in 'hi\n' - and
                            # returns 1 (bytes written)
f.tell()                    # Returns 2 (position of file pointer in bytes)
f.seek(0)                   # Moves file pointer back to start of file and returns 0 (pos)
b = f.read()                # Reads entire file as bytes object: b'ho\n'
f.close()                   # Closes file
                            # File now contains b'ho\n'

# Appending to & reading binary file (creating it if needed)
f = open(fname, 'a+b')      # (Creates and) opens file in binary mode for appending and
                            # reading from end of file
type(f)                     # Returns class io.BufferedRandom
isinstance(f, io.BufferedRandom)# Returns True
isinstance(f, io.BufferedIOBase)# Returns True (superclass of all binary files)
f.tell()                    # Returns 3 (position of file pointer in bytes)
f.seek(0)                   # Moves file pointer back to start of file and returns 0 (pos)
b = f.read()                # Reads entire file as bytes object: b'ho\n'
f.tell()                    # Returns 3 (position of file pointer in bytes)
f.seek(0)                   # Moves file pointer back to start of file (doesn't affect
                            # append operation)
f.write(b'hum\n')           # Appends 'hum\n' to end of file and returns 4 (bytes written);
                            # file is now 'ho\nhum\n'
f.tell()                    # Should return 7 but returns wrong value after append when
                            # file pointer was not at end of file before append (bug?);
                            # file pointer is actually at position 7, though
f.read()                    # Reads and returns remainder of file: b'' because file pointer
                            # was at end of file
f.tell()                    # Returns 7 (position of file pointer in bytes)
f.close()                   # Closes file
                            # File now contains b'ho\nhum\n'

Expressions

Operators

All operators are listed below (highlighted) in precedence groups from highest to lowest.
Within each group, x op y op z == (x op y) op z, unless otherwise noted.
###########################################################################################
(2 + 3)                 # Returns 5
(2, 3, 4)               # Returns tuple (2, 3, 4)
[2, 3, 4]               # Returns list [2, 3, 4]
{2: 3, 4: 5}            # Returns dict {2: 3, 4: 5}
{2, 3, 4}               # Returns set {2, 3, 4}
###########################################################################################
x[2]                    # Returns item/value with index/key 2 from tuple/list/dict/string x
                        # (or other object with __getitem__() method)
x[1:6:2]                # Returns tuple/list/string by extracting slice (indices 1,3,5)
                        # from x (x can't be a dict)
x[2, 6:0:-2,            # Returns item(s) from x selected by expression list of indices/
    slice(6,0,-2), ...] # slices/Ellipsis; built-in types other than memoryview allow only
                        # 1 item between [] (not Ellipsis); numpy module allows any
f(2, 3, 4)              # Returns result of calling function f with given arguments
x.y                     # Returns member y of object x
###########################################################################################
await asyncio.sleep(1,9)# Suspends current coroutine, runs sleep coroutine (for 1 second),
                        # resumes this coroutine, and returns 9 (return value from sleep)
###########################################################################################
2**3                    # Returns 8 (exponentiation); 4**3**2 == 4**(3**2) == 262144;
                        # -2**-2 == -(2**(-2)) == -0.25
###########################################################################################
+1                      # Returns 1
-1                      # Returns -1
~1                      # Returns -2 (bitwise NOT)
###########################################################################################
2 * 3                   # Returns 6
'hi' * 3                # Returns 'hihihi' (sequence repetition)
x @ y                   # Matrix multiplication (supported by numpy module, not by built-in
                        # types)
-5 / 2                  # Returns -2.5 (always a float)
-5.0 // 2               # Returns -3.0 (floor div); for int: -5 // 2 == -3
-5 % 3                  # Returns 1 (modulo); x == floor(x/y)*y + x%y
'%0*d %X' % (5, 3, 12)  # Returns '00003 C' (string formatting)
###########################################################################################
2 + 3                   # Returns 5
[1,2] + [3,4]           # Returns [1, 2, 3, 4] (sequence concatenation)
2 - 3                   # Returns -1
{1,2,3} - {3,4}         # Returns {1,2} (set difference)
###########################################################################################
6 << 2                  # Returns 24 (left shift); x << y == x * 2**y; y >= 0
-6 >> 2                 # Returns -2 (right shift); x >> y == x // 2**y; y >= 0
###########################################################################################
6 & 3                   # Returns 2 (bitwise AND)
{1,2,3} & {3,4}         # Returns {3} (set intersection)
###########################################################################################
6 ^ 3                   # Returns 5 (bitwise XOR)
{1,2,3} ^ {3,4}         # Returns {1,2,4} (set symmetric difference)
###########################################################################################
6 | 3                   # Returns 7 (bitwise OR)
{1,2,3} | {3,4}         # Returns {1,2,3,4} (set union)
{'x':2,'y':3} | {'x':1,'z':4}   # Returns {'x':1,'y':3,'z':4} (dict union)
###########################################################################################
# Comparisons and tests (chainable, i.e. x < y < z means x < y and y < z (but y is
# evaluated only once)):
5 in (3, 5, 8)          # Returns True (membership test)
'a' not in 'hi'         # Returns True (non-membership test)
[] is []                # Returns False (identity test); these lists are not same object
                        # (but immutable objects might be identical, e.g. maybe 5 is 5)
{} is not {}            # Returns True (non-identity test)
2 < 3                   # Returns True
'ab' <= 'abc'           # Returns True (lexicographic Unicode comparison)
[2,3] > [2]             # Returns True (lexicographic comparison of corresponding items)
(3,) >= (2,5)           # Returns True (lexicographic comparison of corresponding items)
{2,3} > {3}             # Returns True (proper superset)
2 != '2'                # Returns True (different types generally compare unequal)
2 == 2.0                # Returns True (comparison works across different numeric types)
###########################################################################################
not 2                   # Returns False (boolean NOT)
###########################################################################################
2 and 3                 # Returns 3 (boolean AND, returns 1st arg if False, else 2nd arg;
                        # 2nd arg is not evaluated if 1st arg is returned)
###########################################################################################
0 or 'a'                # Returns 'a' (boolean OR, returns 1st arg if True, else 2nd arg;
                        # 2nd arg is not evaluated if 1st arg is returned)
###########################################################################################
2 if True else 3        # Returns 2 (conditional expression);
                        # 3rd arg is not evaluated if 1st arg is returned, and vice versa;
                        # (x if a else y if b else z) == (x if a else (y if b else z))
###########################################################################################
lambda x,y: x + y       # Returns anonymous function which will return sum of its 2 args
                        # (lambda expression)
###########################################################################################
(x := 2)                # Assignment expression; binds name x to object 2 and returns that
                        # object; needs parentheses when at top level of expression state-
                        # ment or at top level of right side of assignment statement (or
                        # certain other places)
###########################################################################################
#=========================================================================================#
# The "operators" below this point are not officially operators, but are included here to #
# show their effective relative precedence in the special contexts where they are valid   #
#=========================================================================================#
###########################################################################################
f(x=2)                  # Passes named argument to function f, i.e. binds f's formal param
                        # x to object 2 regardless of x's position in f's parameter list
f(*[4, 'hi'])           # Passes all items of given iterable as args to function f
                        # (same as f(4, 'hi'))
x,*y,z = range(5)       # Unpacks iterable (same as x,y,z = 0,[1,2,3],4)
f(**{'x': 2, 'y':3})    # Passes key/value pairs of given dictionary as named args to f
                        # (same as f(x=2, y=3))
###########################################################################################
2, 3                    # Returns tuple (2, 3) or expression list (see enclosing operators
                        # above and multiple assignments below for uses)
###########################################################################################
(yield 2, 3)            # Returns (2,3) to caller of next() or send() on generator produced
                        # by function containing this yield, suspends generator until next
                        # call to next()/send(), then returns None or send's argument as
                        # return value of yield expression; needs parentheses except when
                        # at top level of right side of assignment, or when used as a
                        # statement, so e.g. print((yield 2)) needs double parentheses;
                        # (yield) is same as (yield None)
(yield from range(5))   # Same as executing yield in a loop taking each item from range(5)
                        # until exhausted, then returning any StopIteration value from
                        # range(5) (i.e. None); passes send's argument to range(5)'s send
                        # (not supported)
###########################################################################################
# Normal assignments (chainable, i.e. x = y = z means x = z, then y = z (but z is
# evaluated only once); note the counterintuitive left-to-right assignment order!):
x = 2                   # Binds name x to object 2
o[0] = 2                # Sets item 0 of mutable object o to object 2
                        # (by calling o.__setitem__(0, 2))
o.x = 2                 # Sets attribute x of mutable object o to object 2
                        # (by calling o.__setattr__('x', 2))
x = o[x] = o[x]         # Chained assignments; same as tmp = o[x]; x = tmp; o[x] = tmp;
                        # if x and o are initially 0 and [1, 2], they become 1 and [1, 1]
                        # (o changes due to left-to-right assignment order)
x, o[0], o.x = 2, 2, 2  # Multiple assignments using expression lists
# Augmented assignments (not chainable; supports same targets as above except expression
# lists):
x += 2                  # Same as x = x + 2, but x is evaluated only once and updated in
                        # place if possible
x -= 2                  # x = x - 2 (but see x += 2)
x *= 2                  # x = x * 2 (but see x += 2)
x @= y                  # x = x @ y (not supported by built-in types)
x /= 2                  # x = x / 2 (but see x += 2)
x //= 2                 # x = x // 2 (but see x += 2)
x %= 2                  # x = x % 2 (but see x += 2)
x **= 2                 # x = x ** 2 (but see x += 2)
x >>= 2                 # x = x >> 2 (but see x += 2)
x <<= 2                 # x = x << 2 (but see x += 2)
x &= 2                  # x = x & 2 (but see x += 2)
x ^= 2                  # x = x ^ 2 (but see x += 2)
x |= 2                  # x = x | 2 (but see x += 2)
###########################################################################################

String Operations

'hello' + ' there\n'    # Returns 'hello there\n' ('\n' is a single newline)
'-' * 5                 # Returns '-----'
ord('A')                # Returns 65
chr(65)                 # Returns 'A'
ord('\u0E01')           # Returns 3585
chr(3585)               # Returns '\u0E01' (1 character)
ord('\U0001F60E')       # Returns 128526
chr(128526)             # Returns '\U0001F60E' (1 character)

String Formatting

There are several ways in which special codes embedded in a string can be replaced at run-time with objects converted to strings. The various methods are listed below in order of increasing flexibility.
Formatting with Template Class
Simple approach similar to Unix shell variables.
# Import Template class from string module
from string import Template
# Replace $-names in string with named arguments to substitute() method
Template('$n $x or ${x}y $$').substitute(x='thing', n=1)    # Returns '1 thing or thingy $'
Formatting with Modulo Operator
Old C printf-style formatting using '%' operator with many formatting options.
General Features
# Single value (replaces %d-specifier)
'answer = %d' % 42                                      # Returns 'answer = 42'
# Tuple of values (number of %-specifiers must match length of tuple; otherwise TypeError
# exception is raised)
'%s = %d' % ('answer', 42)                              # Returns 'answer = 42'
'%s = %d' % ('answer', 42, 1)                           # Raises TypeError exception
'%s = %d' % ('answer',)                                 # Raises TypeError exception
# Single value which is a tuple (needs to be embedded in another tuple)
'mytuple = %s' % ((1, 2, 3),)                           # Returns 'mytuple = (1, 2, 3)'
# Dictionary of values with key lookup (%()-specifiers can reference same key multiple
# times, and not all keys need to be referenced; if key doesn't exist, KeyError exception
# is raised)
'%(x)d + %(x)d = %(z)d' % {'x':2, 'y':3, 'z':4}         # Returns '2 + 2 = 4'
# Minimum field width and left/right alignment (right is default, left is specified with
# '-')
'%3s_%-3d_%7g_%3d' % ('A', 6, -1e7, 1234)               # Returns '  A_6  _ -1e+07_1234'
# Dynamic minimum field width ('*' is replaced with the tuple item before the one that
# replaces the whole %-specifier)
'%*s_%-*d' % (2, 'A', 3, 6)                             # Returns ' A_6  '
# Surrounding text (no whitespace needed)
'one%dall' % 4                                          # Returns 'one4all'
# '%%' produces a literal '%'
'level in %% = %d%%' % 50                               # Returns 'level in % = 50%'
Integer Formats
# Integer formats (all are identical, but %u is obsolete)
'%d %i %u' % (-12, -13, -14)                            # Returns '-12 -13 -14'
# Integer format with minimum field width and zero-padding (specified with '0' and field
# width before 'd'; '*' is dynamic field width replaced with tuple item)
'%3d_%03d_%0*d' % (-6, -7, 3, 8)                        # Returns ' -6_-07_008'
# Integer format with forced sign (specified with '+'; '-' aligns left)
'%+d_%+d_%-+3d_%+03d' % (6, -7, 8, 9)                   # Returns '+6_-7_+8 _+09'
# Integer format with forced room for sign (specified with ' '; '-' aligns left but leaves
# room for sign)
'% d_% d_%- 3d_% 03d' % (6, -7, 8, 9)                   # Returns ' 6_-7_ 8 _ 09'
# Hexadecimal formats ('#' prepends '0x' or '0X')
'%x %x %X %04x %#x %#04X' % (10, -11, 12, 13, 14, 15)   # Returns 'a -b C 000d 0xe 0X0F'
# Octal formats ('#' prepends '0o')
'%o %o %04o %#o %#05o' % (8, -9, 10, 11, 12)            # Returns '10 -11 0012 0o13 0o014'
# Integer format %d will round a float towards zero, but %x and %o will raise TypeError on
# float
'%d %d' % (4.9, -4.9)                                   # Returns '4 -4'
Floating Point Formats
# Floating point formats (%f/e/g never/always/sometimes uses exponential format)
'%f %e %g' % (1e-4, 2e-4, 3e-4)             # Returns '0.000100 2.000000e-04 0.0003'
'%f %e %g' % (1e-5, 2e-5, 3e-5)             # Returns '0.000010 2.000000e-05 3e-05'
'%f %e %g' % (1e5, 2e5, 3e5)                # Returns '100000.000000 2.000000e+05 300000'
'%f %e %g' % (1e6, 2e6, 3e6)                # Returns '1000000.000000 2.000000e+06 3e+06'
# Floating point formats with uppercase 'E' (%F is same as %f)
'%F %E %G' % (1e-5, 2e-5, 3e-5)             # Returns '0.000010 2.000000E-05 3E-05'
# Floating point formats %f/e with maximum number of digits after '.' (default 6)
'%.1f %.1e' % (11.16, 22.6)                             # Returns '11.2 2.3e+01'
# Floating point format %g with maximum total number of significant digits (default 6)
'%.2g %.2g %.2g %.2g' % (11.6, 2.26, .336, 446)         # Returns '12 2.3 0.34 4.5e+02'
# Floating point formats with minimum field width and zero-padding (specified with '0' and
# field width before '.')
'%4.1f_%04.1f_%08.1e_%04.2g' % (0.06, 1.16, 2.26, 3.36) # Returns ' 0.1_01.2_02.3e+00_03.4'
# Floating point formats with dynamic widths ('*' is replaced with next tuple item)
'%0*.*f_%0*.*e_%0*.*g' % (4, 1, 1.16,    8, 1, 2.26,
                          4, 2, 3.36)                   # Returns'01.2_02.3e+00_03.4'
# Floating point formats with forced sign (specified with '+')
'%+.1f_%+.1e_%+.2g' % (1.16, 2.26, 3.36)                # Returns '+1.2_+2.3e+00_+3.4'
'%+.1f_%+.1e_%+.2g' % (-1.16, -2.26, -3.36)             # Returns '-1.2_-2.3e+00_-3.4'
# Floating point formats with forced room for sign (specified with ' ')
'% .1f_% .1e_% .2g' % (1.16, 2.26, 3.36)                # Returns ' 1.2_ 2.3e+00_ 3.4'
'% .1f_% .1e_% .2g' % (-1.16, -2.26, -3.36)             # Returns '-1.2_-2.3e+00_-3.4'
# Alternate floating point formats (specified with '#' which always includes decimal point
# and for %g also zeros); note that floating point formats accept int arguments but raise
# TypeError on complex arguments
'%#.0f %#.0e %#g' % (1, 2, 3)                           # Returns '1. 2.e+00 3.00000'
'%.0f %.0e %g' % (1, 2, 3)                              # Returns '1 2e+00 3'
String Formats
# String formats (%s/r/a uses str()/repr()/ascii() conversion)
'%s %r %a' % (('rosé',) * 3)                            # Returns "rosé 'rosé' 'ros\\xe9'"
# String format of any value that has a string representation
'object = %s' % ((dict(x=5), list()),)                  # Returns "object = ({'x': 5}, [])"
# String format with maximum field width
'%.3s_%.3s' % ('A', 'ABCD')",                           # Returns 'A_ABC'
# String format with minimum and maximum field width (maximum is applied first, and may be
# smaller than minimum; '-' aligns left)
'%-2.3s_%2.3s_%3.2s' % ('A', 'ABCD', 'ABC')",           # Returns 'A _ABC_ AB'
# String format with dynamic minimum/maximum field width ('*' is replaced with next tuple
# item)
'%*s_%.*s_%*.*s' % (3, 'A', 2, 'ABC', 3, 2, 'ABC')",    # Returns '  A_AB_ AB'
Character Formats
# Single-character format (with or without minimum field width)
'%c_%3c' % ('A', 0xe9)                                  # Returns 'A_  é'
Formatting with str.format Method
Newer str.format() method with more formatting and object access options than printf-style.
General Features
# Single argument to format method (replaces {}-specifier)
'answer = {}'.format(42)                                # Returns 'answer = 42'
# Multiple positional arguments (replace {}-specifiers sequentially; number of arguments
# must be equal to or greater than number of {}-specifiers, otherwise IndexError exception
# is raised; extra arguments are ignored)
'{} = {}'.format('answer', 42, 43)                      # Returns 'answer = 42'
'{} = {}'.format('answer',)                             # Raises IndexError exception
# Identified positional arguments ('{0}' refers to first argument, '{1}' to second, etc.;
# same argument can be referenced multiple times, and not all arguments need to be
# referenced; if argument doesn't exist, IndexError exception is raised)
'{1} + {1} = {0}'.format(4, 2, 8)                       # Returns '2 + 2 = 4'
'{1} + {1} = {0}'.format(4)                             # Raises IndexError exception
# Identified named arguments (same argument can be referenced multiple times, and not all
# arguments need to be referenced; if argument doesn't exist, KeyError exception is
# raised)
'{x} + {x} = {z}'.format(x=2, y=3, z=4)                 # Returns '2 + 2 = 4'
'{x} + {x} = {z}'.format(y=3, z=4)                      # Raises KeyError exception
# Identified named arguments can be mixed with either identified or sequential positional
# arguments, but ValueError exception will be raised if there are both identified and
# sequential positional arguments
'{x} + {x} = {1}'.format(8, 4, y=3, x=2)                # Returns '2 + 2 = 4'
'{x} + {x} = {}'.format(4, y=3, x=2)                    # Returns '2 + 2 = 4'
'{0} + {0} = {}'.format(2, 4)                           # Raises ValueError exception
# Accessing indexed items and attributes of arguments
'{[a][1].imag} {.stop}'.format({'a':[7, 8+9j]}, range(5))       # Returns '9.0 5'
'{0[a][1].imag} {x.stop}'.format({'a':[7, 8+9j]}, x=range(5))   # Returns '9.0 5'
# Minimum field width and left/center/right alignment (specified with '<'/'^'/'>';
# right is default for numbers, left for others, so this is different than when
# formatting with the '%' operator)
'{:3s}_{:3d}_{:7g}_{:3d}'.format('A', 6, -1e7, 1234)    # Returns 'A  _  6_ -1e+07_1234'
'{:>3s}_{:^3d}_{:<7g}'.format('A', 6, -1e7)             # Returns '  A_ 6 _-1e+07 '
# Minimum field width and specified fill character (alignment must also be specified)
'{:x<3s}_{:>>5d}'.format('A', -6)                       # Returns 'Axx_>>>-6'
# Dynamic minimum field width (arguments are taken in the order of the '{' characters, so
# this makes the order different than for the '%*' specifier when
# formatting with the '%' operator)
'{:{}s}_{:<{}d}'.format('A', 2, 6, 3)                   # Returns 'A _6  '
# Dynamic everything (same as above but now with all specifier parts (after colon) given as
# arguments)
'{:{}{}}_{:{}}'.format('A', 2, 's', 6, '<3d')           # Returns 'A _6  '
'{0:{1}{2}}_{3:{4}}'.format('A', 2, 's', 6, '<3d')      # Returns 'A _6  '
# Surrounding text (no whitespace needed)
'one{}all'.format(4)                                    # Returns 'one4all'
# '{{' and '}}' produce literal '{' and '}'
'{{br{}s}}'.format('ace')                               # Returns '{braces}'
Integer Formats
# Integer format ('{:d}' produces same result as '{}' but will raise ValueError exception
# if argument is not an int (e.g. a float))
'{0} {0:d}'.format(-12)                                 # Returns '-12 -12'
'{:d}'.format(-12.0)                                    # Raises ValueError exception
# Integer format with minimum field width and zero-padding (specified with '0' and field
# width after ':'; nested '{}' is dynamic field width replaced with one of the arguments)
'{:3d}_{:03d}_{:0{}d}'.format(-6, -7, 8, 3)             # Returns ' -6_-07_008'
# Integer format with forced sign (specified with '+'; '<' aligns left)
'{:+d}_{:+d}_{:<+3d}_{:+03d}'.format(6, -7, 8, 9)       # Returns '+6_-7_+8 _+09'
# Integer format with forced room for sign (specified with ' '; '<' aligns left but leaves
# room for sign)
'{: d}_{: d}_{:< 3d}_{: 03d}'.format(6, -7, 8, 9)       # Returns ' 6_-7_ 8 _ 09'
# Integer format with minimum field width, left aligned sign, and right aligned number
# (specified with '='; this is the default when using zero-padding; 'x' is an arbitrary
# fill character)
'{:=3d}_{:=+3d}_{:x=5d}'.format(-6, 7, -8)              # Returns '- 6_+ 7_-xxx8'
# Hexadecimal formats ('#' prepends '0x' or '0X')
'{:x} {:x} {:X} {:04x} {:#x} {:#04X}'.format(
                            10, -11, 12, 13, 14, 15)    # Returns 'a -b C 000d 0xe 0X0F'
# Octal formats ('#' prepends '0o')
'{:o} {:o} {:04o} {:#o} {:#05o}'.format(
                            8, -9, 10, 11, 12)          # Returns '10 -11 0012 0o13 0o014'
# Binary formats ('#' prepends '0b')
'{:b} {:b} {:04b} {:#b} {:#05b}'.format(2, -3, 4, 5, 3) # Returns '10 -11 0100 0b101 0b011'
# Integer format with thousands separator (specified with ',' or '_', the former is only
# valid for 'd')
'{:09,d} {:_d}'.format(6, 70000)                        # Returns '0,000,006 70_000'
'{:_x} {:_o} {:_b}'.format(0x89abc, 0o34567, 31)        # Returns '8_9abc 3_4567 1_1111'
'{:,x}'.format(1)                                       # Raises ValueError exception
'{:,o}'.format(1)                                       # Raises ValueError exception
'{:,b}'.format(1)                                       # Raises ValueError exception
Floating Point Formats
# Floating point formats (f/e/g never/always/sometimes uses exponential format)
'{:f} {:e} {:g}'.format(1e-4, 2e-4, 3e-4)   # Returns '0.000100 2.000000e-04 0.0003'
'{:f} {:e} {:g}'.format(1e-5, 2e-5, 3e-5)   # Returns '0.000010 2.000000e-05 3e-05'
'{:f} {:e} {:g}'.format(1e5, 2e5, 3e5)      # Returns '100000.000000 2.000000e+05 300000'
'{:f} {:e} {:g}'.format(1e6, 2e6, 3e6)      # Returns '1000000.000000 2.000000e+06 3e+06'
'{0:f} {0:e} {0:g}'.format(float('-inf'))   # Returns '-inf -inf -inf'
'{0:f} {0:e} {0:g}'.format(float('nan'))    # Returns 'nan nan nan'
# Floating point format '{}' is similar to '{:g}' with a few exceptions
'{0} {0:g}'.format(1.00)                    # Returns '1.0 1'
'{0} {0:g}'.format(1.23456789012345678)     # Returns '1.2345678901234567 1.23457'
'{0} {0:g}'.format(1.2e15)                  # Returns '1200000000000000.0 1.2e+15'
'{0} {0:g}'.format(1.2e16)                  # Returns '1.2e+16 1.2e+16'
'{0} {0:g}'.format(1.2e-4)                  # Returns '0.00012 0.00012'
'{0} {0:g}'.format(1.2e-5)                  # Returns '1.2e-05 1.2e-05'
# Floating point formats with uppercase 'E' (F is same as f, except for inf and nan)
'{:F} {:E} {:G}'.format(1e-5, 2e-5, 3e-5)   # Returns '0.000010 2.000000E-05 3E-05'
'{0:F} {0:E} {0:G}'.format(float('-inf'))   # Returns '-INF -INF -INF'
'{0:F} {0:E} {0:G}'.format(float('nan'))    # Returns 'NAN NAN NAN'
# Floating point percentage format (similar to format f, but multiplies by 100 and appends
# '%')
'{:%} {:%}'.format(1, 1e-5)                 # Returns '100.000000% 0.001000%'
# Floating point formats f/e/% with maximum number of digits after '.' (default 6)
'{:.1f} {:.1e} {:.1%}'.format(11.16, 22.6, .3336)       # Returns '11.2 2.3e+01 33.4%'
# Floating point format g with maximum total number of significant digits (default 6)
'{:.2g} {:.2g} {:.2g} {:.2g}'.format(
                                11.6, 2.26, .336, 446)  # Returns '12 2.3 0.34 4.5e+02'
# Floating point formats with minimum field width and zero-padding (specified with '0' and
# field width before '.')
'{:4.1f}_{:04.1f}_{:08.1e}_{:04.2g}'.format(
                                0.06, 1.16, 2.26, 3.36) # Returns ' 0.1_01.2_02.3e+00_03.4'
# Floating point formats with dynamic widths (nested '{}' is replaced with next argument)
'{:0{}.{}f}_{:0{}.{}e}_{:0{}.{}g}'.format(1.16, 4, 1,
                                          2.26, 8, 1,
                                          3.36, 4, 2)   # Returns '01.2_02.3e+00_03.4'
# Floating point formats with forced sign (specified with '+')
'{:+.1f}_{:+.1e}_{:+.2g}'.format(1.16, 2.26, 3.36)      # Returns '+1.2_+2.3e+00_+3.4'
'{:+.1f}_{:+.1e}_{:+.2g}'.format(-1.16, -2.26, -3.36)   # Returns '-1.2_-2.3e+00_-3.4'
# Floating point formats with forced room for sign (specified with ' ')
'{: .1f}_{: .1e}_{: .2g}'.format(1.16, 2.26, 3.36)      # Returns ' 1.2_ 2.3e+00_ 3.4'
'{: .1f}_{: .1e}_{: .2g}'.format(-1.16, -2.26, -3.36)   # Returns '-1.2_-2.3e+00_-3.4'
# Alternate floating point formats (specified with '#' which always includes decimal point
# and for format g also zeros); note that floating point formats accept int and complex
# arguments
'{:#.0f} {:#.0e} {:#g}'.format(1+0j, 2, 3)              # Returns '1.+0.j 2.e+00 3.00000'
'{:.0f} {:.0e} {:g}'.format(1+0j, 2, 3)                 # Returns '1+0j 2e+00 3'
# Floating point format with thousands separator (specified with ',' or '_')
'{:09,.0f} {:09,.0e} {:,g}'.format(6, 7, 8e4)       # Returns '0,000,006 0,007e+00 80,000'
'{:09_.0f} {:09_.0e} {:_g}'.format(6, 7, 8e4)       # Returns '0_000_006 0_007e+00 80_000'
String Formats
# String format ('{:s}' produces same result as '{}' but will raise ValueError exception if
# argument is not a string
'{0} {0:s}'.format('ABC')                               # Returns 'ABC ABC'
'{:s}'.format(42)                                       # Raises ValueError exception
# String format with forced conversion of argument to string (!s/r/a uses
# str()/repr()/ascii() conversion)
'{!s:s}'.format(42)                                     # Returns '42'
'{0!s} {0!r} {0!a}'.format('rosé')                      # Returns "rosé 'rosé' 'ros\\xe9'"
# String format with maximum field width
'{:.3s}_{:.3s}'.format('A', 'ABCD')                     # Returns 'A_ABC'
# String format with minimum and maximum field width (maximum is applied first, and may be
# smaller than minimum; '>' aligns right)
'{:>2.3s}_{:2.3s}_{:3.2s}'.format('A', 'ABCD', 'ABC')               # Returns ' A_ABC_AB '
# String format with dynamic minimum/maximum field width (nested '{}' is replaced with next
# argument)
'{:{}s}_{:.{}s}_{:{}.{}s}'.format('A', 3, 'ABC', 2, 'ABC', 3, 2)    # Returns 'A  _AB_AB '
Character Formats
# Single-character format (with or without minimum field width); raises ValueError if
# argument is not an int
'{:c}_{:3c}'.format(65, 0xe9)                           # Returns 'A_  é'
'{:c}'.format('A')                                      # Raises ValueError exception
'{:c}'.format(65.0)                                     # Raises ValueError exception
Formatting with F-String Literals
Latest f-string approach (formatted string literals added in Python 3.6) with the same formatting options as str.format() but (almost) arbitrary expressions can be specified within the format string. However, unlike the other methods above, f-strings cannot be stored for later formatting - they are immediately evaluated into string objects.
# Misc initializations for use below
k = 1
n = 4
x = 5+6j

# F-strings are prefixed with 'f' or 'F' (no difference) and support same formatting as
# str.format method
f'{n:03d} {x.real:0{n}.{k}f} {x.imag!s:.2s}'                # Returns '004 05.0 6.'
F'{x:-^{k}{n}}'                                             # Returns '----(5+6j)----'
# Arbitrary expressions are allowed in f-strings (unlike in str.format)
f'There {"is" if k==1 else "are"} {k} item{"s"[:k!=1]}'     # Returns 'There is 1 item'
f'There {"is" if n==1 else "are"} {n} item{"s"[:n!=1]}'     # Returns 'There are 4 items'
# Backslashes and quotes (even the same type of quotes as those delimiting the f-string)
# can be used freely (since Python 3.12)
f'{'A\\B':\t^7}\n'                                          # Returns '\t\tA\\B\t\t\n'
# The expressions themselves can be included in the result (specified with '=')
f'{n=},{ n * 2 = :+d}'                                      # Returns 'n=4, n * 2 = +8'

Conditional Expressions

r = x if a else y       # Assigns x to r if a is True (y is not evaluated),
                        # otherwise assigns y to r (x is not evaluated)
r = x if a \
    else y if b \
    else z              # (Note: backslashes used to break single line)

Regular Expressions

Functions and Methods

Compiled regular expression objects are highlighted below for clarity, as many module functions (re.xxx()) have compiled object method equivalents (myobj.xxx()), but do not support quite the same parameters (module functions support flags; compiled object methods support start/end indexes).
import re                   # Imports regular expression module

# Compile and match
rs1 = r'(\w)(.*?)([0-5](Z)?)'   # Assigns a (raw) string containing a regular expression
rc1 = re.compile(rs1)       # Compiles regular expr for faster repeated execution
type(rc1)                   # Returns class re.Pattern
isinstance(rc1, re.Pattern) # Returns True
re.compile(r'(')            # Raises re.error exception due to bad regular expression
s1 = 'abc950'               # Some string to search for matches
m1 = re.match(rs1, s1)      # Finds a match at start of string s1, and returns
                            # a match object - or None if no match was found
m1 = rc1.match(s1)          # Same as above, but uses compiled regular expression object
if m1:                      # If a match was found, then...
    print(m1.group())       # Prints 'abc95' - the part of s1 matched by rs1
    print(m1[0])            # Prints 'abc95' (same as above)
    print(m1.group(1))      # Prints 'a' - captured by 1st '()' in rs1
    print(m1[1])            # Prints 'a' (same as above)
    print(m1.group(3, 2))   # Prints "('5', 'bc9')" - from 3rd & 2nd '()' in rs1
    print(m1.groups())      # Prints "('a', 'bc9', '5', None)" - from all '()' in rs1
                            # Note: non-matching '()' returns None
    print(m1.start())       # Prints 0 - start index of match in s1
    print(m1.end())         # Prints 5 - end index + 1 of match in s1
    print(m1.span())        # Prints '(0, 5)' - start and end of match
    print(m1.start(2))      # Prints 1 - start index of 2nd '()' capture in s1
    print(m1.end(2))        # Prints 4 - end index + 1 of 2nd '()' capture in s1
    print(m1.span(2))       # Prints '(1, 4)' - start and end of 2nd '()'

# Fullmatch
m1 = rc1.fullmatch(s1)      # Like rc1.match() but finds a match to whole string s1
print(m1.group())           # Prints 'abc950' - the part of s1 matched by rs1
print(m1.groups())          # Prints "('a', 'bc95', '0', None)" - from all '()' in rs1

# Search
s2 = '.20 391Z'                 # A new string to search for matches
m2 = rc1.search(s2)             # Finds first match in string s2
if m2:
    print(m2.groups())          # Prints "('2', '', '0', None)" - from all '()' in rs1
m2 = rc1.search(s2, m2.end())   # Finds first match in string s2 starting from previous end
if m2:
    print(m2.groups())          # Prints "('3', '9', '1Z', 'Z')" - from all '()' in rs1

# Finditer
ri1 = rc1.finditer(s2)          # Returns an iterator
type(ri1)                       # Returns class with name 'callable_iterator'
print(next(ri1).groups())       # Prints "('2', '', '0', None)"
print(next(ri1).groups())       # Prints "('3', '9', '1Z', 'Z')"

# Findall
print(rc1.findall(s2))          # Prints "[('2', '', '0', ''), ('3', '9', '1Z', 'Z')]"
                                # Note: non-matching '()' returns '' (not None)
rs3 = r'\d\d'                   # String containing regular expression with no '()'
s3 = ' 12.345-6789a'            # A new string to search for matches
print(re.findall(rs3, s3))      # Prints "['12', '34', '67', '89']"

# Split
print(re.split(rs3, s3))        # Prints "[' ', '.', '5-', '', 'a']"

# Sub and subn (substitution)
print(re.sub(rs3, 'xy', s3))        # Prints ' xy.xy5-xyxya'
print(re.subn(rs3, 'xy', s3))       # Prints "(' xy.xy5-xyxya', 4)" (4 substitutions)
print(rc1.sub(r'_\1_\g<1>4_', s1))  # Prints '_a_a4_0' (\1 = \g<1> = 1st captured group)
print(rc1.sub(r'(\g<0>)', s1))      # Prints '(abc95)0' (\g<0> = whole match)
def f(m): return m.group(1).upper() # Function that returns replacement string for a match
print(rc1.sub(f, s1))               # Prints 'A0' (calls function f for each match)

Start/End Index Parameters

Start/end indices may be supplied to compiled regular expression object methods to restrict their operation to a portion of the input string.

WARNING: ONLY compiled object methods (e.g. rc1.match()) support start/end index parameters! If used with module functions (e.g. re.match()), these parameters may be interpreted as flags and you may not get an error - just strange behavior!

import re

rs1 = r'(\w)(.*?)([0-5](Z)?)'   # Assigns a (raw) string containing a regular expression
rc1 = re.compile(rs1)       # Compiles regular expr
s1 = 'abc950'
m1 = rc1.match(s1, 2)       # Finds a match at start of string s1[2:]
if m1:                      # If a match was found, then...
    print(m1.groups())      # Prints "('c', '9', '5', None)" - from all '()' in rs1
print(rc1.match(s1, 1, 4))  # Prints 'None' because rs1 does not match s1[1:4] ('bc9')

Flags

Flags may be supplied to regular expression module functions to modify the behavior of a regular expression.

WARNING: ONLY module functions (e.g. re.match()) support flags! If used with compiled object methods (e.g. rc1.match()), the flags may be interpreted as indexes and you may not get an error - just strange behavior!

import re

# Flags:
#   re.I (or re.IGNORECASE): case-insensitive matching;
#   re.M (or re.MULTILINE):  '^' and '$' match start/end of each line within string;
#   re.S (or re.DOTALL):     '.' matches also newline

rs4 = r'^hi.\w+$'
rc4 = re.compile(rs4, re.I|re.M|re.S)    # Applies flags I, M, and S to rs4 in rc4
s4 = 'Hi\nHo\nHi\nHUM'
print(rc4.findall(s4))                   # Prints "['Hi\nHo', 'Hi\nHUM']"

# Flags:
#   re.X (or re.VERBOSE): extra whitespace and comments allowed in regular expression

rs5 = r"""      # This is a verbose regular expression string (these comments are inside it)
    (           # Start of 1st capturing group
        \w      # Word character
    )           # End of 1st capturing group
    (           # Start of 2nd capturing group
        .*?     # Minimal sequence of 0 or more characters except \n
    )           # End of 2nd capturing group
    (           # Start of 3rd capturing group
        [0-5]   # Digit in range 0-5
        (       # Start of 4th capturing group
            Z   # 'Z' character
        )       # End of 4th capturing group
        ?       # 4th group is optional
    )           # End of 3rd capturing group
"""
m1 = re.match(rs5, 'abc950', re.X)  # Applies flag X to rs5; returns the same result as
                # re.match(r'(\w)(.*?)([0-5](Z)?)', 'abc950') - see earlier

# Flags:
# (These character set flags cannot be combined with each other)
#   re.A (or re.ASCII):   \d, \s, \w classes are restricted to ASCII characters (this also
#                         affects \b, \B, \D, \S, \W);
#                         this is the default for bytes-type regular expressions;
#   re.L (or re.LOCALE):  \w class and re.I flag depend on the current locale (discouraged);
#                         only allowed for bytes-type regular expressions;
#   re.U (or re.UNICODE): \d, \s, \w classes follow unicode rules (this also affects
#                         \b, \B, \D, \S, \W);
#                         this is the default for str-type regular expressions;
#                         only allowed for str-type regular expressions

re.search(r'\d\s\w', '३\u2003Ω 3 z')[0]       # Returns '३\u2003Ω'
re.search(r'\d\s\w', '३\u2003Ω 3 z', re.A)[0] # Returns '3 z'

# Flags:
#   re.NOFLAG: no effect; convenient name for the value 0 which represents no flags
There is also a DEBUG flag which prints detailed - but somewhat cryptic - information about the regular expression:
import re

# Flags:
#   re.DEBUG:  debug info is printed

re.match(r'\w+', 'ab', re.DEBUG)        # Returns 'ab' and prints the debug info below
MAX_REPEAT 1 MAXREPEAT
  IN
    CATEGORY CATEGORY_WORD

 0. INFO 4 0b0 1 MAXREPEAT (to 5)
 5: REPEAT_ONE 9 1 MAXREPEAT (to 15)
 9.   IN 4 (to 14)
11.     CATEGORY UNI_WORD
13.     FAILURE
14:   SUCCESS
15: SUCCESS

Example: String Tokenizer

Here's an example of a string tokenizer which tries to match various regular expressions at the same position in the input string until a match is found, then repeats the process at the position following the match:
import re                           # Imports regular expression module

# Prepare a sequence of compiled regular expressions and associated functions;
# each function converts the matched string to a token
token_matchers = [(func, re.compile(regx)) for func, regx in (
    (float, r'\s*((?:\d+\.\d*|\.\d+)(?:[eE][-+]?\d+)?)\s*'),
    (int,   r'\s*(\d+)\s*'),
    (str,   r'\s*(\*\*|\W)\s*'))]

# Define a function which parses a string and returns a list of found tokens
def get_tokens(in_str):
    tokens = []
    pos = 0                                     # Sets matching position to start of string
    while pos < len(in_str):                 # Repeats while not at end of string
        for func, regx in token_matchers:       # Tries each regular expr in turn
            if m := regx.match(in_str, pos):    # When a regular expr matches at pos:
                tokens.append(func(m.group(1))) # Uses func to convert matched string to
                                                # token and adds it to list of tokens
                pos = m.end()                   # Moves position to next char after match
                break                           # Ends for-loop
        else:                                   # If for-loop found nothing, then
            raise ValueError(f'Bad token: {in_str[pos:]!r}')    # raises exception
    return tokens                               # Returns list of tokens to caller of
                                                # get_tokens

get_tokens('23*(.45 + -6.7e-2)')    # Returns [23, '*', '(', 0.45, '+', '-', 0.067, ')']

Regular Expression Language

This section details the regular expression language used in the string passed as the first argument to e.g. re.match. In the following examples, these phrases are used: Note that re.match(REGEX, INPUT) only finds a match at the start of INPUT.
# Basic combinations
r'XY'       # X then Y; binds tighter than X|Y but looser than quantifiers;
            #   r'abc' matches 'abc' in 'abcd', but not 'acb'
r'X|Y'      # X or Y;
            #   r'ab|c|d' matches 'ab' in 'abcd', 'c' in 'cd', 'd' in 'de', but not 'ac'
# Character classes
r'.'        # Any single character - except newline if not in dot-all mode;
            #   r'.' matches 'a' in 'ab', ' ' in ' a', '\r' in '\r', but not '\n';
            #   re.match(r'.', '\n', re.DOTALL)[0] returns '\n'
r'\d'       # Any digit character (as defined by o.isdecimal(), see section
            # Container Object Attributes);
            #   r'\d' matches '7' in '78', '\u0e52' in '\u0e52', but not '\u00b2'
r'\D'       # Any non-digit character;
            #   r'\D' matches 'a' in 'ab', '\n' in '\n', but not '7'
r'\s'       # Any whitespace character;
            #   r'\s' matches ' ', '\t', '\n', '\r', '\f', '\v', but not '.'
r'\S'       # Any non-whitespace character;
            #   r'\S' matches '.', but not ' ', '\t', '\n', '\r', '\f', '\v'
r'\w'       # Any word character (as defined by o.isalnum() plus '_', see section
            # Container Object Attributes);
            #   r'\w' matches 'a', 'A', '7', '_', '\u00b2', but not ' ', '.', '-'
r'\W'       # Any non-word character;
            #   r'\W' matches ' ', '.', '-', but not 'a', 'A', '7', '_', '\u00b2'
r'[XYA-C]'  # Any single character that is X, Y, or in range A-C;
            #   r'[-a\]]' matches '-' in '-4', 'a' in 'ab', ']' in ']a', but not 'b';
            #   r'[a-c6-8]' matches 'b' in 'bc', 'c' in 'cd', '7' in '78', but not 'd';
            #   r'[\d\s]' matches '7' in '78', ' ' in ' a', but not 'a';
r'[^XYA-C]' # Any single character that is NOT X, Y, or in range A-C;
            #   r'[^a-c3]' matches 'd' in 'de', '\n' in '\n', but not 'a', 'b', 'c', or '3'
# Exact quantifier
r'X{3}'     # Sequence of 3 Xs;
            #   r'ab{3}' matches 'abbb' in 'abbbbb', but not 'abb'

# Greedy quantifiers
r'X?'       # Largest sequence of 0 or 1 X;
            #   r'ab?' matches 'a' in 'a', 'ab' in 'abbb'
r'X*'       # Largest sequence of 0 or more Xs;
            #   r'ab*' matches 'a' in 'a', 'abbb' in 'abbb'
r'X+'       # Largest sequence of 1 or more Xs;
            #   r'ab+' matches 'ab' in 'ab', 'abbb' in 'abbb', but not 'a'
r'X{2,}'    # Largest sequence of 2 or more Xs;
            #   r'ab{2,}' matches 'abb' in 'abb', 'abbbb' in 'abbbb', but not 'ab'
r'X{,3}'    # Largest sequence of 0 to 3 Xs;
            #   r'ab{,3}' matches 'a' in 'a', 'abbb' in 'abbbb'
r'X{2,3}'   # Largest sequence of 2 to 3 Xs;
            #   r'ab{2,3}' matches 'abb' in 'abb', 'abbb' in 'abbbb', but not 'ab'

# Minimal quantifiers
r'X??'      # Smallest sequence of 0 or 1 X;
            #   r'ab??' matches 'a' in 'abbb';
            #   r'ab??c' matches 'ac' in 'ac', 'abc' in 'abc', but not 'abbc'
r'X*?'      # Smallest sequence of 0 or more items;
            #   r'ab*?' matches 'a' in 'abbb';
            #   r'ab*?c' matches 'ac' in 'ac', 'abbbc' in 'abbbc'
r'X+?'      # Smallest sequence of 1 or more items;
            #   r'ab+?' matches 'ab' in 'abbb';
            #   r'ab+?c' matches 'abc' in 'abc', 'abbbc' in 'abbbc', but not 'ac'
r'X{2,}?'   # Smallest sequence of 2 or more Xs;
            #   r'ab{2,}?' matches 'abb' in 'abbb';
            #   r'ab{2,}?c' matches 'abbc' in 'abbc', 'abbbc' in 'abbbc', but not 'abc'
r'X{,3}?'   # Smallest sequence of 0 to 3 Xs;
            #   r'ab{,3}?' matches 'a' in 'abbb';
            #   r'ab{,3}?c' matches 'ac' in 'ac', 'abbbc' in 'abbbc', but not 'abbbbc'
r'X{2,3}?'  # Smallest sequence of 2 to 3 Xs;
            #   r'ab{2,3}?' matches 'abb' in 'abbb';
            #   r'ab{2,3}?c' matches 'abbc' in 'abbc', 'abbbc' in 'abbbc', but not 'abc' or
            #   'abbbbc'

# Greedy and possessive quantifiers
r'X?+'      # Largest sequence of 0 or 1 X with no back-tracking;
            #   r'ab?+' matches 'a' in 'a', 'ab' in 'abbb';
            #   r'ab?+b' matches 'abb' in 'abb', but not 'ab'
r'X*+'      # Largest sequence of 0 or more Xs with no back-tracking;
            #   r'ab*+' matches 'a' in 'a', 'abbb' in 'abbb';
            #   r'ab*+b' does not match anything
r'X++'      # Largest sequence of 1 or more Xs with no back-tracking;
            #   r'ab++' matches 'ab' in 'ab', 'abbb' in 'abbb', but not 'a';
            #   r'ab++b' does not match anything
r'X{2,}+'   # Largest sequence of 2 or more Xs with no back-tracking;
            #   r'ab{2,}+' matches 'abb' in 'abb', 'abbb' in 'abbb', but not 'ab';
            #   r'ab{2,}+b' does not match anything
r'X{,3}+'   # Largest sequence of 0 to 3 Xs with no back-tracking;
            #   r'ab{,3}+' matches 'a' in 'a', 'abbb' in 'abbbb';
            #   r'ab{,3}+b' matches 'abbbb' in 'abbbb', but not 'abbb'
r'X{2,3}+'  # Largest sequence of 2 to 3 Xs with no back-tracking;
            #   r'ab{2,3}+' matches 'abb' in 'abb', 'abbb' in 'abbbb', but not 'ab';
            #   r'ab{2,3}+b' matches 'abbbb' in 'abbbb', but not 'abbb'
# Zero-width assertions (consuming no characters)
r'^'        # Matches start of string (or after each newline in multiline mode);
            #   re.findall(r'^\d', '23\n4') returns ['2'];
            #   re.findall(r'^\d', '23\n4', re.MULTILINE) returns ['2', '4']
r'\A'       # Matches start of string (regardless of multiline mode);
            #   re.findall(r'\A\d', '23\n4') returns ['2'];
            #   re.findall(r'\A\d', '23\n4', re.MULTILINE) returns ['2']
r'$'        # Matches end of string or before newline at end of string (or before each
            # newline in multiline mode);
            #   both r'\n$' and r'$\n' match '\n';
            #   re.findall(r'$', '\n') returns ['', ''];
            #   re.findall(r'\d$', '2\n34') returns ['4'];
            #   re.findall(r'\d$', '2\n34', re.MULTILINE) returns ['2', '4']
r'\Z'       # Matches end of string (regardless of multiline mode);
            #   r'\n\Z' matches '\n', whereas r'\Z\n' matches nothing;
            #   re.findall(r'\Z', '\n') returns [''];
            #   re.findall(r'\d\Z', '2\n34') returns ['4'];
            #   re.findall(r'\d\Z', '2\n34', re.MULTILINE) returns ['4']
r'\b'       # Matches start or end of word (\w characters);
            #   r'\b.+?\b' matches 'a2_Ω' in 'a2_Ω,a';
            #   re.findall(r'\b.+?\b', 'A2! _Ω') returns ['A2', '! ', '_Ω']
r'\B'       # Matches anywhere but start or end of word (\w characters);
            #   r'.\B.+?\B' matches 'a2,_ Ω' in 'a2,_ Ωa';
            #   re.findall(r'\B.+?\B', 'A2! _Ω') returns ['2!', ' _']
r'(?=X)'    # Positive lookahead assertion matching current position if a match for X
            # starts at this position;
            #   r'ab(?=cd)' matches 'ab' in 'abcd', but not 'abdc'
r'(?!X)'    # Negative lookahead assertion matching current position if no match for X
            # starts at this position;
            #   r'ab(?!cd)' matches 'ab' in 'abdc', but not 'abcd'
r'(?<=X)'   # Positive lookbehind assertion matching current position if a match for X
            # ends at this position;
            #   re.findall(r'(?<=a).', 'aabacd') returns ['a', 'b', 'c']
r'(?<!X)'   # Negative lookbehind assertion matching current position if no match for X
            # ends at this position;
            #   re.findall(r'(?<!a).', 'aabacd') returns ['a', 'a', 'd']
# Groups
r'(X)'          # Capturing group matching X;
                #   r'(.*?)(\d)' matches 'ab2' in 'ab23' and captures 'ab' and '2', i.e.:
                #     re.match(r'(.*?)(\d)', 'ab23').groups() returns ('ab', '2');
                #   groups may be nested and capture in the order of their '(', e.g.:
                #     re.match(r'((a(b))c)d', 'abcd').groups() returns ('abc', 'ab', 'b');
                #   if a group doesn't contribute to the match, it captures None, e.g.:
                #     re.match(r'a(.)*|(.)', 'a').groups() returns (None, None);
                #   if a group is repeated, only the last match is captured, e.g.:
                #     re.match(r'(\s)*(\d)*(.)*', '2ab').groups() returns (None, '2', 'b');
                #   note: putting the quantifier inside the group is entirely different:
                #     re.match(r'(\s*)(\d*)(.*)', '2ab').groups() returns ('', '2', 'ab')
r'(?P<name>X)'  # Named capturing group matching X;
                #   r'(?P<first>\d*)' captures '23' in '23a' and names it 'first', i.e.:
                #     re.match(r'(?P<first>\d*)', '23a')['first'] returns '23';
                #     re.match(r'(?P<first>\d*)', '23a')[1] returns '23'
r'(?:X)'        # (Non-capturing) group matching X;
                #   r'(?:aa)+' matches 'aaaa' in 'aaaaa' and captures nothing, i.e.:
                #     re.match(r'(?:aa)+', 'aaaaa')[0] returns 'aaaa';
                #     re.match(r'(?:aa)+', 'aaaaa').groups() returns ()
r'(?auLimsx-imsx:X)'    # Flagged group matching X; each letter corresponds to a flag to be
                        # enabled (or disabled if preceded by '-'), e.g. (?i:X) enables
                        # re.I for expression X, and (?-i:X) disables re.I for X;
                        #   r'(?ims:.*?^b.c$)' matches 'a\nB\nc' in 'a\nB\nc\nd';
                        #   r'(?x: a b c)' matches 'abc' in 'abcd';
                        #   r'(?a:\w+)' matches 'a2' in 'a2Ω';
                        #   re.match(r'(?u:\w+)', 'a2Ω', re.A)[0] returns 'a2Ω'
r'(?>X)'        # Atomic group matching X (no back-tracking to points inside X once X is
                # matched);
                #   r'ab?b' matches both 'ab' and 'abb', but - like r'ab?+b' - r'a(?>b?)b'
                #   matches 'abb' but not 'ab';
r'(?#X)'        # Comment group matching nothing (X is the comment);
                #   r'a(?#That was an a, now comes a b)b' matches 'ab'
# Backreferences (referencing results of previous capturing groups)
r'\1'           # Matches what 1st capturing group matched;
                #   r'(.)(.)\2\1' matches 'abba', but not 'abab'
r'(?P=name)'    # Matches what named capturing group matched;
                #   r'(?P<first>\d+)(?P=first)' matches '123123', but not '123456'
r'(?(1)X)'      # Matches X if 1st capturing group matched something, otherwise matches
                # current position;
                #   r'(\()?(\[)?a(?(2)\])(?(1)\))' matches '([a])', '(a)', '[a]', 'a', but
                #   not '([a]', '([a)', '([a)]'
r'(?(1)X|Y)'    # Matches X if 1st capturing group matched something, otherwise matches Y;
                #   r'(?>(1)|\d+) (?(1)mouse|mice)' matches '1 mouse' and '3 mice', but not
                #   '1 mice' or '3 mouse'
r'(?(name)X)'   # Matches X if named capturing group matched something, otherwise matches
                # current position;
                #   r'(?P<round>\()?(?P<square>\[)?a(?(square)\])(?(round)\))' behaves like
                #   r'(\()?(\[)?a(?(2)\])(?(1)\))' above
r'(?(name)X|Y)' # Matches X if named capturing group matched something, otherwise matches Y;
                #   r'(?>(?P<n>1)|\d+) (?(n)mouse|mice)' behaves like
                #   r'(?>(1)|\d+) (?(1)mouse|mice)' above
# Other
r'(?auLimsx)X'  # Flags to be applied to entire regular expression X (must be placed at
                # start of expression); each letter corresponds to a flag to be enabled,
                # e.g. (?i)X enables re.I for expression X;
                #   r'(?ims).*?^b.c$' matches 'a\nB\nc' in 'a\nB\nc\nd';
                #   r'(?x) a b c' matches 'abc' in 'abcd';
                #   r'(?a)\w+' matches 'a2' in 'a2Ω';
                # (?a), (?u), (?L) are not allowed to override re.A, re.U, re.L given as
                # argument to e.g. re.match

Comprehensions

For each iteration of the inner (last) 'for' loop, the expression highlighted below is evaluated to produce another item in the resulting list - unless an 'if' condition is false, in which case no item is produced for that iteration.
# List comprehension
[x * y for x in [1, -1] for y in range(4) if y > x] # Returns list [2, 3, 0, -1, -2, -3]

# Dictionary comprehension
{x: y for x, y in ((0, 3), (1, 4), (2, 3))}         # Returns dict {0: 3, 1: 4, 2: 3}

# Set comprehension
{x**2 for x in range(4)}                            # Returns set {0, 1, 4, 9}

# Tuple comprehension - has no dedicated syntax,
# but a generator expression can be passed to tuple()
tuple(chr(x) for x in range(65, 67))                # Returns tuple ('A', 'B')

Generator Expressions

g = (x for x in 'hello' if x < 'm')     # Assigns a generator object prepared to produce
                                        # the sequence 'h', 'e', 'l', 'l';
                                        # generator objects are also iterators
type(g)                                 # Returns class types.GeneratorType with name
                                        # 'generator'
next(g)                                 # Returns 'h', i.e. next (first) item
next(g)                                 # Returns 'e', i.e. next item
list(g)                                 # Returns ['l', 'l'], i.e. all remaining items;
                                        # g is useless now and can't be restarted
list(g)                                 # Returns []; no more items
next(g)                                 # Raises StopIteration exception; no more items

g = (x**2 for x in range(5))            # Assigns a new generator object
for i in g:                             # Assigns each generated item to i in turn
    if i == 9:                          # If item is 9, then...
        try:
            i = next(g)                 # ... skip to next item if any
        except StopIteration:
            i = 'end'                   # If no more, set i to 'end' (will not happen)
    print(i)                            # Prints '0', '1', '4', '16', one by one
See also generator functions.

Lambda Expressions

f = lambda x, y: x + y  # Assigns a lambda expression (anonymous function) that takes 2
                        # arguments and returns the sum of these; this is basically the
                        # same as 'def f(x, y): return x + y', except a lambda doesn't
                        # need to be bound to a name, and is limited to one expression
type(f)                 # Returns class types.FunctionType with name 'function'
f(3,5)                  # Calls lambda expression bound to name f; returns 8
map(lambda x: x**2, range(4))   # Applies lambda to range and returns iterator for sequence
                                # 0, 1, 4, 9
g = lambda x: lambda y: x + y   # Binds g to a function which returns a function
g(3)                            # Returns a function which adds 3 to stuff
g(3)(4)                         # Returns 7

Functions

Function Definitions and Calls

# Function definitions
def f1(x, y=0):         # Arguments may have default values (calculated only once when
                        # 'def' is executed, so beware if using mutable values, e.g. y=[])

    # Here's a docstring for this function:
    """This docstring is accessible via f1.__doc__
    """

    print(x, y)
    if x > y:
        return          # Exits function with return value None
    if x < y:
        return y, x     # Exits function with expression list as return value
                        # Return value is None if function ends without 'return'

def f2(x, *args, **keyargs): # Special * and ** syntax explained in function body:
    print(x, end=' ')   # Prints first argument (and a space, no newline)
    print(args, end=' ')# Prints a tuple of all remaining unnamed arguments
    print(keyargs)      # Prints a dict of all remaining named arguments

def f3(x, /, y, *, z):  # Special / and * syntax: parameters before / are positional only
                        # (f3(5,...) is OK, but f3(x=5,...) is not); parameters between /
                        # and * may be positional or named (f(5,6,...) and f(5,y=6,...) are
                        # both OK); parameters after * are named only (f(5,6,z=7) is OK,
                        # but f(5,6,7) is not)
    print(x, y, z)      # Prints the 3 arguments

def f4(x):
    def g(y):           # Defines an inner function (function object) inside f4
        return x + y    # Function g uses object referenced by f4's local x, so keeps that
                        # object in existence after f4 returns
    return g            # Function f4 returns the created function object (in this case a
                        # 'closure' because it keeps data (x) in an outer scope (f4) alive
                        # even though that scope has ended when the function is later
                        # called)

type(f1)                # Returns class types.FunctionType with name 'function' (functions
                        # are also objects)

# Function calls
f1(3)                   # Calls f1 which prints '3 0' and returns None
print(f1(3,5))          # Calls f1 which prints '3 5', then prints result '(5, 3)'
f1(y=5, x=3)            # Passes named arguments to f1 which prints '3 5'
a1 = [3, 5]
f1(a1, [4])             # Passes a1 and [4] to f1 which prints '[3, 5] [4]' and returns
                        # ([4], [3, 5]) because [3,5] is lexicographically smaller than [4]
f1(*a1)                 # Passes each item of a1 as an argument to f1 which prints '3 5'
                        # and returns (5, 3)
d = {'y':5, 'x':3}      # Creates a dictionary
f1(**d)                 # Passes values of d as named arguments to f1 which prints '3 5'
f1(*[3], **{'y':5})     # Passes items of list and values of dictionary as arguments
                        # to f1 which prints '3 5'
f2(3)                   # Prints '3 () {}'
f2(3, 4, 5, a=6)        # Prints "3 (4, 5) {'a': 6}"
f3(5, 6, z=7)           # Prints "5 6 7"
f3(5, z=7, y=6)         # Prints "5 6 7"
f3(5, 6, 7)             # Raises TypeError (parameter z requires a named argument)
f3(x=5, y=6, z=7)       # Raises TypeError (parameter x requires an unnamed argument)

add10 = f4(10)          # Calls f4 which returns new function that adds 10 to its argument
print(add10(9))         # Calls function add10 and prints return value '19'

f2 = f1                 # (Re-)binds name f2 to same function object as f1
f2(3)                   # Calls f2 (now same as f1) which prints '3 0' and returns None
Notes:

Generator Functions

# Using 'yield'
def f1(m):              # The yield statement in the body of this function causes it to
                        # return a generator object, which produces a new item whenever
                        # the yield statement is reached;
                        # generator objects are also iterators
    print('1st')        # Prints '1st' when first item is requested from generator (first
                        # call to next())
    end = 5 * m + 1
    for x in range(m, end, m):
        yield x                 # Returns x to caller of next() on generator object, and
                                # stops execution until next() is called again, whereafter
                                # execution continues after yield statement
                        # When the loop ends and f1 returns, the generator object raises a
                        # StopIteration exception

g = f1(3)               # Assigns the generator object returned from function f1
type(g)                 # Returns class types.GeneratorType with name 'generator'
next(g)                 # Prints '1st' and returns 3, i.e. next (first) item of generator
next(g)                 # Returns 6, i.e. next item
list(g)                 # Returns [9, 12, 15], i.e. all remaining items;
                        # g is useless now and can't be restarted (but f1 can be called
                        # again to get a new generator object)
list(g)                 # Returns []; no more items
next(g)                 # Raises StopIteration exception; no more items
# Using 'yield from'
def f2(m):              # Identical to f1 above, but uses 'yield from' instead of 'yield'
    print('1st')
    end = 5 * m + 1
    yield from range(m, end, m) # Returns items one by one from iterable to caller of
                                # next(), stopping execution each time

g = f2(3)               # Assigns the generator object returned from function f2
next(g)                 # Prints '1st' and returns 3, i.e. next (first) item of generator
list(g)                 # Returns [6, 9, 12, 15], i.e. all remaining items
# Using 'send' and return value from 'yield',
# and assigning a value to StopIteration exception
def f3(m):              # Identical to f1 above, but allows values to be sent into the
                        # generator at any time (and returned from yield) to modify its
                        # behavior while iterating over it; also sets a StopIteration value
    print('1st')
    end = 5 * m + 1
    x = m
    while x < end:
        y = yield x     # Returns x to caller of next() or send(), stops execution until
                        # next() or send() is called again, then sets y to None (for
                        # next()) or to argument of send(), and resumes execution here
        x = x + m if y is None else y  # Updates next item or uses value provided by send()
    return x            # Puts next x (which would have been generated if iteration hadn't
                        # ended) into value attribute of StopIteration exception

g = f3(3)               # Assigns the generator object returned from function f3
g.send(4)               # Raises TypeError exception; can't send non-None value to
                        # just-started generator (no yield to receive it)
next(g)                 # Prints '1st' and returns 3, i.e. next (first) item of generator
next(g)                 # Returns 6, i.e. next item
g.send(4)               # Sends 4 into generator, which sets next item to 4 and returns 4
next(g)                 # Returns 7, i.e. next item
next(g)                 # Returns 10, i.e. next item
next(g)                 # Returns 13, i.e. next item
try:
    next(g)                 # Raises StopIteration exception due to no more items
except StopIteration as e:  # Catches StopIteration exception and assigns it to e
    print(e.value)          # Prints '16' (StopIteration value returned at end of f3)
    print(e.args)           # Prints '(16,)' (more general way to get arguments used to
                            # create an exception; e.value is specific to StopIteration)
try:
    next(g)                 # Raises StopIteration exception again (still no more items)
except StopIteration as e:
    print(e.value)          # Prints 'None' (StopIteration value returned by f3 can only be
                            # obtained once)
# Using 'send' and return value from 'yield from';
# see above for definition of f3()
def f4(m):
    print('first-', end='')
    y = yield from f3(m)    # Returns items one by one from f3 generator to caller of
                            # next() or send(); sets y to f3's StopIteration value when f3
                            # is exhausted; argument to send() is passed to f3's send();
    yield from (y, y * 2)   # Provide 2 more items (one by one) after f3 is exhausted

g = f4(3)               # Assigns the generator object returned from function f4
g.send(4)               # Raises TypeError exception; can't send non-None value to
                        # just-started generator (no yield to receive it)
next(g)                 # Prints 'first-1st' and returns 3 (first item from f4, which got
                        # it from f3)
next(g)                 # Returns 6 (next item from f4, which got it from f3)
g.send(4)               # Sends 4 into f4, which forwards it to f3, which sets next item to
                        # 4 and returns 4 to f4, which returns 4 here
next(g)                 # Returns 7 (next item from f4, which got it from f3)
list(g)                 # Returns [10, 13, 16, 32] (all remaining items from f4, which got
                        # the first 2 items from f3, then produced 2 more items based on
                        # f3's StopIteration value (16))
# Using 'throw', and 'close'
def f5():
    try:
        yield from (1,2,3)  # Returns items one by one from a tuple to the caller (f6), and
                            # raises any exception passed in via throw()
    except ValueError as e: # Catches a ValueError exception and
        yield from e.args   # returns its arguments one by one to the caller (f6)

def f6():
    yield from f5()         # Returns items one by one from f5 to caller of next(), send(),
                            # or throw() (after forwarding those calls to f5)

g = f6(3)               # Assigns the generator object returned from function f6
next(g)                 # Returns 1 (first item from f6, which got it from f5)
next(g)                 # Returns 2 (next item from f6, which got it from f5)
g.throw(ValueError, (7,8,9)) # Passes the exception ValueError(7,8,9) to f6 (via f6's
                             # 'yield'), which passes it to f5 (via f5's first 'yield'),
                             # which can't pass it any further so raises the exception at
                             # that 'yield', catches the exception in the 'except', then
                             # starts returning items one by one from the tuple of
                             # arguments passed to ValueError, i.e. (7,8,9); thus f5
                             # returns 7 to f6, which returns 7 here
next(g)                 # Returns 8 (next item from f6, which got it from f5)
g.close()               # Closes/exhausts the generator early (so skips the last item 9)
next(g)                 # Raises StopIteration exception due to no more items (closed)
g.close()               # Closing an already exhausted generator has no effect
See also generator expressions.

Decorators

from functools import wraps # Imports the 'wraps' decorator factory which copies attributes
                            # (__name__, __doc__, __module__) from the wrapped function to
                            # the wrapper function to make the wrapping more transparent

def deco1(f):               # Defines a function deco1 which takes another function and
                            # returns a modified version of it; any function that takes a
                            # function or class as its sole argument can be used as a
                            # decorator regardless of what it returns (see how to use deco1
                            # below)
    @wraps(f)               # Applies the 'wraps' decorator factory to f_wrapper1
    def f_wrapper1(*args):                      # Defines the wrapper function which
        return 2 * f(args[0] * 10, *args[1:])   # calls the wrapped/decorated function
    return f_wrapper1       # Returns the wrapper function which will later be called
                            # instead of the wrapped/decorated function

def deco_factory(inscale, outscale):    # Defines a function deco_factory which uses its
                            # arguments to produce a function that can be used as a
                            # decorator; any function that does this can be used as a
                            # decorator factory (see how to use deco_factory below)
    def deco2(f):           # Defines a function deco2 similar to deco1 above, but this
                            # one is customized based on the arguments to deco_factory
        @wraps(f)
        def f_wrapper2(*args):
            return outscale * f(args[0] * inscale, *args[1:])
        return f_wrapper2
    return deco2            # Returns the deco2 function

# The following line decorates the myadd1 function with the deco1 function;
# same as doing myadd1 = deco1(myadd1) after defining myadd1,
# so the name myadd1 will end up referring to f_wrapper1 which, when called,
# will call the original myadd1 defined below
@deco1
def myadd1(x, y):
    return x + y

# The following line calls deco_factory and uses the returned function (deco2)
# as a decorator for the myadd2 function;
# same as doing myadd2 = deco_factory(100, 10)(myadd2) after defining myadd2,
# so the name myadd2 will end up referring to f_wrapper2 which, when called,
# will call the original myadd2 defined below
@deco_factory(100, 10)
def myadd2(x, y):
    return x + y

# Any number of decorators can be applied to the same function; they are applied
# in reverse order;
# the following is the same as doing myadd3 = deco_factory(100, 10)(deco1(myadd3))
# after defining myadd3
@deco_factory(100, 10)
@deco1
def myadd3(x, y):
    return x + y

myadd1(3, 4)                # Calls f_wrapper1 which calls the original myadd1 and returns
                            # 68 (the result of 2 * (3 * 10 + 4))
myadd2(3, 4)                # Calls f_wrapper2 which calls the original myadd2 and returns
                            # 3040 (the result of 10 * (3 * 100 + 4))
myadd3(3, 4)                # Calls f_wrapper2 which calls f_wrapper1 which calls the
                            # original myadd3; returns 60080 (10 * (2 * (3 * 100 * 10 + 4)))
Examples of standard functions often used as decorators are: classmethod(), staticmethod(), property(), functools.total_ordering(). Most decorators take a function and return a function, but property() takes a function and returns a property object, and functools.total_ordering() takes a class and returns a class. Examples of standard functions often used as decorator factories are: functools.wraps().

Built-in Functions

__build_class__(f,'C')  # TBD
__import__('sys')       # TBD
abs(-3+4j)              # Returns 5.0
aiter(o)                # Returns an asynchronous iterator for asynchronous iterable o;
                        # (asynchronous version of iter())
all([True,4,'0'])       # Returns True (Are all items True after conversion to bool? Yes);
                        # all([]) returns True (not False!)
anext(i,d)              # Returns an awaitable which, when awaited, returns next item from
                        # asynchronous iterator i, or, if no more items, returns d or
                        # raises StopIteration if no d; (asynchronous version of next())
any([False,0,''])       # Returns False (Are any items True after conversion to bool? No);
                        # any([]) returns False
ascii(o)                # Same as repr(o) but uses escapes for non-ascii characters
bin(12)                 # Returns '0b1100' (binary representation of 12)
bool()                  # Returns a new bool
breakpoint()            # Calls sys.breakpointhook() which by default pauses execution and
                        # enters the Python debugger
bytearray()             # Returns a new bytearray
bytes()                 # Returns a new bytes object
callable(f)             # Returns True if f appears to be callable (i.e. a function, class,
                        # or other object with a __call__ method); note that some objects
                        # may appear to be callable but will fail when called
chr(65)                 # Returns 'A' (character with Unicode code 65)
classmethod(f)          # Returns a class method for function f (usually used as decorator
                        # @classmethod, see classes)
compile('x=3\nprint(x)', '', 'exec') # Returns a code object which can be executed by exec()
compile('2+5', '', 'eval')          # Returns a code object which can be evaluated by eval()
complex()               # Returns a new complex
delattr(o, 'x')         # Deletes object o's attribute x; same as del o.x
dict()                  # Returns a new dict
dir(o)                  # Returns a list of o's attributes, or a list of names in the
                        # current local scope if no argument is given
divmod(-5, 3)           # Returns (-2, 1); same as (-5 // 3, -5 % 3)
enumerate(x)            # Returns a new enumerate; see iterator types
eval('2+3')             # Returns 5; evaluates any Python expression (or compile() object)
exec('if True:\n print("hi")') # Prints 'hi'; executes code in a string or compile() object
exit()                  # Exits interactive interpreter; see script termination
filter(f, 'hello')      # Returns iterator for sequence 'h','l','l','o' assuming f is:
                        # def f(x): return x > 'g'
filter(None, [3,0,''])  # Returns iterator for sequence 3 (all True items in list)
float()                 # Returns a new float
format(4, '<03')        # Returns '400' (same as '{:<03}'.format(4), see string operations)
frozenset()             # Returns a new frozenset
getattr(o, 'x', d)      # Returns the value of object o's attribute x (same as o.x), or, if
                        # o.x doesn't exist, returns d or raises AttributeError if no d
globals()               # Returns a dict representing the current global symbol table;
                        # globals()['x'] = 3 is equivalent to global x; x = 3
hasattr(o, 'x')         # Returns True if object o has attribute x
hash(o)                 # Returns a hash value (integer) of immutable object o
help(o)                 # Prints documentation on object o - or topic o if o is a string
hex(254)                # Returns '0xfe' (hexadecimal respresentation of 254)
id(o)                   # Returns the unique id (integer) of object o
input('Input: ')        # Prints 'Input: ', reads a line from stdin, and returns the line
                        # (excluding the final newline)
int()                   # Returns a new int
isinstance(o, c)        # Returns True if o is an instance of class c or of a subclass of c
                        # - or of any item in c if c is a tuple of classes
isinstance(o, c1 | c2)  # Same as isinstance(o, (c1,c2))
issubclass(c1, c2)      # Returns True if c1 is a subclass of, or identical to, c2 - or any
                        # item in c2 if c2 is a tuple of classes; c1 must be a class
issubclass(c1, c2 | c3) # Same as issubclass(c1, (c2,c3))
iter(o)                 # Returns an iterator for iterable o; see iterator types
iter(f,x)               # Returns an iterator that calls f with no args until return value
                        # equals x; see iterator types
len([6,7,8])            # Returns 3 (number of items in list/tuple/set/...)
list()                  # Returns a new list
locals()                # Returns a dict representing the current local symbol table (this
                        # dict should not be modified)
map(f, [5,2,6], (3,4))  # Returns iterator for sequence '53','24' assuming f is:
                        # def f(x,y): return str(x)+str(y);
                        # the number of sequences must match the number of arguments to f
max(3,5,2)              # Returns 5
max([3,5,2])            # Returns 5
max([], default=4)      # Returns 4
max([3,5,2], key=lambda x:-x)       # Returns 2
max([], key=lambda x:-x, default=4) # Returns 4
memoryview(o)           # Returns a new memoryview
min(3,5,2)              # Returns 2
min([3,5,2])            # Returns 2
min([], default=4)      # Returns 4
min([3,5,2], key=lambda x:-x)       # Returns 5
min([], key=lambda x:-x, default=4) # Returns 4
next(i,d)               # Returns next item from iterator i, or, if no more items, returns
                        # d or raises StopIteration if no d; see iterator types
object()                # Returns a new object
oct(8)                  # Returns '0o10' (octal representation of 8)
open('file', 'w')       # Opens 'file' for writing and returns a file object; see files
ord('A')                # Returns 65 (Unicode code of character 'A')
pow(3.0, 2.0)           # Returns 9.0; same as 3.0**2.0;
pow(3, 2, 4)            # Returns 1; same as 3**2 % 4 but more efficient; all args must be
                        # integers
print()                 # Prints its arguments; see standard in/out/error
quit()                  # Exits interactive interpreter; see script termination
property()              # Returns a new property
range(10)               # Returns a new range
repr(o)                 # Returns a formal string representation of o, preferably one that
                        # can be executed by eval() to recreate o
reversed([1,2,3])       # Returns an iterator for the sequence 3,2,1
reversed('hello')       # Returns an iterator for the sequence 'o','l','l','e','h'
round(25.16)            # Returns 25 (25.16 rounded to 0 digits after the point)
round(25.16, 0)         # Returns 25.0 (25.16 rounded to 0 digits after the point)
round(25.16, 1)         # Returns 25.2 (25.16 rounded to 1 digit after the point)
round(25.16, -1)        # Returns 30.0 (25.16 rounded to 1 digit before the point)
set()                   # Returns a new set
setattr(o, 'x', 3)      # Sets object o's attribute x to 3; same as o.x = 3
slice(10)               # Returns a new slice
sorted([10,-1,9])       # Returns [-1, 9, 10] (takes the same additional args as o.sort())
staticmethod(f)         # Returns a static method for function f (usually used as decorator
                        # @staticmethod, see classes)
str()                   # Returns a new str
sum([1,2,3,4])          # Returns 10 (the sum of all items)
sum([2,3,4], 1)         # Returns 10 (the sum of all items and the 2nd argument)
super(C, self).m()      # Calls method m of class C's parent/sibling class (the next class
                        # after C in the method resolution order of self's class) and
                        # passes object self to that method; see class inheritance;
                        # arguments C and self are optional inside class definitions
tuple()                 # Returns a new tuple
type(10)                # Returns int (i.e. a type object that is the type of the argument)
type('MyT', (), {})     # Returns a new type; see class creation and instantiation
vars(o)                 # Returns o.__dict__, or same as locals() if no o
zip([5,2,6], (3,4))     # Returns an iterator for sequence (5, 3), (2, 4);
                        # any number of collections are supported;
                        # zip can also unzip, because if x is a list of same-length tuples,
                        # and y = list(zip(*x)), then x == list(zip(*y))
zip([5,2,6], (3,4), strict=True)  # Returns same iterator as above, except this iterator
                        # raises ValueError instead of StopIteration when done, because
                        # arguments don't have same length

Classes

About Classes/Types and Objects

Types and classes are two terms for the same thing: objects representing categories of other objects. The UML diagram below illustrates the relationships between various types of objects ("int:type" means object int is an instance/object of class type, and arrows point from subclasses to the superclasses/bases they inherit from).

Objects Classes Metaclasses Other Objects object:type type:type Mymetaclass:type int:type Myclass:type Mysubclass:type Myclass2:Mymetaclass 42:int x:Myclass y:Mysubclass z:Myclass2 o:object

The following code creates the objects shown in the diagram above and checks their relationships.

# Create the non-standard classes shown in the diagram above
class Myclass: pass
class Mysubclass(Myclass): pass
class Mymetaclass(type): pass
class Myclass2(metaclass=Mymetaclass): pass

# Create the non-standard "other objects" shown in the diagram above
x = Myclass()
y = Mysubclass()
z = Myclass2()
o = object()

# Create the object groups shown in the diagram above
classes = {object, type, Mymetaclass, Myclass, Mysubclass, int, Myclass2}
metaclasses = {type, Mymetaclass}
other_objs = {42, x, y, z, o}
objects = classes | other_objs

# All objects (even 'object' itself) are instances of 'object' (possibly instances of a
# subclass of 'object')
all(isinstance(obj, object) for obj in objects)             # Returns True
# All objects have a __class__ attribute referencing the class of which the object is a
# direct instance; this is also the class returned when the object is passed to the type()
# function
all(obj.__class__ is type(obj) for obj in objects)          # Returns True

# All metaclasses are subclasses of 'type', except in the hypothetical and probably not
# very useful case where they are NOT subclasses of 'type' but nevertheless behave like
# 'type' (no known examples of this); note that Python considers any class to be a subclass
# of itself
all(issubclass(cls, type) for cls in metaclasses)           # Returns True

# All classes (even 'type' itself) are instances of 'type' (possibly instances of a
# subclass of 'type' in the case of metaclass instances), except if they are instances of
# one of the unlikely metaclasses mentioned above
all(isinstance(cls, type) for cls in classes)               # Returns True
all(type(cls) is type for cls in (classes - {Myclass2}))    # Returns True
# Myclass2 is an example of an instance of a subclass of 'type'
type(Myclass2) is Mymetaclass                               # Returns True
issubclass(Mymetaclass, type)                               # Returns True

# Other objects than classes are not instances of 'type' (not even instances of a subclass
# of 'type'), they are instances of some other class
any(isinstance(obj, type) for obj in other_objs)            # Returns False
any(issubclass(type(obj), type) for obj in other_objs)      # Returns False
expected_class_for_obj = {42:int, x:Myclass, y:Mysubclass, z:Myclass2, o:object}
all(isinstance(obj, expected_class_for_obj[obj])
    for obj in other_objs)                                  # Returns True
all(type(obj) is expected_class_for_obj[obj]
    for obj in other_objs)                                  # Returns True

# All classes are subclasses of 'object' (possibly subclasses of a subclass of 'object';
# note that Python considers any class to be a subclass of itself)
all(issubclass(cls, object) for cls in classes)             # Returns True
# Mymetaclass and Mysubclass are examples of subclasses of subclasses of 'object'
issubclass(Mymetaclass, type)                               # Returns True
issubclass(type, object)                                    # Returns True
issubclass(Mysubclass, Myclass)                             # Returns True
issubclass(Myclass, object)                                 # Returns True
# All classes have a (possibly empty) __bases__ tuple containing their superclasses
all(hasattr(cls, '__bases__') for cls in classes)           # Returns True
all(cls.__bases__ == (object,)
    for cls in classes - {object, Mymetaclass, Mysubclass}) # Returns True
object.__bases__                                            # Returns ()
Mymetaclass.__bases__                                       # Returns (type,)
Mysubclass.__bases__                                        # Returns (Myclass,)

# Other objects than classes obviously cannot be subclasses (issubclass will raise
# TypeError) and do not have the __bases__ tuple
any(hasattr(obj, '__bases__') for obj in other_objs)        # Returns False

Class Creation and Instantiation

# Class definitions
class Myclass:          # Defines a class (which inherits only from 'object')

    # Here's a docstring for this class:
    """This docstring is accessible via Myclass.__doc__
    or e.g. o1.__doc__ where o1 is an instance of Myclass.
    """

    n = 3               # Defines a class variable, shared by all instances of this class

    @staticmethod       # Decorator that defines a static method to be invoked on the class
    def setn(n):        # itself (or optionally on instances of the class)
        Myclass.n = n   # Updates the class variable

    @classmethod        # Decorator that defines a class method to be invoked on the class
    def setn2(cls, n):  # itself (or optionally on instances of the class); first arg is
                        # the class, conventionally named 'cls'
        cls.n = n       # Updates the class variable

    def __init__(self, x):  # Defines the instance constructor; first arg is the instance,
                            # conventionally named 'self' (like 'this' in C++)
        self.x = x          # Creates an instance variable belonging to the given instance

    def add(self, y):       # Defines an instance method to be invoked on a given instance
        self.x += (y *      # Updates the previously created instance variable
            self.n)         # Class variables may be read (not written!) via 'self'
                            # (if written, a new instance variable is created hiding the
                            # class variable!)

    def __str__(self):      # Defines informal nicely printable string representation of
                            # an instance of this class
        return str(self.x)  # Returns instance variable converted to string

    def __repr__(self):     # Defines formal string representation of an instance of this
                            # class, preferably executable by eval() to recreate instance
        return 'Myclass(%d)' % self.x

    def __getitem__(self, item):        # Defines special method for getting indexed item
        print('get', item)
        t = item if isinstance(item, tuple) else (item,) # Make item a tuple if not already
        for i in t:                                      # Step through tuple
            if isinstance(i, slice):                     # Handle slice by converting it to
                print(range(*i.indices(self.n)), end=' ')# range (self.n sets upper limit)
            elif i == Ellipsis:                          # Handle Ellipsis object by just
                print('...', end=' ')                    # printing 3 dots
            else:
                print(i, end=' ')
        print()
        return self.x

    def __setitem__(self, key, val):    # Defines special method for setting indexed item
        print('set', key, val)

    def __add__(self, other):           # Defines special method overriding '+' operator
        return self.x + other

    def __radd__(self, other):          # Defines special method overriding '+' operator if
                                        # this object is 2nd arg and other.__add__()
                                        # returned NotImplemented
        return self.x + other + self.n

type(Myclass)               # Returns class type; same as Myclass.__class__
isinstance(Myclass, type)   # Returns True
issubclass(Myclass, object) # Returns True
Myclass.__name__            # Returns 'Myclass'
Myclass.__bases__           # Returns (object,) (tuple of base classes)
Myclass.mro()               # Returns [Myclass, object]
                            # (method resolution order: order in which classes are searched
                            # for a method definition)
Myclass.n                   # Returns 3
type(Myclass.setn)          # Returns class types.FunctionType with name 'function'
type(Myclass.setn2)         # Returns class types.MethodType with name 'method'
type(Myclass.add)           # Returns class types.FunctionType with name 'function'
                            # (unbound method)

# Dynamically defined classes
type('Mydynclass', (Myclass,), {'n': 4})    # Creates and returns a class with __name__ ==
                        # 'Mydynclass' but not bound to any name in current namespace;
                        # this class inherits from Myclass and sets class variable n to 4;
                        # the class will be lost unless a reference to it is saved, e.g.
                        # Someclass = type('Mydynclass', ...)

# Instantiation
o1 = Myclass(10)        # Creates an object as an instance of class 'Myclass' and runs
                        # the __init__ constructor with parameter x = 10
type(o1)                # Returns class Myclass; same as o1.__class__
type(o1.add)            # Returns class types.MethodType with name 'method'; more about
                        # method objects below
o2 = Myclass(20)        # Creates a second instance of the same class using x = 20
o1.x                    # Returns 10
o2.x                    # Returns 20
o1.n                    # Returns 3 (the value of class variable n)
str(o1)                 # Returns '10' (return value from o1.__str__())
repr(o1)                # Returns 'Myclass(10)' (return value from o1.__repr__())
dir(o1)                 # Returns list of all o1's attributes: [..., '__doc__', ...,
                        # '__init__', ..., 'add', 'n', 'setn', 'setn2', 'x']

o1[4]                   # Calls o1.__getitem__(4)
o1[::-1] = 2            # Calls o1.__setitem__(slice(None,None,-1),2); see slice
o1[2,:3,...]            # Calls o1.__getitem__((2,slice(None,3,None),Ellipsis))
                        # (note: this extended syntax is not supported by built-in types
                        # such as list and tuple)

o1 + 4                  # Calls o1.__add__(4) which returns 14
5 + o1                  # Calls o1.__radd__(5) (when (5).__add__(o1) returns NotImplemented)
                        # which returns 18
o1.add(2)               # Passes 2 to the 'add' method of o1 which updates o1's x;
                        # equivalent to Myclass.add(o1, 2)
o1.x                    # Returns 16 (2 * 3 was added to the previous value 10)
o2.x                    # Returns 20 (no change)

Myclass.setn(5)         # Changes the class variable n value to 5
Myclass.setn2(5)        # Same effect as above (Myclass is automatically passed as 1st arg
                        # to setn2, and 5 becomes 2nd arg)
Myclass.n = 5           # Same effect as above
o1.setn(5)              # Same effect as above (o1 is only used to access Myclass)
o1.setn2(5)             # Same effect as above (o1 is only used to access Myclass)
                        # (don't do o1.n = 5, it hides the class variable from o1)
o1.n                    # Returns 5
o2.n                    # Returns 5 (same class var n is accessed from any instance)

o2.add(-1)
o1.x                    # Returns 16
o2.x                    # Returns 15 (-1 * 5 was added to 20)

o1.s = 'hi'             # Creates a new instance variable on o1 only
Myclass.k = 100         # Creates a new class variable (visible in all existing and new
                        # instances)

# Bound method objects
o1a = o1.add            # Assigns a bound method object referencing o1's 'add' method (a
                        # new object is created every time a user defined method is
                        # accessed like this, so (o1.add is o1.add) evaluates to False!)
type(o1a)               # Returns class types.MethodType with name 'method'
o1a.__self__            # Returns o1
o1a.__func__            # Returns Myclass.add
o1a(4)                  # Passes 4 to o1's 'add' method, which updates o1's x
o1.x                    # Returns 36 (4 * 5 was added to 16)

# Built-in function/method objects
ss = 'abba'.strip       # Assigns a bound method object referencing the built-in strip
                        # method of string 'abba'
type(ss)                # Returns class types.BuiltinFunctionType with name
                        # 'builtin_function_or_method'
ss.__self__             # Returns 'abba'
ss.__func__             # ILLEGAL! Raises AttributeError exception because built-in methods
                        # don't have the __func__ attribute
ss('a')                 # Returns 'bb' (same as 'abba'.strip('a'))
type(len)               # Returns class types.BuiltinFunctionType with name
                        # 'builtin_function_or_method'; built-in functions are actually
                        # bound methods of the 'builtins' module object (which is hidden
                        # unless explicitly imported)
len.__self__            # Returns builtins module object

Class Inheritance

class A(list):                          # Defines a class A which inherits from list (which
                                        # inherits from object)
    def __str__(self):                  # Overrides list's __str__ method in order to...
        return ('A:' +                  # ... prepend 'A:' to...
            super().__str__())          # ... whatever is returned from __str__() of the
                                        # next classes in the method resolution order (i.e.
                                        # the previous classes in the inheritance order);
                                        # the next class is list when self is an instance
                                        # of A, but B when self is an instance of C!

class B(list):                          # Defines a class B just like A, except...
    def __str__(self):
        return ('B:' +                  # ... prepend 'B:' to...
            super().__str__())          # ... whatever is returned from __str__() of the
                                        # next classes in the method resolution order; the
                                        # next class is list when self is an instance of
                                        # either B or C

class C(A, B):                          # Defines a class C which inherits primarily from A
                                        # and secondarily from B
    def __str__(self):                  # Overrides the __str__ method in order to...
        return ('C:' +                  # ... prepend 'C:' to...
            super().__str__())          # ... whatever is returned from __str__() of the
                                        # next classes in the method resolution order; the
                                        # next class is A when self is an instance of C

C.__bases__ # Returns (A, B)

# Method resolution order (MRO) for classes A, B, and C (see notes)
A.mro()     # Returns [A, list, object]; this means that A().__str__() will first look for
            # an __str__ method in class A, then in class list, then in class object, until
            # a class is found which has the method
B.mro()     # Returns [B, list, object]
C.mro()     # Returns [C, A, B, list, object]

a = A([0])  # Assigns an instance of class A initialized by calling a.__init__([0]) which
            # resolves to list.__init__(a,[0]) (list is 1st class with __init__ in A.mro())
            # which sets the initial value to [0]
b = B([1])  # Assigns an instance of class B initialized to [1] by list.__init__(b,[1])
c = C([2])  # Assigns an instance of class C initialized to [2] by list.__init__(c,[2])

print(a)    # Prints 'A:[0]', because print calls a.__str__()
            # which resolves to A.__str__(a) (A is a's class and has __str__)
            # which calls super(A, a).__str__()
            # which resolves to list.__str__(a) (list follows A in A.mro() and has __str__)
            # which returns '[0]' to A.__str__
            # which returns 'A:[0]' to print
print(b)    # Prints 'B:[1]', because print calls b.__str__()
            # which resolves to B.__str__(b) (B is b's class and has __str__)
            # which calls super(B, b).__str__()
            # which resolves to list.__str__(b) (list follows B in B.mro() and has __str__)
            # which returns '[1]' to B.__str__
            # which returns 'B:[1]' to print
print(c)    # Prints 'C:A:B:[2]', because print calls c.__str__()
            # which resolves to C.__str__(c) (C is c's class and has __str__)
            # which calls super(C, c).__str__()
            # which resolves to A.__str__(c) (A follows C in C.mro() and has __str__)
            # which calls super(A, c).__str__()
            # which resolves to B.__str__(c) (B follows A in C.mro() and has __str__)
            # which calls super(B, c).__str__()
            # which resolves to list.__str__(c) (list follows B in C.mro() and has __str__)
            # which returns '[2]' to B.__str__
            # which returns 'B:[2]' to A.__str__
            # which returns 'A:B:[2]' to C.__str__
            # which returns 'C:A:B:[2]' to print
Notes on method resolution order (MRO):

Descriptors

A descriptor is an object attribute which calls special methods to override the default behavior when getting, setting, and/or deleting the attribute. In order to do so, a descriptor must be an instance of a class which defines the special methods __get__, __set__, and/or __delete__.

A descriptor that defines both the __get__ and __set__ methods is a data descriptor and looks like an instance/class variable. It overrides any instance/class variable with the same name. A property attribute is an example of a data descriptor.

A descriptor that defines only the __get__ method is a non-data descriptor and looks like a normal method. It is overridden by any normal method with the same name.

class MyDescriptor:                             # Defines a descriptor class
    def __set_name__(self, ownerclass, name):   # Special method called when descriptor
                                                # instance is assigned to name in class
        self.hidden_var_name = '_' + name       # Stores name '_x' in descriptor instance
                                                # (assuming descriptor was assigned to name
                                                # x in ownerclass definition)

    def __get__(self, instance, ownerclass):    # Special method called when descriptor is
                                                # accessed as instance.x or ownerclass.x
        if instance is None:                    # If accessed as ownerclass.x, then
            return ownerclass.__name__          # return name of ownerclass
        return getattr(instance,                # Otherwise return instance._x
                       self.hidden_var_name)

    def __set__(self, instance, value):         # Special method called when descriptor is
                                                # set to value, i.e. instance.x = value
        setattr(instance,                       # Sets instance._x = float(value)
                self.hidden_var_name,
                float(value))

    def __delete__(self, instance):             # Special method called when descriptor is
                                                # deleted, i.e. del instance.x
        delattr(instance,                       # Deletes instance._x (the descriptor
                self.hidden_var_name)           # instance.x itself remains intact)

class MyOwnerClass:         # Defines class MyOwnerClass; will contain our descriptor
    def __init__(self, v):  # Defines the instance constructor
        self.x = v          # Calls MyDescriptor.__set__(x, self, v) which sets self._x =
                            # float(v)
    x = MyDescriptor()      # Defines descriptor x as instance of class MyDescriptor

MyOwnerClass.x,             # Returns 'MyOwnerClass' (calls MyDescriptor.__get__(x, None,
                            # MyOwnerClass))
o = MyOwnerClass(1)         # Assigns a new instance of MyOwnerClass with o._x set to 1.0
o._x,                       # Returns 1.0
o.x,                        # Returns 1.0 (calls MyDescriptor.__get__(x, o, MyOwnerClass))
o.x = 2,                    # Sets o._x to 2.0 (calls MyDescriptor.__set__(x, o, 2))
o.x,                        # Returns 2.0 (calls MyDescriptor.__get__(x, o, MyOwnerClass))
del o.x                     # Deletes o._x (calls MyDescriptor.__delete__(x, o))
o.x,                        # Raises AttributeError exception because o._x doesn't exist
o.x = 3,                    # Sets o._x to 3.0 (calls MyDescriptor.__set__(x, o, 3))
o.x,                        # Returns 3.0 (calls MyDescriptor.__get__(x, o, MyOwnerClass))

Property Attributes

The built-in function property(), which creates a property object implementing the descriptor protocol, can be used as a decorator to define functions to be called whenever a certain class attribute is read, written, or deleted.
class C:
    def __init__(self, v):  # Defines the instance constructor
        self._x = float(v)  # Initializes an instance variable which is not supposed to be
                            # accessed directly from outside class C

    @property               # Creates a property attribute x whose __get__() method is
    def x(self):            # this function (function name becomes property name)
        return self._x

    @x.setter               # Sets the __set__() method of property x to
    def x(self, v):         # this function (use same name for function and property)
        self._x = float(v)

    @x.deleter              # Sets the __delete__() method of property x to
    def x(self):            # this function (use same name for function and property)
        del self._x

type(C.x)                   # Returns class property
isinstance(C.x, property)   # Returns True

c = C(1)                    # Assigns a new instance of class C with c._x set to 1.0
c._x                        # Returns 1.0 (but we're not supposed to access _x directly)
c.x                         # Returns 1.0 (calls our getter C.x.__get__(c))
c.x = 2                     # Sets c._x to 2.0 (calls our setter C.x.__set__(c, 2))
c.x                         # Returns 2.0 (calls our getter C.x.__get__(c))
del c.x                     # Deletes c._x (calls our deleter C.x.__delete__(c))
c.x                         # Raises AttributeError exception because c._x doesn't exist
c.x = 3                     # Sets c._x to 3.0 (calls our setter C.x.__set__(c, 3))
c.x                         # Returns 3.0 (calls our getter C.x.__get__(c))

Special Methods

Certain special methods - if defined in the class hierarchy of an object (not on the object itself) - are automatically called when certain built-in Python functions, statements, or syntax are applied to that object, and such calls are never redirected to the __getattr__ or __getattribute__ methods even if those exist. Built-in Python classes themselves have many of these special methods.

In the list below, all methods are instance methods unless specifically stated to be static methods or class methods (the @staticmethod and @classmethod decorators are unnecessary for special methods defined within the body of a class definition - the methods are automatically converted to the correct type), and the methods are assumed to have been defined in a class C of which object o is in instance, like this:

class C:
    def __new__(cls, *args, **kwargs): pass
    def __init__(self, *args, **kwargs): pass
    # ...etc...

o = C()
List of special methods along with examples of code that causes them to be called:
# Object creation/deletion
o = C.__new__(C)            # Static method; called by o = C() to create object of class C
o.__init__()                # Called by o = C() to initialize object created by __new__()
o.__del__()                 # Called by del o; gc.collect(), i.e. when o is garbage
                            # collected after last reference to it has been removed

# Class creation
C.__init_subclass__(C2)     # Class method; called by class C2(C): pass, i.e. when a
                            # subclass of class C is defined
C2 = C.__class_getitem__(t) # Class method; called by C2 = C[t] to create specialized class
                            # C2 from generic class C using type t

# Object attribute access
y = o.__getattr__('x')      # Called by y = o.x if o.x and o.__getattribute__ don't exist,
                            # or if o.__getattribute__('x') raises AttributeError
y = o.__getattribute__('x') # Called by y = o.x
o.__setattr__('x', 5)       # Called by o.x = 5
o.__delattr__('x')          # Called by del o.x
y = o.__dir__()             # Called by y = dir(o)

# Container object item access
y = o.__len__()             # Called by y = len(o)
y = o.__getitem__(3)        # Called by y = o[3]
o.__setitem__(3, 9)         # Called by o[3] = 9
o.__delitem__(3)            # Called by del o[3]
y = o.__contains__(3)       # Called by y = 3 in o
y = o.__iter__()            # Called by y = iter(o)
y = o.__reversed__()        # Called by y = reversed(o)
y = o.__missing__(3)        # Called by y = o[3] (from within dict.__getitem__(3)) if o's
                            # class inherits from dict and o[3] doesn't exist

# Iterator object item access
y = o.__next__()            # Called by y = next(o)

# Object conversion
y = o.__repr__()            # Called by y = repr(o)
y = o.__str__()             # Called by y = str(o)
y = o.__format__('^6')      # Called by y = format(o, '^6') or y = '{:^6}'.format(o)
y = o.__bytes__()           # Called by y = bytes(o)
y = o.__bool__()            # Called by y = bool(o)
y = o.__hash__()            # Called by y = hash(o)
y = o.__int__()             # Called by y = int(o)
y = o.__float__()           # Called by y = float(o)
y = o.__complex__()         # Called by y = complex(o)

# Object calling
y = o.__call__()            # Called by y = o()

# Context management
y = o.__enter__()           # Called when entering with o as y: pass
o.__exit__(None, None, None)# Called when exiting with o: pass (if no exceptions)
y = o.__exit__(excp_type, excp_val, traceback)  # Called when exiting with o: raise excp;
                            # if bool(y) == True, the exception is suppressed

# Object comparison
y = o.__lt__(o2)            # Called by y = o < o2, or by y = o2 > o if o's type is
                            # subclass of o2's type or if o2.__gt__(o) returns
                            # NotImplemented
y = o.__le__(o2)            # Called by y = o <= o2, or by y = o2 >= o if o's type is
                            # subclass of o2's type or if o2.__ge__(o) returns
                            # NotImplemented
y = o.__eq__(o2)            # Called by y = o == o2, or by y = o2 == o if o's type is
                            # subclass of o2's type or if o2.__eq__(o) returns
                            # NotImplemented
y = o.__ne__(o2)            # Called by y = o != o2, or by y = o2 != o if o's type is
                            # subclass of o2's type or if o2.__ne__(o) returns
                            # NotImplemented
y = o.__gt__(o2)            # Called by y = o > o2, or by y = o2 < o if o's type is
                            # subclass of o2's type or if o2.__lt__(o) returns
                            # NotImplemented
y = o.__ge__(o2)            # Called by y = o >= o2, or by y = o2 <= o if o's type is
                            # subclass of o2's type or if o2.__le__(o) returns
                            # NotImplemented

# Unary arithmetic operations
y = o.__neg__()             # Called by y = -o
y = o.__pos__()             # Called by y = +o
y = o.__abs__()             # Called by y = abs(o)
y = o.__invert__()          # Called by y = ~o
y = o.__round__()           # Called by y = round(o)
y = o.__round__(2)          # Called by y = round(o, 2)
y = o.__trunc__()           # Called by y = math.trunc(o)
y = o.__floor__()           # Called by y = math.floor(o)
y = o.__ceil__()            # Called by y = math.ceil(o)
y = o.__index__()           # Called by y = operator.index(o) or 'hello'[:o] (returns
                            # 'hello'[:y]) or hex(o) (returns hex(y)) or wherever an exact
                            # integer is needed

# Binary arithmetic operations
y = o.__add__(o2)           # Called by y = o + o2 (but see __radd__)
y = o.__sub__(o2)           # Called by y = o - o2 (but see __rsub__)
y = o.__mul__(o2)           # Called by y = o * o2 (but see __rmul__)
y = o.__matmul__(o2)        # Called by y = o @ o2 (but see __rmatmul__)
y = o.__truediv__(o2)       # Called by y = o / o2 (but see __rtruediv__)
y = o.__floordiv__(o2)      # Called by y = o // o2 (but see __rfloordiv__)
y = o.__mod__(o2)           # Called by y = o % o2 (but see __rmod__)
y = o.__divmod__(o2)        # Called by y = divmod(o, o2) (but see __rdivmod__)
y = o.__pow__(o2)           # Called by y = o ** o2 or y = pow(o, o2) (but see __rpow__)
y = o.__pow__(o2, 5)        # Called by y = pow(o, o2, 5) (no __rpow__ variant)
y = o.__lshift__(o2)        # Called by y = o << o2 (but see __rlshift__)
y = o.__rshift__(o2)        # Called by y = o >> o2 (but see __rrshift__)
y = o.__and__(o2)           # Called by y = o & o2 (but see __rand__)
y = o.__or__(o2)            # Called by y = o | o2 (but see __ror__)
y = o.__xor__(o2)           # Called by y = o ^ o2 (but see __rxor__)

# Reverse binary arithmetic operations
y = o.__radd__(o2)          # Called by y = o2 + o if o's type is subclass of o2's
                            # type or if o2.__add__(o) returns NotImplemented
y = o.__rsub__(o2)          # Called by y = o2 - o if o's type is subclass of o2's
                            # type or if o2.__sub__(o) returns NotImplemented
y = o.__rmul__(o2)          # Called by y = o2 * o if o's type is subclass of o2's
                            # type or if o2.__mul__(o) returns NotImplemented
y = o.__rmatmul__(o2)       # Called by y = o2 @ o if o's type is subclass of o2's
                            # type or if o2.__matmul__(o) returns NotImplemented
y = o.__rtruediv__(o2)      # Called by y = o2 / o if o's type is subclass of o2's
                            # type or if o2.__truediv__(o) returns NotImplemented
y = o.__rfloordiv__(o2)     # Called by y = o2 // o if o's type is subclass of o2's
                            # type or if o2.__floordiv__(o) returns NotImplemented
y = o.__rmod__(o2)          # Called by y = o2 % o if o's type is subclass of o2's
                            # type or if o2.__mod__(o) returns NotImplemented
y = o.__rdivmod__(o2)       # Called by y = divmod(o2, o) if o's type is subclass of o2's
                            # type or if o2.__divmod__(o) returns NotImplemented
y = o.__rpow__(o2)          # Called by y = o2 ** o or y = pow(o2, o) if o's type is
                            # subclass of o2's type or if o2.__pow__(o) returns
                            # NotImplemented
                            # pow(o2, o, 5) always calls o2.__pow__(o, 5), never __rpow__
y = o.__rlshift__(o2)       # Called by y = o2 << o if o's type is subclass of o2's
                            # type or if o2.__lshift__(o) returns NotImplemented
y = o.__rrshift__(o2)       # Called by y = o2 >> o if o's type is subclass of o2's
                            # type or if o2.__rshift__(o) returns NotImplemented
y = o.__rand__(o2)          # Called by y = o2 & o if o's type is subclass of o2's
                            # type or if o2.__and__(o) returns NotImplemented
y = o.__ror__(o2)           # Called by y = o2 | o if o's type is subclass of o2's
                            # type or if o2.__or__(o) returns NotImplemented
y = o.__rxor__(o2)          # Called by y = o2 ^ o if o's type is subclass of o2's
                            # type or if o2.__xor__(o) returns NotImplemented

# Augmented arithmetic assignment
o = o.__iadd__(o2)          # Called by o += o2 (falls back to o = o + o2 if no __iadd__)
o = o.__isub__(o2)          # Called by o -= o2
o = o.__imul__(o2)          # Called by o *= o2
o = o.__imatmul__(o2)       # Called by o @= o2
o = o.__itruediv__(o2)      # Called by o /= o2
o = o.__ifloordiv__(o2)     # Called by o //= o2
o = o.__imod__(o2)          # Called by o %= o2
o = o.__ipow__(o2)          # Called by o **= o2
o = o.__ilshift__(o2)       # Called by o <<= o2
o = o.__irshift__(o2)       # Called by o >>= o2
o = o.__iand__(o2)          # Called by o &= o2
o = o.__ior__(o2)           # Called by o |= o2
o = o.__ixor__(o2)          # Called by o ^= o2

# Coroutine methods
y = o.__await__()           # Called by z = await o (inside a coroutine), where y is a
                            # coroutine wrapper (handled by await) and z is the coroutine's
                            # return value when it completes
y = o.__aiter__()           # Called by y = aiter(o) or async for z in o: pass (inside a
                            # coroutine) to get an asynchronous iterator y for the
                            # asynchronous iterable o; asynchronous variant of __iter__
y = o.__anext__()           # Called by y = anext(o) or repeatedly by
                            # async for z in o: pass (inside a coroutine) to get an
                            # awaitable y which - when it completes - produces the next
                            # item in o (to be assigned to z); asynchronous variant __next__
y = o.__aenter__()          # Called when entering async with o as z: pass (inside a
                            # coroutine); y is an awaitable which - when it completes -
                            # produces the value to be assigned to z; asynchronous variant
                            # of __enter__
y = o.__aexit__(None, None, None)   # Called when exiting async with o: pass (inside a
                            # coroutine) (if no exceptions); y is an awaitable which
                            # completes when the __aexit__ coroutine completes;
                            # asynchronous variant of __exit__
y = o.__aexit__(excp_type, excp_val, traceback)     # Called when exiting
                            # async with o: raise excp (inside a coroutine); y is an
                            # awaitable which - when it completes - produces a value, and
                            # if bool(value) == True, the exception is suppressed;
                            # asynchronous variant of __exit__

# Buffer protocol
y = o.__buffer__(flags)     # Called by z = memoryview(o) which sets flags =
                            # inspect.BufferFlags.FULL_RO and wraps y in z
o.__release_buffer__(y)     # Called by z.release() where z = memoryview(o) and y =
                            # o.__buffer__(flags) as described above
See also special methods for descriptors.

Modules

Module Creation and Usage

File mymodule.py:
# Here's a docstring for this module:
"""Any Python file can be imported as a module,
or run as a top level script.
"""

def f(x):
    return x * 2

if __name__ == '__main__':      # If this file is run as a script, its module name is
                                # '__main__',
    print(f(10))                # in which case call f and print the result '20'
else:                           # Otherwise, this file is imported as a module,
    print('Module:', __name__)  # so print 'Module: mymodule'
Some other Python file:
import mymodule         # Runs mymodule.py; puts all its names into namespace 'mymodule';
                        # the module prints 'Module: mymodule'
import os, re           # Imports multiple modules in same statement
type(mymodule)          # Returns class types.ModuleType with name 'module'
print(mymodule.f(8))    # Calls mymodule's function f, and prints result '16'
print(mymodule.__doc__) # Prints mymodule's docstring: 'Any Python file can be ...'
print(__doc__)          # Prints 'None' (this module has no docstring)
Some other Python file:
import mymodule as m    # Runs mymodule.py; puts all its names into namespace 'm';
                        # the module prints 'Module: mymodule'
import os as o, re as r # Imports multiple modules in same statement
print(m.f(8))           # Calls mymodule's function f, and prints result '16'
Some other Python file:
from mymodule import f  # Runs mymodule.py; puts its name f into our namespace;
                        # the module prints 'Module: mymodule'
from re import sub, subn    # Imports multiple names from module in same statement
print(f(8))             # Calls function f (defined in mymodule), and prints result '16'
Some other Python file:
from mymodule import f as g # Runs mymodule.py; binds name g in our namespace to whatever
                            # mymodule's name f is bound to;
                            # the module prints 'Module: mymodule'
from re import (sub as s,   # Imports multiple names from module in same statement;
                subn as sn) # parentheses can be used to avoid backslash line-continuation
print(g(8))             # Calls function g (f in mymodule), and prints result '16'
Some other Python file:
from mymodule import *  # Runs mymodule.py; puts all its names into our namespace;
                        # the module prints 'Module: mymodule'
print(f(8))             # Calls function f (defined in mymodule), and prints result '16'

Some Standard Modules

Python has a large number of standard modules which are included in the Python installation. A few are mentioned here.
import sys              # System functionality, e.g. argv, exit(), stdin, stdout, stderr,
                        # path, version_info
import os               # Operating system functions, e.g. getcwd(), chdir(), mkdir(),
                        # makedirs(), rmdir(), remove(), rename(), walk()
import os.path          # Path manipulation, e.g. exists(), join(), abspath(), dirname(),
                        # basename()
import subprocess       # Running external programs, e.g. run('echo hi', shell=True, \
                        # capture_output=True).stdout
import logging          # Logging, e.g. info(), warning(), error(), debug()
import atexit           # Exit handler registration, e.g. register()

import math             # Math constants and functions, e.g. pi, e, sqrt(), sin()
import cmath            # Complex math functions, e.g. sqrt(-1)
import decimal          # Class for precise representation of decimal numbers, e.g.
                        # Decimal('0.1'); see float type
import random           # Random number generation, e.g. random(), randint(), randbytes()
import functools        # Function manipulation, e.g. partial(), reduce(), @wraps
import itertools        # Iterator construction, e.g. cycle(), chain(), permutations()

import re               # See regular expressions
import collections      # See container types
import copy             # Shallow/deep object copying functions, e.g. copy(), deepcopy()

import time             # Time functions, e.g. sleep(), monotonic()
import datetime         # Date/time objects, e.g. datetime.now(timezone.utc).astimezone() \
                        # .strftime('%Y-%m-%d %H:%M:%S.%f UTC%z')

Non-Standard Modules

In addition to the large number of standard modules, an even larger number of non-standard modules are available for installation. A few are mentioned here.
import numpy            # Numerical vector/matrix math
import scipy            # Scientific/engineering math (uses numpy)
import matplotlib       # Graphing (uses numpy)

Before they can be imported by Python scripts, non-standard modules/packages need to be installed on top of the relevant Python installation. This may be done by running (on the command line) the 'pip' command belonging to the Python installation. If you have multiple Python installations, e.g. for different Python versions, make sure to use the 'pip' command belonging to the one you wish to install modules for.

Example of a Linux command (not Python code) to install numpy:

/path/to/correct/pip install numpy
or
/path/to/correct/python -m pip install numpy
On Windows the Python launcher py.exe may be used in the following way (where 'X.Y' is the Python version to install the module for):
py -X.Y -m pip install numpy

Packages

A package is a collection of modules and may also contain nested subpackages. In its simplest form a package is a directory (on a disk) containing an __init__.py file along with the module files and subpackage directories belonging to the package.

When a package is imported, the resulting package object has the same type as a module object, but has an additional __path__ attribute pointing to the package directory.

Let's assume we have the following directory structure and files:

mypackage/
__init__.py
mymodule0.py
subpkg1/
__init__.py
mymodule1.py
mymodule1b.py
subpkg2/
__init__.py
mymodule2.py
subpkg3/
__init__.py
mymodule3.py
mymodule3b.py

The file contents are as follows:

File mymodule1.py:
# Importing using relative package references
from . import mymodule1b        # Imports mymodule1b from same subpackage
from ..subpkg3 import mymodule3 # Imports mymodule3 from sibling subpackage subpkg3
from .. import mymodule0        # Imports mymodule0 from parent package mypackage
# Importing using absolute package reference (from top level package)
from mypackage.subpkg3 import mymodule3b    # Imports mymodule3b

def who():
    print(__name__)     # Prints the module name (incl. parent package names)

def who1b():
    mymodule1b.who()    # Calls the who function in mymodule1b

def who3():
    mymodule3.who()     # Calls the who function in mymodule3

def who3b():
    mymodule3b.who()    # Calls the who function in mymodule3b

def who0():
    mymodule0.who()     # Calls the who function in mymodule0
All other mymodule*.py files:
def who():
    print(__name__)     # Prints the module name (incl. parent package names)
File __init__.py under subpkg1:
__all__ = ['mymodule1']     # Specifies which modules to import from subpkg1 if '*' is used
All other __init__.py files are empty:


The package can now be used by some other Python file:
import mypackage.subpkg1.mymodule1  # Imports mymodule1 from the package
type(mypackage)                     # Returns class types.ModuleType with name 'module'
                                    # (a package is also a module)
mypackage.subpkg1.mymodule1.who()   # Prints 'mypackage.subpkg1.mymodule1'
Some other Python file:
from mypackage.subpkg1 import mymodule1 # Imports mymodule1 from the package
mymodule1.who()                         # Prints 'mypackage.subpkg1.mymodule1'
mymodule1.who1b()                       # Prints 'mypackage.subpkg1.mymodule1b'
mymodule1.who3()                        # Prints 'mypackage.subpkg3.mymodule3'
mymodule1.who3b()                       # Prints 'mypackage.subpkg3.mymodule3b'
mymodule1.who0()                        # Prints 'mypackage.mymodule0'
Some other Python file:
from mypackage.subpkg1 import *     # Imports all modules mentioned in the __all__ list in
                                    # subpkg1's __init__.py  (i.e. mymodule1)
from mypackage.subpkg2 import *     # Imports nothing because subpkg2's __init__.py doesn't
                                    # define an __all__ list
mymodule1.who()                     # Prints 'mypackage.subpkg1.mymodule1'
mymodule2                           # Raises NameError because mymodule2 was never imported

Names

Binding Names to Objects

Almost everything (values, functions, classes, modules) is an object - mutable or immutable. Names are not objects themselves - they come into existence when they are bound (assigned) to objects, and may subsequently be rebound to different objects or deleted. An object may have multiple names bound to it, and becomes irrelevant (possibly deleted) when there are no more references to it. Names are NOT permanent references to fixed storage locations which can be filled with values as in some other languages (e.g. C).
x = 10                  # (Creates and) binds name x to (immutable) object 10
y = x                   # Binds name y to same object as name x
del x                   # Forgets name x; its previously bound object is still bound to y
z = 10                  # Binds name z to (immutable) object 10 (possibly same object 10
                        # that y is bound to; mutable objects will never be reused like
                        # this)
y = 11                  # Binds name y to a different object than before
p = q = ['hi']          # Binds names p and q to same (mutable) object (first p, then q!)
p, z = z, p             # Swaps objects bound to names p and z (so p = 10, z = ['hi'])

def f1(b):              # Binds name f1 to function object defined here, and binds name b
                        # to object passed as argument to f1
    b = 3               # Binds name b to different object than was passed to f1 (this
                        # does not alter the passed object nor rebind whatever name was
                        # used to refer to that object when calling f1)
    return b            # Returns object bound to name b (and name b goes out of scope)

x = f1(y)               # Calls function object bound to name f1, and passes object bound
                        # to name y as argument (name y itself is not passed and can't be
                        # rebound from within f1);
                        # also binds name x to object returned by function call

class C1:               # Binds name C1 to class object defined here
    pass

c = C1()                # Binds name c to new instance object created from class object
                        # bound to name C1

y = (x := 2 + 3) + x    # Adds 2 and 3 and binds name x to resulting object 5, then adds
                        # that and object bound to name x (i.e. 5) and binds name y to
                        # resulting object 10; x is still bound to 5 after this statement

Name Scope

def f1(x, y):
    global d, e             # Allows this function to modify module level names d & e
    print(b, x)             # Prints '1 1'; no need for 'global' to read external b, but
                            # this will fail if b is assigned anywhere within f1
                            # (UnboundLocalError exception if assigned after this line)
                            # (except if we cheat and assign to globals()['b'])
    c, d, e = 10, 11, 12    # Creates local c hiding external c (because no 'global')
                            # (but we can still cheat and read/write globals()['c']);
                            # also creates e at module level (did not exist before this!)
    f[0] = 13               # f refers to mutable object which can be changed even though
                            # f is not listed as 'global' (but without 'global' external f
                            # can't be changed to reference another object)
    x = 14                  # x was same as external b, but is now 14, while b is still 1
    y[0] = 15               # y refers to same object as external g, and that object can
                            # be changed via y (but g can't be changed to refer to another
                            # object)
    f2()                    # Calls external function f2
    if x == 14:             # Function definitions may be conditional like any statement
                            # (the 'if' body does not have its own local name scope)
        def f3(d):          # f3 is defined within f1, so can read f1's locals
            print(d)        # Prints local parameter d which is hiding external d
            global c        # Gives f3 access to module level c - not local c in f1
            c = x           # Assigns f1's local x to module level c (now c = 14)
            nonlocal h      # Gives f3 access to outer scope h (excl. module level)
            h = 'C'         # Changes f1's h from 'B' to 'C'

    h = 'B'                 # Creates local h hiding external h
    f3(30)                  # Calls inner function f3 which prints '30'
    print(b,c,d,e,f,g,h)    # Prints '1 10 11 12 [13] [15] C'; f3 changed h

def f2():                   # f2 is not defined within f1, so can't read f1's locals
    pass

b, c, d = 1, 2, 3           # Names read by function f1 must be defined before call to f1
f, g, h = [4], [5], 'A'     # but not necessarily before definition of f1
f1(b, g)                    # Calls function f1 and sets parameters x = b and y = g
print(b,c,d,e,f,g,h)        # Prints '1 14 11 12 [13] [15] A'; f1 changed d, e, and objects
                            # referenced by f & g (but didn't change f & g themselves);
                            # f3 changed c
Notes:

Coroutines/Tasks/Threads/Processes

import asyncio              # Support for multiple concurrent tasks/coroutines
import threading            # Support for multiple threads
import multiprocessing      # Support for multiple processes
import concurrent.futures   # Used here for thread and process pool executors
import contextlib           # Used here to suppress exceptions in a with statement
import os                   # Used here to get process id
import datetime             # Used here to get/format current time and do time calculations

# A class to handle thread-safe and process-safe logging
class Logger:
    def __init__(self, job_id, lock):
        self.job_id = job_id
        self.lock = lock

    # The __call__() method is called automatically when an instance of this class is
    # called as a function
    def __call__(self, msg):
        # Grab the global lock before printing to avoid simultaneous printing from
        # different threads/processes
        with self.lock:
            print('{}, job {} (proc {:5}, thread {:5}): {}'.format(
                datetime.datetime.now().strftime('%H:%M:%S.%f'),
                self.job_id, os.getpid(), threading.get_ident(), msg), flush=True)

# A coroutine function identified by the async keyword; returns a coroutine object
# representing the code in the function body, which is capable of cooperatively sharing
# the same thread with other coroutines by occasionally allowing itself to be suspended
# (e.g. by doing an await); execution of and switching between coroutines is handled by an
# event loop
async def concurrent_job(job_id, s, lock):
    log = Logger(job_id, lock)
    log(f'Started! Input={s!r}')
    for c in s:
        # Suspend this task for 1 second
        await asyncio.sleep(1)
        log(c)
    log(f'Done! Result={c!r}')
    return c

# Another coroutine function whose coroutine object runs forever until cancelled
async def endless_concurrent_job(job_id, lock, reraise=False):
    log = Logger(job_id, lock)
    loop = asyncio.get_running_loop()
    time = loop.time()
    log(f'Started! Time={time:.3f}')
    try:
        while True:
            # Suspend this task for 1.7 second
            await asyncio.sleep(1.7)
            delta = loop.time() - time
            time += delta
            log(f'Time delta={delta:.3f}')
    except asyncio.CancelledError as e:
        # An exception was raised to cancel this job
        log(f'Cancelled, msg={e.args[0]!r}')
        if reraise:
            raise
    time = loop.time()
    log(f'Time={time:.3f}')
    log(f'Result={time!r}')
    return time

# A coroutine generator function identified by the async and yield keywords; returns a
# generator object which - when iterated - runs each iteration in a coroutine and returns
# the next yielded item as the result of the coroutine
async def concurrent_generator_func(job_id, n, lock):
    log = Logger(job_id, lock)
    log(f'Started! Input={n}')
    for i in range(n):
        # Suspend this task for 1 second
        await asyncio.sleep(1)
        log(f'Sending {i}')
        # Produce the iteration item
        yield i
    log('Done!')

# A normal non-cooperative function which blocks the whole thread until it returns, so it
# should be run on a separate thread or in a separate process if the main thread needs to
# do other things in parallel
def blocking_job(job_id, step, lock):
    log = Logger(job_id, lock)
    start_time = datetime.datetime.now()
    end_time = start_time + datetime.timedelta(seconds=5)
    log(f'Started! Input={step}')
    i = 0
    while datetime.datetime.now() < end_time:
        i += step
    log(f'Done! Result={i!r}')
    return i

async def main():
    # Create a single global lock to be passed to all threads and processes to ensure that
    # they don't corrupt each other's output (the simpler threading.RLock() can be used for
    # single-process programs, and no lock is needed for single-thread programs)
    lock = multiprocessing.Manager().RLock()

    log = Logger('Main', lock)
    log('Started!')

    # Get running event loop for our thread
    loop = asyncio.get_running_loop()

    # Start 3 concurrent jobs/coroutines in new separate tasks (in same thread as ours;
    # return value is a task which is a subclass of a 'future' which represents the job's
    # promised result when it completes; in addition to being a 'future' a task handles the
    # execution of the wrapped coroutine object)
    con1_task = asyncio.create_task(concurrent_job('Con1', 'ABCDEF', lock))
    con2_task = asyncio.create_task(endless_concurrent_job('Con2', lock))
    con3_task = asyncio.create_task(endless_concurrent_job('Con3', lock, reraise=True))

    # Start parallel blocking job in new separate thread (in same process as ours; 1st arg
    # None selects default executor concurrent.futures.ThreadPoolExecutor, next arg
    # blocking_job is function to call in new thread, and remaining args are passed to that
    # function; return value is a 'future' object representing this job's promised result
    # when it completes)
    blk_t_future = loop.run_in_executor(None, blocking_job, 'BlkT', 1, lock)

    # Start parallel blocking job in new separate process (a separate Python interpreter is
    # started in that process, loads this module, and calls function blocking_job with
    # specified args; WARNING: make sure all this code is not run again when this module is
    # loaded in that new process, or else it will again spawn a new process etc.)
    process_executor = concurrent.futures.ProcessPoolExecutor()
    blk_p_future = loop.run_in_executor(process_executor, blocking_job, 'BlkP', -1, lock)

    # While the above jobs are running, iterate through a generator by running each
    # iteration in a separate coroutine (in same thread as ours)
    con_g_generator = concurrent_generator_func('ConG', 3, lock)
    async for i in con_g_generator:
        log(f'Received {i} from ConG')

    # Wait until all jobs (except the endless ones) complete, then get their results as a
    # list (list items have same order as args to gather())
    results = await asyncio.gather(con1_task, blk_t_future, blk_p_future)
    # Convert the results into a dict with the job names as keys
    results = dict(zip(('Con1', 'BlkT', 'BlkP'), results))

    # Stop endless job Con2 (by raising a CancelledError exception inside it)
    con2_task.cancel('Bye')
    # Wait until the job actually stops and get its result (the CancelledError exception is
    # caught and suppressed in Con2, so no exception is seen here)
    results['Con2'] = await con2_task

    # Stop endless job Con3 (by raising a CancelledError exception inside it)
    con3_task.cancel('Ciao')
    # Wait until the job actually stops (the CancelledError exception is not suppressed in
    # Con3, so suppress it here; the exception prevents 'await' from returning a result)
    with contextlib.suppress(asyncio.CancelledError):
        results['Con3'] = await con3_task

    for job in results:
        log(f'{job} result: {results[job]!r}')
    return 'Done!'

# Make sure that main() only gets called when this module is run as a script, not when it
# is imported as a module (as it will be inside the subprocess spawned by main() - see
# WARNING above)
if __name__ == '__main__':
    # Run coroutine main() and print its return value when it completes
    print(asyncio.run(main()))
Example of output from above code:
16:59:17.484854, job Main (proc 14204, thread 12668): Started!
16:59:17.487854, job BlkT (proc 14204, thread 12036): Started! Input=1
16:59:18.100757, job ConG (proc 14204, thread 12668): Started! Input=3
16:59:18.148753, job BlkP (proc 10388, thread 11804): Started! Input=-1
16:59:18.244754, job Con1 (proc 14204, thread 12668): Started! Input='ABCDEF'
16:59:18.324757, job Con2 (proc 14204, thread 12668): Started! Time=1171.968
16:59:18.388756, job Con3 (proc 14204, thread 12668): Started! Time=1172.062
16:59:19.226306, job ConG (proc 14204, thread 12668): Sending 0
16:59:19.257555, job Main (proc 14204, thread 12668): Received 0 from ConG
16:59:19.319856, job Con1 (proc 14204, thread 12668): A
16:59:20.120442, job Con2 (proc 14204, thread 12668): Time delta=1.828
16:59:20.179725, job Con3 (proc 14204, thread 12668): Time delta=1.797
16:59:20.336100, job ConG (proc 14204, thread 12668): Sending 1
16:59:20.380381, job Main (proc 14204, thread 12668): Received 1 from ConG
16:59:20.412978, job Con1 (proc 14204, thread 12668): B
16:59:21.462500, job ConG (proc 14204, thread 12668): Sending 2
16:59:21.493750, job Main (proc 14204, thread 12668): Received 2 from ConG
16:59:21.524633, job ConG (proc 14204, thread 12668): Done!
16:59:21.565310, job Con1 (proc 14204, thread 12668): C
16:59:21.913473, job Con2 (proc 14204, thread 12668): Time delta=1.797
16:59:21.977473, job Con3 (proc 14204, thread 12668): Time delta=1.797
16:59:22.487480, job BlkT (proc 14204, thread 12036): Done! Result=10168136
16:59:22.612480, job Con1 (proc 14204, thread 12668): D
16:59:23.140135, job BlkP (proc 10388, thread 11804): Done! Result=-10056909
16:59:23.612465, job Con1 (proc 14204, thread 12668): E
16:59:23.674968, job Con2 (proc 14204, thread 12668): Time delta=1.766
16:59:23.721724, job Con3 (proc 14204, thread 12668): Time delta=1.750
16:59:24.623154, job Con1 (proc 14204, thread 12668): F
16:59:24.623154, job Con1 (proc 14204, thread 12668): Done! Result='F'
16:59:24.623154, job Con2 (proc 14204, thread 12668): Cancelled, msg='Bye'
16:59:24.624165, job Con2 (proc 14204, thread 12668): Time=1178.312
16:59:24.624165, job Con2 (proc 14204, thread 12668): Result=1178.312
16:59:24.624165, job Con3 (proc 14204, thread 12668): Cancelled, msg='Ciao'
16:59:24.625165, job Main (proc 14204, thread 12668): Con1 result: 'F'
16:59:24.625165, job Main (proc 14204, thread 12668): BlkT result: 10168136
16:59:24.625165, job Main (proc 14204, thread 12668): BlkP result: -10056909
16:59:24.625165, job Main (proc 14204, thread 12668): Con2 result: 1178.312
Done!

Running External Programs

High-Level 'subprocess.run' API

The subprocess.run() method provides an easy way to run an external program in a subprocess and wait for it to complete.
import subprocess                               # Imports subprocess module
command_line = 'findstr /b [A-P] | sort'        # Prepares a shell command line to execute
some_input = 'Lisa\nBart\nR2D2\n'               # Prepares some command input
completed_proc = subprocess.run(command_line,         # Executes the command line
                                shell=True,           # in a shell
                                text=True,            # with text (not bytes) input/output,
                                input=some_input,     # some_input as stdin,
                                capture_output=True)  # stdout/stderr captured,
                                                      # and waits for command completion
completed_proc.returncode                       # Returns 0 (command succeeded)
completed_proc.stdout                           # Returns 'Bart\nLisa\n' (command output)
completed_proc.stderr                           # Returns '' (no error output from command)

Low-Level 'subprocess.Popen' API

The subprocess.Popen class provides more fine-grained control than the run() method described above, including the ability to do something else while the subprocess is running.
import subprocess                               # Imports subprocess module

# The following Windows command line will wait for 1 second (because the waitfor command
# fails to receive the signal 'nothing' within the specified 1-second deadline), then
# prints DONE; the final exit code is 0 because the echo command succeeds
command_line = 'waitfor nothing /t 1 2>NUL || echo DONE'

# An instance of the Popen class can be used as a context manager in a 'with' statement
# to ensure that pipes are closed and the subprocess is waited for when the 'with' block
# exits (but here the proc.wait() below explicitly waits for the subprocess)
with subprocess.Popen(command_line,             # Executes the command line
                      shell=True,               # in a shell
                      text=True,                # with text (not bytes) input/output,
                      stdout=subprocess.PIPE) as proc:  # stdout captured,
                                                # and doesn't wait for command completion
    print('Command launched')
    assert proc.poll() is None                  # proc.poll() returns None while the
                                                # subprocess is still running
    proc.wait()                                 # Waits for the subprocess to complete
                                                # (WARNING: if the subprocess produces more
                                                # output than will fit in the pipe buffer,
                                                # it will stall and never end because
                                                # proc.wait does not read from proc.stdout
                                                # while waiting)
    assert proc.poll() == proc.returncode       # After completion, proc.poll() returns the
                                                # command exit code
    print('Finished with code %d and output: %s' % (proc.returncode, proc.stdout.read()))
Command launched
Finished with code 0 and output: DONE

Here's another example showing how the external program output can be obtained line by line as each line becomes available:
import subprocess                               # Imports subprocess module

# The following Windows command line prints the numbers 1 through 5 and waits for 1 second
# after each number; the final exit code is 1 because the waitfor command fails to receive
# the signal 'nothing'
command_line = 'for /L %i in (1,1,5) do @(echo %i && waitfor nothing /t 1 2>NUL)'

with subprocess.Popen(command_line,             # Executes the command line
                      shell=True,               # in a shell
                      text=True,                # with text (not bytes) input/output,
                      bufsize=1,                # line buffering,
                      stdout=subprocess.PIPE) as proc:  # stdout captured,
                                                # and doesn't wait for command completion
    for line in proc.stdout:                    # Gets 1 line at a time from the subprocess
        assert proc.poll() is None              # proc.poll() returns None while the
                                                # subprocess is still running
        print(line, end='')                     # Prints the received line of text

# When the above 'with' statement exits, it waits for the subprocess to complete
assert proc.poll() == proc.returncode           # After completion, proc.poll() returns the
                                                # command exit code
print('Finished with code %d' % proc.returncode)
1 
2 
3 
4 
5 
Finished with code 1