Functional Programming

Variable arguments and Keyword arguments

In [3]:
max(1, 2, 3, 4)
Out[3]:
4
In [4]:
dict(a=1, b=2)
Out[4]:
{'a': 1, 'b': 2}
In [5]:
def add(*args): 
    return sum(args)

print add(1, 2, 3, 4)
10
In [6]:
def mydict(**kwargs):
    return kwargs

print mydict(a=1, b=2)
{'a': 1, 'b': 2}
In [10]:
def render_tag(tagname, **attrs):
    pairs = ["%s=%s" % (k,v) for k, v in attrs.items()]
    return "<%s %s/>" % (tagname, " ".join(pairs))

render_tag("input", type="text", name="x", id="x")
Out[10]:
'<input type=text name=x id=x/>'
In [11]:
numbers = [1, 2, 3, 4]
print add(*numbers)
10

Problem: Write a function call_func(f, *args, **kwargs), that calls the given function f with args and kwargs.

def square(x): 
    return x*x
print call_func(square, 2)
print call_func(square, x=2)

def add(a, b): 
    return a + b
print call_func(add, 2, 3)

print call_func(max, 2, 3, 4, 5, 6, 7)
print call_func(dict, a=1, b=2, c=3)
In [14]:
def add(a, b): return a+b
args=[1]
kwargs={"b": 2}

print add(*args, **kwargs)
3
In [15]:
def call_fun(f, *args, **kwargs):
    return f(*args, **kwargs)

Back to Decorators

In [21]:
import time
def timeit(f):
    def g():
        t0 = time.time()
        value = f()
        t1 = time.time()
        print "took %f seconds" % (t1-t0)
        return value
    return g

def timepass():
    for i in range(10000):
        for j in range(1000):
            x = i*j
    return 0

def timepass2(n):
    for i in range(n):
        for j in range(1000):
            x = i*j
    return 0


timepass = timeit(timepass)
print timepass()

import urllib
@timeit
def wget(url):
    return urllib.urlopen(url).read()

x = wget("http://python.org")
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-21-868cc2b3e2d9> in <module>()
     30     return urllib.urlopen(url).read()
     31 
---> 32 x = wget("http://python.org")

TypeError: g() takes no arguments (1 given)
took 0.772692 seconds
0

Problem: Write a decorator vectorize, that takes a function working on single value and make it work on a list.

@vectorize
def square(x):
    return x*x

print square([1, 2, 3, 4]) # should print [1, 4, 9, 16]

Example: Web Framework

In [6]:
%%file bicycle.py
mapping = []
before_hooks = []

def request(url):
    for path, func in mapping:
        if path == url:
            for h in before_hooks:
                h(url)
            return func()
        
    return "404 - Not Found"
    
def route(path):
    def decorator(f):
        mapping.append((path, f))
        return f
    return decorator

def before_request(f):
    before_hooks.append(f)
    return f
Overwriting bicycle.py
In [8]:
from bicycle import route, request, before_request
import time

@route("/hello")
def hello():
    return "Hello, world"

@route("/bye")
def bye():
    return "Goodbye"

@before_request
def log(url):
    print time.asctime(), url

if __name__ == "__main__":
    print "MAIN"
    print request("/hello") # should print "Hello, world"
    print request("/bye")
    print request("/foo")
MAIN
Sat May 25 10:53:05 2013 /hello
Hello, world
Sat May 25 10:53:05 2013 /bye
Goodbye
404 - Not Found

Example: writing command line apps using decorators

In [44]:
%%file command0.py
def command(f):
    def g(filenames, **kw):
        lines = readfiles(filenames)
        #lines = (outline for line in lines 
        #                 for outline in f(line, **kw))
        lines = generate_output(f, lines, **kwargs)
        printlines(lines)
    return g

def generate_output(f, lines, **kwargs):
    for line in lines:
        for outline in f(line, **kwargs):
            yield outline

def readfiles(filenames):
    for f in filenames:
        for line in open(f):
            yield line

def printlines(lines):
    for line in lines:
        print line.strip("\n")
Overwriting command0.py
In [36]:
%%file uppercase.py
from command0 import command

@command
def uppercase(line):
    yield line.upper()

if __name__ == "__main__":
    import sys
    uppercase(sys.argv[1:])
Overwriting uppercase.py
In [37]:
!python uppercase.py uppercase.py
FROM COMMAND0 IMPORT COMMAND

@COMMAND
DEF UPPERCASE(LINE):
    YIELD LINE.UPPER()

IF __NAME__ == "__MAIN__":
    IMPORT SYS
    UPPERCASE(SYS.ARGV[1:])
In [38]:
%%file grep.py
from command0 import command

@command
def grep(line, pattern):
    if pattern in line:
        yield line
        
if __name__ == "__main__":
    import sys
    pattern = sys.argv[1]
    filenames = sys.argv[2:]
    grep(filenames, pattern=pattern)
Overwriting grep.py
In [39]:
!python grep.py def grep.py
def grep(line, pattern):
In [42]:
%%file twice.py
from command0 import command

@command
def twice(line):
    yield line
    yield line
    
if __name__ == "__main__":
    import sys
    twice(sys.argv[1:])
Overwriting twice.py
In [43]:
!python twice.py twice.py
from command0 import command
from command0 import command


@command
@command
def twice(line):
def twice(line):
    yield line
    yield line
    yield line
    yield line
    
    
if __name__ == "__main__":
if __name__ == "__main__":
    import sys
    import sys
    twice(sys.argv[1:])
    twice(sys.argv[1:])

exec and eval

In [45]:
code = "x = 1"
exec(code)
print x
1
In [46]:
eval("1 + 2")
Out[46]:
3
In [47]:
env = {"x": 2}
eval("x + 2", env)
Out[47]:
4
In [ ]: