(This is a bit of a hodgepodge at the moment…)

# 1 Truthiness and Falsiness

(), [], {}, set(), '', 0, and None are all Falsey. Everything else is Truthy.

# 2 Equality and comparing

>>> ('a', 'b', 'c') == ['a', 'b', 'c']
False

>>> list(('a', 'b', 'c')) == ['a', 'b', 'c']
True

If they’re of the same type, Python compares them item-by-item. If they contain a data structure, Python dives in and compares recursively. Works for dicts and sets as well.

# 3 Comprehensions

List comprehension: [x**2 for x in range(10)]
Dict comprehension: {'~' + str(i) + '~': x**2 for i,x in enumerate(range(10))}
Set comprehension: {x for x in 'aabaabbbaababa'}

# 4 Unpacking

def foo(a, b):
print(a, b)

x = [1, 2]
foo(*xs)  # Unpacks the values in x into positional args.
# As if you'd called
foo(1, 2)

d = {'a': 1, 'b': 2}
foo(**d)  # Unpacks the values into kwargs.
# As if you'd called
foo(a=1, b=2)

Writing def foo(*args) ... creates a function that accepts zero or more positional args, and which then slurps them all into a list:

def foo(*stuff):
print(stuff)

x = [1, 2]
foo(*x) # Unpacks `x`, and prints out `[1, 2]`.
# Works as if you'd called
foo(1, 2)

This effectively unpacks x, then repacks it into the stuff param in the body of the function.

Writing def foo(**kwargs) ... creates a function that accepts zero or more keyword-args, and which then slurps them all into a dict:

def foo(**kwargs):
print(kwargs)

# foo(1, 2, 3)      # ERROR
foo(a=1, b=2, c=3)  # prints out a dict

# And note:
d = {'a': 11, 'b': 12, 'c': 13}
foo(**d)
# Works as if you'd called
foo(a=11, b=12, c=13)

This effectively unpacks d into keyword-args, then repacks it into a dict (the kwargs param) in the body of the function.

* * *

This is all separate from the other type of unpacking (“extended iterable unpacking”):

a, *b, c = [1, 2, 3, 4, 5]  # `b` gets `[2, 3, 4]`.

There’s no **whatever (“kwargs”-style) syntax for this kind of unpacking.

list(zip(*m))