#!/usr/bin/env python # coding: utf-8 # ## Decorators and Functional Programming in Python # # Derived from the notebook: http://anandology.com/nb/2014/decorators-demystified/ (Was a workshop in Pycon India '14) # In[1]: def square(x): return x*x print(square(4)) # In[2]: print(square) # In[3]: f = square # In[4]: print(f) # They're called first-class objects # In[5]: def sum_of_square(x, y): return square(x) + square(y) # In[6]: sum_of_square(3,4) # In[7]: def sum_of(f, x, y): return f(x) + f(y) sum_of(square, 2, 3) # In[8]: def mod3(x): return x % 3 sum_of(mod3, 4, 5) # In[9]: sum_of(lambda x: x %3, 4, 5) # In[10]: mod4 = lambda x: x % 4 # In[11]: mod4(17) # In[12]: print(lambda x: x % 4) # In[13]: max(["Python", "Haskell"], key=len) # In[14]: print(len) # In[15]: len("Python") print("python" > "haskell") print(len("python") > len("haskell")) # In[16]: def incr(x, amount=1): return x + amount # In[17]: incr(4) # In[18]: incr(4, 2) # In[19]: def sub(x, y): return x-y # In[20]: sub(3, 5) # In[21]: sub(x=3, y=5) # In[22]: sub(y=5, x=3) # In[23]: max(1,2,3,4,5) # In[24]: def f(*args): print(args) f(2,3,5) f(8,1) # In[25]: def xprint(label, *args): print(type(args)) for a in args: print(label, a) # In[26]: xprint("Info", 1,2,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" # ``` # # In[27]: def maximum(*args, key=lambda x: x): best = args[0] for i in args: if (key(i) > key(best)): best = i return best # In[28]: maximum('aloo', 'python', 'z', 'Haskell', key=len) # In[29]: def strjoin(sep, *args): return sep.join(args) strjoin("-", "a", "b", "c") # In[30]: # Just like variable args, you can have variable keword args too, just use ** instead of * def f(**kwargs): print(kwargs) # In[31]: f(x=1, y=2, abcd=3) # In[32]: [int(x) for x in '2 3 4 5'.split()] # List comprehension # In[33]: [(k,v) for k, v in {'title': 'IIIT-D', 'href': 'http://iiitd.ac.in'}.items()] # In[34]: "Hey %s" % ('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") # '' # In[35]: 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) # In[36]: render_tag("a", href="http://iiitd.ac.in", title="IIIT-D") # In[37]: # Currying def make_adder(x): def add(y): return x + y return add # In[38]: add5 = make_adder(5) add5(2) # In[39]: add6 = make_adder(6) add6(7) # In[40]: make_adder(8)(5) # ## Decorators # In[41]: get_ipython().run_cell_magic('file', 'trace0.py', '\ndef trace(f):\n def wrapper(*args):\n print(f.__name__, args)\n return f(*args)\n return wrapper\n') # In[42]: get_ipython().run_cell_magic('file', 'sum.py', '\nfrom trace0 import trace\n\n@trace\ndef square(x):\n # print("square", x)a\n return x*x\n\n#square = trace(square)\n\n@trace\ndef sum_of_squares(x, y):\n # print("sum_of_squares", x, y)\n return square(x) + square(y)\n\n#sum_of_squares = trace(sum_of_squares)\n\nprint(sum_of_squares(3,4))\n') # In[43]: get_ipython().system('python sum.py') # In[44]: get_ipython().run_cell_magic('file', 'trace1.py', '\nlevel = 0\n\ndef trace(f):\n def wrapper(*args):\n global level\n print("| " * level + "|--", f.__name__, args)\n \n level += 1\n result = f(*args)\n level -= 1\n \n return result\n return wrapper\n') # In[45]: get_ipython().run_cell_magic('file', 'fibo.py', '\nfrom trace1 import trace\nfrom memoize import memoize\n\n@trace\ndef fib(n):\n if n==0 or n==1:\n return 1\n else:\n return fib(n-1) + fib(n-2)\n \nprint(fib(7))\n') # In[46]: get_ipython().system('python fibo.py') # In[47]: get_ipython().run_cell_magic('file', 'memoize.py', '\ndef memoize(f):\n \n cache = {}\n \n def wrapper(*args):\n if args not in cache:\n cache[args] = f(*args)\n return cache[args]\n return wrapper\n') # In[48]: get_ipython().run_cell_magic('file', 'fibo1.py', '\nfrom trace1 import trace\nfrom memoize import memoize\n\n@memoize\n@trace\ndef fib(n):\n if n==0 or n==1:\n return 1\n else:\n return fib(n-1) + fib(n-2)\n \nprint(fib(7))\n') # In[49]: get_ipython().system('python fibo1.py') # **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! # ``` # In[50]: get_ipython().run_cell_magic('file', 'with_retries.py', '\nimport urllib.request\n\ndef with_retries(f, num):\n def wrapper(*args):\n for i in range(num):\n try:\n return f(*args)\n except:\n print("Failed")\n print("Given up!")\n return wrapper\n\n#@with_retries\ndef wget(url):\n return urllib.request.urlopen(url)\n\nwget = with_retries(wget, 7)\nx = wget("http://google.com/pageisnonexistent")\n') # In[51]: get_ipython().system('python with_retries.py') # In[52]: get_ipython().run_cell_magic('file', 'with_retries_adv.py', '\ndef with_retries(num):\n def decor(f):\n def wrapper(*args):\n for i in range(num):\n try:\n return f(*args)\n except:\n print("failed")\n print("given up!")\n return wrapper\n return decor\n\n@with_retries(3)\ndef wget(url):\n return urllib.request.urlopen(url)\n\n# wget = with_retries(3)(wget)\n\nx = wget("http://google.com/pageisnonexistent")\n') # In[53]: get_ipython().system('python with_retries_adv.py') # ## Web framework - JUG (nano flask) # In[54]: get_ipython().run_cell_magic('file', 'jug.py', '\nmapping = []\n\ndef route(path):\n def decor(f):\n mapping.append((path, f))\n return decor\n\ndef request(path):\n for p, func in mapping:\n if p == path:\n return func()\n return "404"\n\ndef wsgifunc(env, start_response):\n path = env[\'PATH_INFO\']\n start_response(\'200 OK\', [("Content-Type", "text/plain")])\n return [request(path).encode(\'utf-8\')]\n\ndef run(port=8080):\n from wsgiref.simple_server import make_server\n server = make_server("0.0.0.0", port, wsgifunc)\n server.serve_forever()\n') # In[55]: get_ipython().run_cell_magic('file', 'hello.py', '\nfrom jug import route\n\n@route(\'/hello\')\ndef hello():\n return "Hello! :D"\n\n@route(\'/bye\')\ndef bye():\n return "Good bye!"\n') # In[56]: get_ipython().run_cell_magic('file', 'client.py', 'import hello\nfrom jug import request, run\nimport sys\n\nif "--web" in sys.argv:\n run()\nelse:\n print(request("/hello"))\n print(request("/bye"))\n') # In[57]: get_ipython().system('python client.py') # In[59]: get_ipython().system('python client.py --web') # In[ ]: