Derived from the notebook: http://anandology.com/nb/2014/decorators-demystified/ (Was a workshop in Pycon India '14)
def square(x):
return x*x
print(square(4))
16
print(square)
<function square at 0x7f8fdd684ae8>
f = square
print(f)
<function square at 0x7f8fdd684ae8>
They're called first-class objects
def sum_of_square(x, y):
return square(x) + square(y)
sum_of_square(3,4)
25
def sum_of(f, x, y):
return f(x) + f(y)
sum_of(square, 2, 3)
13
def mod3(x):
return x % 3
sum_of(mod3, 4, 5)
3
sum_of(lambda x: x %3, 4, 5)
3
mod4 = lambda x: x % 4
mod4(17)
1
print(lambda x: x % 4)
<function <lambda> at 0x7f8fdce3a158>
max(["Python", "Haskell"], key=len)
'Haskell'
print(len)
<built-in function len>
len("Python")
print("python" > "haskell")
print(len("python") > len("haskell"))
True False
def incr(x, amount=1):
return x + amount
incr(4)
5
incr(4, 2)
6
def sub(x, y):
return x-y
sub(3, 5)
-2
sub(x=3, y=5)
-2
sub(y=5, x=3)
-2
max(1,2,3,4,5)
5
def f(*args):
print(args)
f(2,3,5)
f(8,1)
(2, 3, 5) (8, 1)
def xprint(label, *args):
print(type(args))
for a in args:
print(label, a)
xprint("Info", 1,2,3)
<class 'tuple'> Info 1 Info 2 Info 3
Problem Implement a function maximum that takes 2 values x and y and a key function as argument and finds the maximum by comparing key(x) and key(y).
>>> maximum(3, -4, abs)
-4
>>> maximum("Python", "Haskell", len)
'Haskell'
>>> maximum("java", "Python", lambda s: s.lower())
'Python'
Problem Write a function strjoin that takes a separator as first argument followed by variable number of strings to join with that separator.
>>> strjoin("-", "a", "b", "c")
"a-b-c"
def maximum(*args, key=lambda x: x):
best = args[0]
for i in args:
if (key(i) > key(best)):
best = i
return best
maximum('aloo', 'python', 'z', 'Haskell', key=len)
'Haskell'
def strjoin(sep, *args):
return sep.join(args)
strjoin("-", "a", "b", "c")
'a-b-c'
# Just like variable args, you can have variable keword args too, just use ** instead of *
def f(**kwargs):
print(kwargs)
f(x=1, y=2, abcd=3)
{'abcd': 3, 'y': 2, 'x': 1}
[int(x) for x in '2 3 4 5'.split()] # List comprehension
[2, 3, 4, 5]
[(k,v) for k, v in {'title': 'IIIT-D', 'href': 'http://iiitd.ac.in'}.items()]
[('href', 'http://iiitd.ac.in'), ('title', 'IIIT-D')]
"Hey %s" % ('jai')
'Hey jai'
Problem Make a function render_tag(tagname, **attrs)
that returns a html tag as a string
>>> render_tag("a", href="http://iiitd.ac.in", title="IIIT-D")
'<a href="http://iiitd.ac.in", title="IIIT-D">'
def render_tag(tagname, **attrs):
pairs = ['%s="%s"' % (k,v) for k, v in attrs.items()]
pairs_str = " ".join(pairs)
return "<%s %s>" % (tagname, pairs_str)
render_tag("a", href="http://iiitd.ac.in", title="IIIT-D")
'<a href="http://iiitd.ac.in" title="IIIT-D">'
# Currying
def make_adder(x):
def add(y):
return x + y
return add
add5 = make_adder(5)
add5(2)
7
add6 = make_adder(6)
add6(7)
13
make_adder(8)(5)
13
%%file trace0.py
def trace(f):
def wrapper(*args):
print(f.__name__, args)
return f(*args)
return wrapper
Overwriting trace0.py
%%file sum.py
from trace0 import trace
@trace
def square(x):
# print("square", x)a
return x*x
#square = trace(square)
@trace
def sum_of_squares(x, y):
# print("sum_of_squares", x, y)
return square(x) + square(y)
#sum_of_squares = trace(sum_of_squares)
print(sum_of_squares(3,4))
Overwriting sum.py
!python sum.py
sum_of_squares (3, 4) square (3,) square (4,) 25
%%file trace1.py
level = 0
def trace(f):
def wrapper(*args):
global level
print("| " * level + "|--", f.__name__, args)
level += 1
result = f(*args)
level -= 1
return result
return wrapper
Overwriting trace1.py
%%file fibo.py
from trace1 import trace
from memoize import memoize
@trace
def fib(n):
if n==0 or n==1:
return 1
else:
return fib(n-1) + fib(n-2)
print(fib(7))
Overwriting fibo.py
!python fibo.py
|-- fib (7,) | |-- fib (6,) | | |-- fib (5,) | | | |-- fib (4,) | | | | |-- fib (3,) | | | | | |-- fib (2,) | | | | | | |-- fib (1,) | | | | | | |-- fib (0,) | | | | | |-- fib (1,) | | | | |-- fib (2,) | | | | | |-- fib (1,) | | | | | |-- fib (0,) | | | |-- fib (3,) | | | | |-- fib (2,) | | | | | |-- fib (1,) | | | | | |-- fib (0,) | | | | |-- fib (1,) | | |-- fib (4,) | | | |-- fib (3,) | | | | |-- fib (2,) | | | | | |-- fib (1,) | | | | | |-- fib (0,) | | | | |-- fib (1,) | | | |-- fib (2,) | | | | |-- fib (1,) | | | | |-- fib (0,) | |-- fib (5,) | | |-- fib (4,) | | | |-- fib (3,) | | | | |-- fib (2,) | | | | | |-- fib (1,) | | | | | |-- fib (0,) | | | | |-- fib (1,) | | | |-- fib (2,) | | | | |-- fib (1,) | | | | |-- fib (0,) | | |-- fib (3,) | | | |-- fib (2,) | | | | |-- fib (1,) | | | | |-- fib (0,) | | | |-- fib (1,) 21
%%file memoize.py
def memoize(f):
cache = {}
def wrapper(*args):
if args not in cache:
cache[args] = f(*args)
return cache[args]
return wrapper
Overwriting memoize.py
%%file fibo1.py
from trace1 import trace
from memoize import memoize
@memoize
@trace
def fib(n):
if n==0 or n==1:
return 1
else:
return fib(n-1) + fib(n-2)
print(fib(7))
Overwriting fibo1.py
!python fibo1.py
|-- fib (7,) | |-- fib (6,) | | |-- fib (5,) | | | |-- fib (4,) | | | | |-- fib (3,) | | | | | |-- fib (2,) | | | | | | |-- fib (1,) | | | | | | |-- fib (0,) 21
Problem Write a function with_retries that continue to retry for 5 times if there is any exception raised in the function.
@with_retries
def wget(url):
return urllib2.urlopen(url).read()
wget("http://google.com/no-such-page")
Should print:
Failed to download, retrying...
Failed to download, retrying...
Failed to download, retrying...
Failed to download, retrying...
Failed to download, retrying...
Giving up!
%%file with_retries.py
import urllib.request
def with_retries(f, num):
def wrapper(*args):
for i in range(num):
try:
return f(*args)
except:
print("Failed")
print("Given up!")
return wrapper
#@with_retries
def wget(url):
return urllib.request.urlopen(url)
wget = with_retries(wget, 7)
x = wget("http://google.com/pageisnonexistent")
Overwriting with_retries.py
!python with_retries.py
Failed Failed Failed Failed Failed Failed Failed Given up!
%%file with_retries_adv.py
def with_retries(num):
def decor(f):
def wrapper(*args):
for i in range(num):
try:
return f(*args)
except:
print("failed")
print("given up!")
return wrapper
return decor
@with_retries(3)
def wget(url):
return urllib.request.urlopen(url)
# wget = with_retries(3)(wget)
x = wget("http://google.com/pageisnonexistent")
Overwriting with_retries_adv.py
!python with_retries_adv.py
failed failed failed given up!
%%file jug.py
mapping = []
def route(path):
def decor(f):
mapping.append((path, f))
return decor
def request(path):
for p, func in mapping:
if p == path:
return func()
return "404"
def wsgifunc(env, start_response):
path = env['PATH_INFO']
start_response('200 OK', [("Content-Type", "text/plain")])
return [request(path).encode('utf-8')]
def run(port=8080):
from wsgiref.simple_server import make_server
server = make_server("0.0.0.0", port, wsgifunc)
server.serve_forever()
Overwriting jug.py
%%file hello.py
from jug import route
@route('/hello')
def hello():
return "Hello! :D"
@route('/bye')
def bye():
return "Good bye!"
Overwriting hello.py
%%file client.py
import hello
from jug import request, run
import sys
if "--web" in sys.argv:
run()
else:
print(request("/hello"))
print(request("/bye"))
Overwriting client.py
!python client.py
Hello! :D Good bye!
!python client.py --web
127.0.0.1 - - [28/Oct/2015 21:49:49] "GET /hello HTTP/1.1" 200 9 ^CTraceback (most recent call last): File "client.py", line 6, in <module> run() File "/home/darkapex/decorators/jug.py", line 23, in run server.serve_forever() File "/usr/lib/python3.5/socketserver.py", line 237, in serve_forever ready = selector.select(poll_interval) File "/usr/lib/python3.5/selectors.py", line 367, in select fd_event_list = self._poll.poll(timeout) KeyboardInterrupt