Super Intermediate Python 3



Super Intermediate Python 3

1 1


SuperAdvancedPython

PyOhio 2013 Talk on Advanced Python Concepts

On Github kitanata / SuperAdvancedPython

Super Intermediate Python 3

(because super advanced isn't advanced enough)

Presented by Raymond Chandler III / @raymondchandler

Let's talk about Categories!

Study of objects and morphisms

Category: Bag o' Objects

Morphisms

Composition

Composition

Composition

Composition

Identity

Composition (∘): A → B and B → C then A → C

Identity (id ): A → A and B → B and ...

Associative Law: (f∘g)∘h = f∘(g∘h)

Identity Law: f∘id(f) = id(f)∘f = f

Let's talk about Functors

TODO:

Quick Recap

Everything in Python is an object

Categories are groupings of objects and their "mappings" (morphisms)

Everything in Python can be grouped into Categories

Categories can be transformed into other Categories via Functors

Virtually anything in Python can be transformed into something else just using functions!

Set theory tells us we can transform sets into other sets with functions.

Category theory tells us we can transform Objects into other Objects with functors.

Here's the kicker...

Set theory doesn't preserve order or structure

Category theory does!

Functional Programming in Python

List Comprehension

>>> my_list = [1, 2, 3, 4, 5, 6]
>>> print([x * 2 for x in my_list])
[2, 4, 6, 8, 10, 12]

Dict Comprehension

>>> my_dict = {'a': 1, 'b': 2, 'c': 3, 'd': 4, 'e': 5, 'f': 6}
>>> result = {k: v * 2 for k, v in my_dict}
>>> print(result)
{'a': 2, 'b': 4, 'c': 6, 'd': 8, 'e': 10, 'f': 12}

Dict Comprehension (cont.)

>>> print(result.iterkeys())
['a', 'b', 'c', 'd', 'e', 'f']

>>> print(result.itervalues())
[2, 4, 6, 8, 10, 12]

>>> print(result.iteritems())
[('a', 2), ('b', 4), ('c', 6), ('d', 8), ('e', 10), ('f': 12)]

Lambda Expression

>>> my_list = [1, 2, 3, 4, 5, 6]
>>> def my_func(item):
>>>     return item * 2

>>> print([my_func(x) for x in my_list])
[2, 4, 6, 8, 10, 12]

>>> other_func = lambda x: x * 2
>>> print([other_func(x) for x in my_list])
[2, 4, 6, 8, 10, 12]

The "filter" builtin

>>> my_list = [1, 2, 3, 4, 5, 6]
>>> print([x for x in my_list if x % 2 == 0])
[2, 4, 6]

>>> print(filter(lambda x: x % 2 == 0, my_list))
[2, 4, 6]

The "map" builtin

>>> my_list = range(1, 7)
>>> print(my_list)
[1, 2, 3, 4, 5, 6]

>>> print([x * 2 for x in my_list])
[2, 4, 6, 8, 10, 12]

>>> print(map(lambda x: x * 2, my_list))
[2, 4, 6, 8, 10, 12]

The "reduce" builtin

>>> val = 0
>>> for x in range(1, 7):
>>>     val += my_list
>>> print(val)
21

>>> print(reduce(lambda x, y: x + y, range(1, 7)))
21

>>> print(reduce(lambda x, y: x * y, range(1, 7)))
720

The "any" and "all" builtins

>>> my_list = [True, False, False, False]
>>> print(any(my_list))
True

>>> print(all(my_list))
False

>>> my_list = [True, True, True]
>>> print(all(my_list))
True

The "sum" builtin

>>> print(reduce(lambda x, y: x + y, range(1, 7)))
21

>>> print(sum(range(1,7)))
21

Enumerate

>>> my_first_list = ['a', 'b', 'c', 'd', 'e']
>>> my_second_list = [1, 2, 3, 4, 5]
>>> print(zip(my_first_list, my_second_list))
[('a', 1), ('b', 2), ('c', 3), ('d', 4), ('e', 5)]

>>> print(enumerate(my_first_list))
[(0, 'a'), (1, 'b'), (2, 'c'), (3, 'd'), (4, 'e')]

>>> print(enumerate(my_first_list, 3))
[(3, 'a'), (4, 'b'), (5, 'c'), (6, 'd'), (7, 'e')]

Zip

>>> my_first_list = ['a', 'b', 'c', 'd', 'e']
>>> my_second_list = [1, 2, 3, 4, 5]
>>> print(zip(my_first_list, my_second_list))
[('a', 1), ('b', 2), ('c', 3), ('d', 4), ('e', 5)]

>>> my_second_list = ['z', 'y', 'x', 'w', 'v']
>>> print(zip(my_first_list, my_second_list))
[('a', 'z'), ('b', 'y'), ('c', 'x'), ('d', 'w'), ('e', 'v')]

Complex Examples

>>> my_first_list = "abcde"
>>> my_second_list = "zyxwv"
>>> result = zip(my_first_list, my_second_list)
>>> print(result)
[('a', 'z'), ('b', 'y'), ('c', 'x'), ('d', 'w'), ('e', 'v')]

>>> result2 = [''.join(x) for x in result]
>>> print(result2)
['az', 'by', 'cx', 'dw', 'ev']

Complex Examples (cont)

>>> result = zip("abcde", "zyxwv")
>>> print(dict(result))
{'a': 'z', 'c': 'x', 'b': 'y', 'd': 'w'}

>>> print(dict([(k * 3, v) for k, v in result]))
{'aaa': 'z', 'bbb': 'y', 'ccc': 'x', 'ddd': 'w'}

Zip **

chain(*seq)

>>> from itertools import chain
>>> list_a = ['a', 'b', 'c']
>>> list_b = range(1,4)
>>> print(list(chain(list_a, list_b))
['a', 'b', 'c', 1, 2, 3]

compress

>>> from itertools import compress

>>> my_list = 'abcdef'
>>> my_slectors = [1,0,1,1,0,1]
>>> print(list(compress(my_list, my_selectors))
['a', 'c', 'd', 'e', 'f']

>>> print([v for v, s in zip(my_list, my_selectors) if s])
['a', 'c', 'd', 'e', 'f']

count

>>> from itertools import count

>>> counter = count(1, 5)
>>> for l in 'abc':
>>>     print(' '.join(l, str(counter.next())))
a 1
b 6
c 11

cycle

>>> from itertools import cycle

>>> i_want_to_ride_my_bi = cycle('abc123')
>>> print([i_want_to_ride_my_bi.next() for i in range(0,10)])
['a', 'b', 'c', 1, 2, 3, 'a', 'b', 'c']

groupby

>>> from itertools import groupby

>>> my_list='aaaaabbbbcccdd' #sorted
>>> print(list(groupby(my_list)))
[('a', <itertools._grouper object at 0x10fd76410>), 
('b', <itertools._grouper object at 0x10fd76450>), 
...

groupby (cont.)

>>> print({k:list(v) for k, v in groupby(my_list)})
{'a': ['a', 'a', 'a', 'a', 'a'], 
'c': ['c', 'c', 'c'], 
'b': ['b', 'b', 'b', 'b'],
'd': ['d', 'd']}

groupby (cont.)

>>> my_list = [
>>>     {'id': 1, 'name': 'raymond'},
>>>     {'id': 1, 'name': 'bob'},
>>>     {'id': 2, 'name': 'sue'}]

>>> print({k:list(v) for k, v in groupby(my_list, 
>>>         key=lambda x: x['id')})
{1: [{'id': 1, 'name': 'raymond'}, 
{'id': 1, 'name': 'bob'}],
2: [{'id': 2, 'name': 'sue'}]}

groupby (cont.)

>>> my_list = [
>>>     {'id': 1, 'name': 'raymond'},
>>>     {'id': 1, 'email': 'raymond@spkrbar.com'},
>>>     {'id': 2, 'name': 'sue'},
>>>     {'id': 2, 'email': 'sue@sallysue.com'}]

>>> [ dict(
>>>     reduce(lambda y, z: y + z, 
>>>         map(lambda x: x.items(), v)
>>>     )
>>> ) for k, v in groupby(my_list, key=lambda x: x['id']) ]

groupby (cont.)

[{ 'email': 'raymond@spkrbar.com',
'id': 1,
'name': 'raymond'}, 
{ 'email': 'peggy@sue.com',
'id': 2,
'name': 'sue'}]

product

>>> from itertools import product
>>> list_a = 'abc'
>>> list_b = [1, 2]

>>> print(list(product(list_a, list_b)))
[('a', '1'), ('a', '2'), ('b', '1'), ('b', '2'), ('c', '1'), ('c', '2')]

>>> print({x[0]:x[1] for x in product(list_a, list_b)})
{'a': '2', 'c': '2', 'b': '2'}

So why use it?

Object-oriented programming is not mathematically sound

Functional programming is!

Downsides?

It isn't very readable.

It can be difficult to extend and maintain.

Code Organization can suffer.

Please Use Responsibily!

Great for data transformations!

Great for data mining!

Great for mathematically provable results!

Enjoy your new super-powers!

Questions?