Originally inspired from this blog post. But many have been customized and rewritten into an IPython Notebook.

Note: Written for Python 3

Unpacking

a, b, c = 1, 2, 3

a, b, c

(1, 2, 3)
a, b, c = [1, 2, 3]

a, b, c
(1, 2, 3)
a, b, c = (2 * i + 1 for i in range(3))
a, b, c
(1, 3, 5)
a, (b, c), d = [1, (2, 3), 4]

a, b, c, d
(1, 2, 3, 4)

Iterating over list index and value pairs (enumerate)

a = ['Hello', 'world', '!']

for i, x in enumerate(a):
    print('{}: {}'.format(i, x)).. code-block:: python

0: Hello
1: world
2: !

Zipping and unzipping lists and iterables

a = [ 1,   2,   3 ]
b = ['a', 'b', 'c']
z = zip(a, b)

list(zip(*z))
[(1, 2, 3), ('a', 'b', 'c')]

Grouping adjacent list items using zip

a = [1, 2, 3, 4, 5, 6]

# Using iterators
group_adjacent = lambda a, k: list(zip(*([iter(a)] * k)))

group_adjacent(a, 3)
[(1, 2, 3), (4, 5, 6)]
group_adjacent(a, 2)
[(1, 2), (3, 4), (5, 6)]
group_adjacent(a, 1)
[(1,), (2,), (3,), (4,), (5,), (6,)]
# Using slices
from itertools import islice

group_adjacent = lambda a, k: list(zip(*(islice(a, i, None, k) for i in range(k))))

group_adjacent(a, 3)
[(1, 2, 3), (4, 5, 6)]
group_adjacent(a, 2)
[(1, 2), (3, 4), (5, 6)]
group_adjacent(a, 1)
[(1,), (2,), (3,), (4,), (5,), (6,)]

Sliding windows (n-grams) using zip and iterators

from itertools import islice
def n_grams(a, n):
    z = (islice(a, i, None) for i in range(n))
    return list(zip(*z))

a = [1, 2, 3, 4, 5, 6]

n_grams(a, 3)
[(1, 2, 3), (2, 3, 4), (3, 4, 5), (4, 5, 6)]

n_grams(a, 2)
[(1, 2), (2, 3), (3, 4), (4, 5), (5, 6)]
n_grams(a, 4)
[(1, 2, 3, 4), (2, 3, 4, 5), (3, 4, 5, 6)]

Inverting a dictionary using zip

m = {'a': 1, 'b': 2, 'c': 3, 'd': 4}

# Assuming all values are immutable types
mi = dict(zip(m.values(), m.keys()))

mi
{1: 'a', 2: 'b', 3: 'c', 4: 'd'}

List Flattening

nest = [[1, 2], [3, 4], [5, 6]]

Using chain

>>> from itertools import chain
>>> list(chain.from_iterable(nest))
[1, 2, 3, 4, 5, 6]

Using sum

>>> sum(nest, [])
[1, 2, 3, 4, 5, 6]

Using List Comprehensions

>>> [l for n in nest for l in n]
[1, 2, 3, 4, 5, 6]

What the evil voodoo magic is this?!

Better to think of it this way

Go back to basics

# Normal List Comp
>>> [n for n in nest]
[[1, 2], [3, 4], [5, 6]]

List Comp Inside List Comp
# list comp-inception
>>> [[l for l in n] for n in nest]
[[1, 2], [3, 4], [5, 6]]

Now Split Up Components

[l for l in n]    => [l, l],  [l, l]
[n for n in nest] => [[n], [n], [n]]

Map out the Logic

[Part A]   for-every  [Part B]
[l for n in nest] for [l in n]

Reduce Final Solution

A: Get l for n in nest
[l for n in nest] => [l, l, l]

B: for every l in n
[l in n]          => [l, l]

[l for n in nest for l in n] => [l, l, l] X [l, l]
# Random Nested List
a = [1, 2, [3, 4], [[5, 6], [7, 8]]]

def flatten(nested):
    return (
        [x for l in nested for x in flatten(l)]
            if isinstance(nested, list) else
        [nested]
    )

flatten(a)
[1, 2, 3, 4, 5, 6, 7, 8]

Generator expressions

g = (x ** 2 for x in range(10))

next(g), next(g), next(g), next(g)
(0, 1, 4, 9)

Dictionary comprehensions

m = {x: x ** 2 for x in range(5)}

m
{0: 0, 1: 1, 2: 4, 3: 9, 4: 16}
m = {x: 'A' + str(x) for x in range(10)}

m
{0: 'A0',
 1: 'A1',
 2: 'A2',
 3: 'A3',
 4: 'A4',
 5: 'A5',
 6: 'A6',
 7: 'A7',
 8: 'A8',
 9: 'A9'}

Inverting a dictionary using a dictionary comprehension

m = {'a': 1, 'b': 2, 'c': 3, 'd': 4}

{v: k for k, v in m.items()}
{1: 'a', 2: 'b', 3: 'c', 4: 'd'}

1.21: namedtuple

from collections import namedtuple

Point = namedtuple('Point', ['x', 'y'])
point = Point(x=1.0, y=2.0)

point.x, point.y
(1.0, 2.0)

Inheriting from namedtuple

PointBaseClass = namedtuple('PointBaseClass', ['x', 'y'])

class Point(PointBaseClass):
    __slots__ = ()
    def __add__(self, other):
        return Point((self.x + other.x), (self.y + other.y))

x = Point(1.0, 2.0)
y = Point(2.0, 3.0)

x + y
Point(x=3.0, y=5.0)

set operations

# Set Constructor
A = set()
B = set()

# Set Literal Notation
A = {1, 2, 3, 4}
B = {3, 4, 5, 6, 7}

# Set Converted from List
A_ = set([1, 2, 3, 4])
B_ = set([3, 4, 5, 6, 7])

A == A_ and B == B_
True
# Union Operator
A | B
{1, 2, 3, 4, 5, 6, 7}
# Intersection Operator
A & B
{3, 4}
# Difference Operator
A - B
{1, 2}
# Symmetric Difference Operator
A ^ B
{1, 2, 5, 6, 7}
# Putting it Together
(A ^ B) == ((A - B) | (B - A))
True

Multisets and multiset operations (collections.Counter)

from collections import Counter

# Construct a Counter from a List
A = Counter([1, 2, 3])
B = Counter([2, 2, 3])
# Union Operator
A | B
Counter({2: 2, 1: 1, 3: 1})
# Intersection Operator
A & B
Counter({2: 1, 3: 1})
# Addition Operator
A + B
Counter({2: 3, 3: 2, 1: 1})
# Subtraction Operator
A - B, B - A
(Counter({1: 1}), Counter({2: 1}))

Simple Dictionary Trees (Autovivification)

(See https://gist.github.com/hrldcpr/2012250 for more on this.)

# Using `__missing__`
class Tree(dict):
    def __missing__(self, key):
        value = self[key] = type(self)()
        return value

root = Tree()

root['menu']['id'] = 'file'
root['menu']['value'] = 'File'
root['menu']['menuitems']['new']['value'] = 'New'
root['menu']['menuitems']['new']['onclick'] = 'new();'
root['menu']['menuitems']['open']['value'] = 'Open'
root['menu']['menuitems']['open']['onclick'] = 'open();'
root['menu']['menuitems']['close']['value'] = 'Close'
root['menu']['menuitems']['close']['onclick'] = 'close();'

print(json.dumps(root, sort_keys=True, indent=2, separators=(',', ': ')))
{
  "menu": {
    "id": "file",
    "menuitems": {
      "close": {
        "onclick": "close();",
        "value": "Close"
      },
      "new": {
        "onclick": "new();",
        "value": "New"
      },
      "open": {
        "onclick": "open();",
        "value": "Open"
      }
    },
    "value": "File"
  }
}

Comments

comments powered by Disqus