Advanced Python Training at VMWare - Day 1

Apr 02-04, 2018 Vikrant Patil

These notes are available online at http://notes.pipal.in/2018/vmware-advanced-apr

© Pipal Academy LLP

Day 1 | Day 2 | Day 3

Python distribution

We will be using anaconda (python 3) distribution for this training. it can be downloaded from

https://www.anaconda.com/download/#linux

Markdown cell

### Header ###
## Header ##

Recap

lists

In [1]:
l = []
In [3]:
numbers =  [1,2,3,4]
In [5]:
digits  = list(range(0,9,1)) # range(start, end, step)
In [6]:
digits
Out[6]:
[0, 1, 2, 3, 4, 5, 6, 7, 8]
In [7]:
list(range(0,20,2))
Out[7]:
[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]
In [8]:
digits[0]
Out[8]:
0
In [9]:
digits[-1]
Out[9]:
8
In [10]:
digits[1:] # start at 1 end at end of list... drop one
Out[10]:
[1, 2, 3, 4, 5, 6, 7, 8]
In [12]:
digits[:2] # start at 0th index and end at 2nd (exclude) ... take first 2
Out[12]:
[0, 1]
In [13]:
digits[2:5:1] # just like range..start:end:step
Out[13]:
[2, 3, 4]
In [14]:
digits[:]
Out[14]:
[0, 1, 2, 3, 4, 5, 6, 7, 8]
In [15]:
digits[:-1]
Out[15]:
[0, 1, 2, 3, 4, 5, 6, 7]
In [16]:
digits[:3]
Out[16]:
[0, 1, 2]
In [17]:
digits[::-1]
Out[17]:
[8, 7, 6, 5, 4, 3, 2, 1, 0]
In [18]:
digits.pop()
Out[18]:
8
In [19]:
digits
Out[19]:
[0, 1, 2, 3, 4, 5, 6, 7]
In [20]:
digits.insert(0, 2)
In [21]:
digits
Out[21]:
[2, 0, 1, 2, 3, 4, 5, 6, 7]
In [22]:
digits.pop(0)
Out[22]:
2

strings

In [23]:
book = "Alice in Wonderland"
In [24]:
book[0]
Out[24]:
'A'
In [25]:
book[:2]
Out[25]:
'Al'
In [26]:
book.split()
Out[26]:
['Alice', 'in', 'Wonderland']
In [27]:
book.split(maxsplit=1)
Out[27]:
['Alice', 'in Wonderland']
In [28]:
book.upper()
Out[28]:
'ALICE IN WONDERLAND'
In [29]:
book.lower()
Out[29]:
'alice in wonderland'
In [31]:
book.center(50)
Out[31]:
'               Alice in Wonderland                '
In [32]:
book.ljust(50)
Out[32]:
'Alice in Wonderland                               '
In [33]:
book.rjust(50)
Out[33]:
'                               Alice in Wonderland'

dictionaries

In [34]:
person = {"name":"Alice","email":"alice@example.com"}
In [35]:
person['name']
Out[35]:
'Alice'
In [36]:
person.get("country", "UK")
Out[36]:
'UK'
In [37]:
person
Out[37]:
{'email': 'alice@example.com', 'name': 'Alice'}
In [43]:
boolean = {True:"True", False:"False"}
In [39]:
data = {(1,2):[1,2], (2,3):[2,3]}
In [40]:
data
Out[40]:
{(1, 2): [1, 2], (2, 3): [2, 3]}
In [41]:
True
Out[41]:
True

boolean

List comprehensions

In [45]:
numbers = range(10)
In [46]:
[n*n for n in numbers]
Out[46]:
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
In [47]:
[n*n for n in numbers if n%2==0]
Out[47]:
[0, 4, 16, 36, 64]
In [50]:
t = [[i*j for i in range(1,11)] for j in range(1,6)]
In [53]:
t
Out[53]:
[[1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
 [2, 4, 6, 8, 10, 12, 14, 16, 18, 20],
 [3, 6, 9, 12, 15, 18, 21, 24, 27, 30],
 [4, 8, 12, 16, 20, 24, 28, 32, 36, 40],
 [5, 10, 15, 20, 25, 30, 35, 40, 45, 50]]
In [54]:
[str(item) for item in t[0]]
Out[54]:
['1', '2', '3', '4', '5', '6', '7', '8', '9', '10']

Files

In [52]:
f = open("data.csv","w")
In [56]:
for row in t:
    f.write(",".join([str(i) for i in row]))
    f.write("\n")
f.close()
In [57]:
!cat data.csv
1,2,3,4,5,6,7,8,9,10
2,4,6,8,10,12,14,16,18,20
3,6,9,12,15,18,21,24,27,30
4,8,12,16,20,24,28,32,36,40
5,10,15,20,25,30,35,40,45,50
In [59]:
def fun(filename):
    pass
In [60]:
%%file cat.py

import sys

if __name__ == "__main__":
    print(open(sys.argv[1]).read())
Writing cat.py
In [61]:
!python cat.py data.csv
1,2,3,4,5,6,7,8,9,10
2,4,6,8,10,12,14,16,18,20
3,6,9,12,15,18,21,24,27,30
4,8,12,16,20,24,28,32,36,40
5,10,15,20,25,30,35,40,45,50

In [62]:
def csvparse(filename):
    t = []
    with open(filename) as f:
        for line in f:
            tokens = line.strip().split(",")
            items = [int(t) for t in tokens]
            t.append(items)
    return t
In [63]:
csvparse("data.csv")
Out[63]:
[[1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
 [2, 4, 6, 8, 10, 12, 14, 16, 18, 20],
 [3, 6, 9, 12, 15, 18, 21, 24, 27, 30],
 [4, 8, 12, 16, 20, 24, 28, 32, 36, 40],
 [5, 10, 15, 20, 25, 30, 35, 40, 45, 50]]
In [64]:
def csvparse_(filename):
    return [[int(i) for i in line.strip().split(",")] for line in open(filename)]
In [65]:
csvparse_("data.csv")
Out[65]:
[[1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
 [2, 4, 6, 8, 10, 12, 14, 16, 18, 20],
 [3, 6, 9, 12, 15, 18, 21, 24, 27, 30],
 [4, 8, 12, 16, 20, 24, 28, 32, 36, 40],
 [5, 10, 15, 20, 25, 30, 35, 40, 45, 50]]

problem

  • Parse following file and create a dictionary which contains all the configurations metioned in the file
# If you change this file, run 'update-grub' afterwards to update
# /boot/grub/grub.cfg.
# For full documentation of the options in this file, see:
#   info -f grub -n 'Simple configuration'

GRUB_DEFAULT=0
GRUB_HIDDEN_TIMEOUT=0
GRUB_HIDDEN_TIMEOUT_QUIET=true
GRUB_TIMEOUT=10
GRUB_DISTRIBUTOR=`lsb_release -i -s 2> /dev/null || echo Debian`
GRUB_CMDLINE_LINUX_DEFAULT="quiet splash"
GRUB_CMDLINE_LINUX=""

# Uncomment to enable BadRAM filtering, modify to suit your needs
# This works with Linux (no patch required) and with any kernel that obtains
# the memory map information from GRUB (GNU Mach, kernel of FreeBSD ...)
#GRUB_BADRAM="0x01234567,0xfefefefe,0x89abcdef,0xefefefef"

# Uncomment to disable graphical terminal (grub-pc only)
#GRUB_TERMINAL=console

# The resolution used on graphical terminal
# note that you can use only modes which your graphic card supports via VBE
# you can see them in real GRUB with the command `vbeinfo'
#GRUB_GFXMODE=640x480

# Uncomment if you don't want GRUB to pass "root=UUID=xxx" parameter to Linux
#GRUB_DISABLE_LINUX_UUID=true

# Uncomment to disable generation of recovery mode menu entries
#GRUB_DISABLE_RECOVERY="true"

# Uncomment to get a beep at grub start
#GRUB_INIT_TUNE="480 440 1"
In [66]:
%%file grub.conf
# If you change this file, run 'update-grub' afterwards to update
# /boot/grub/grub.cfg.
# For full documentation of the options in this file, see:
#   info -f grub -n 'Simple configuration'

GRUB_DEFAULT=0
GRUB_HIDDEN_TIMEOUT=0
GRUB_HIDDEN_TIMEOUT_QUIET=true
GRUB_TIMEOUT=10
GRUB_DISTRIBUTOR=`lsb_release -i -s 2> /dev/null || echo Debian`
GRUB_CMDLINE_LINUX_DEFAULT="quiet splash"
GRUB_CMDLINE_LINUX=""

# Uncomment to enable BadRAM filtering, modify to suit your needs
# This works with Linux (no patch required) and with any kernel that obtains
# the memory map information from GRUB (GNU Mach, kernel of FreeBSD ...)
#GRUB_BADRAM="0x01234567,0xfefefefe,0x89abcdef,0xefefefef"

# Uncomment to disable graphical terminal (grub-pc only)
#GRUB_TERMINAL=console

# The resolution used on graphical terminal
# note that you can use only modes which your graphic card supports via VBE
# you can see them in real GRUB with the command `vbeinfo'
#GRUB_GFXMODE=640x480

# Uncomment if you don't want GRUB to pass "root=UUID=xxx" parameter to Linux
#GRUB_DISABLE_LINUX_UUID=true

# Uncomment to disable generation of recovery mode menu entries
#GRUB_DISABLE_RECOVERY="true"

# Uncomment to get a beep at grub start
#GRUB_INIT_TUNE="480 440 1"
Writing grub.conf
In [67]:
list(range(5))
Out[67]:
[0, 1, 2, 3, 4]
In [68]:
tuple([2,3])
Out[68]:
(2, 3)
In [69]:
dict([(1,2),(3,4)])
Out[69]:
{1: 2, 3: 4}
In [72]:
def grubconfparser(filename):
    data = {}
    for line in open(filename):
        if not line.startswith("#") and line.strip():
            k,v = line.strip().split("=", maxsplit=1)
            data[k] = v
    return data
In [73]:
grubconfparser("grub.conf")
Out[73]:
{'GRUB_CMDLINE_LINUX': '""',
 'GRUB_CMDLINE_LINUX_DEFAULT': '"quiet splash"',
 'GRUB_DEFAULT': '0',
 'GRUB_DISTRIBUTOR': '`lsb_release -i -s 2> /dev/null || echo Debian`',
 'GRUB_HIDDEN_TIMEOUT': '0',
 'GRUB_HIDDEN_TIMEOUT_QUIET': 'true',
 'GRUB_TIMEOUT': '10'}
In [75]:
def grubconfparser_(filename):
    
    return dict([line.strip().split("=", maxsplit=1) for line in open(filename) 
                 if not line.startswith("#") and line.strip()])
       
In [76]:
grubconfparser_("grub.conf")
Out[76]:
{'GRUB_CMDLINE_LINUX': '""',
 'GRUB_CMDLINE_LINUX_DEFAULT': '"quiet splash"',
 'GRUB_DEFAULT': '0',
 'GRUB_DISTRIBUTOR': '`lsb_release -i -s 2> /dev/null || echo Debian`',
 'GRUB_HIDDEN_TIMEOUT': '0',
 'GRUB_HIDDEN_TIMEOUT_QUIET': 'true',
 'GRUB_TIMEOUT': '10'}
In [77]:
zip(["one","two","three"],[1,2,3])
Out[77]:
<zip at 0x7f20bcad80c8>
In [78]:
for item in zip(["one","two","three"],[1,2,3]):
    print(item)
('one', 1)
('two', 2)
('three', 3)
In [79]:
dict(zip(["one","two","three"],[1,2,3]))
Out[79]:
{'one': 1, 'three': 3, 'two': 2}

Functions

Positional Arguments

In [80]:
import math as m
def cyl_volume(radius, height):
    return m.pi*radius**2*height
In [81]:
cyl_volume(2, 1)
Out[81]:
12.566370614359172
In [82]:
cyl_volume(1,2)
Out[82]:
6.283185307179586
In [83]:
cyl_volume(radius=1, height=2)
Out[83]:
6.283185307179586

Default arguments

In [84]:
import math as m
def cyl_volume(radius=1.0, height=1.0):
    return m.pi*radius**2*height
In [85]:
cyl_volume()
Out[85]:
3.141592653589793
In [86]:
cyl_volume(radius=2)
Out[86]:
12.566370614359172
In [87]:
cyl_volume(height=2)
Out[87]:
6.283185307179586
In [88]:
cyl_volume(1.5, height=2)
Out[88]:
14.137166941154069
In [89]:
cyl_volume(height=2, 1.5)
  File "<ipython-input-89-96d9e51e76b6>", line 1
    cyl_volume(height=2, 1.5)
                        ^
SyntaxError: positional argument follows keyword argument

Only Named arguments

In [90]:
import os
def binlocation(base="/home/vikrant",*, package="jupyter"):
    return os.path.sep.join([base, "usr","local",package])
In [91]:
binlocation()
Out[91]:
'/home/vikrant/usr/local/jupyter'
In [92]:
binlocation(base="/home/root",package="virtulenv")
Out[92]:
'/home/root/usr/local/virtulenv'
In [93]:
binlocation("/home/root",package="virtulenv")
Out[93]:
'/home/root/usr/local/virtulenv'
In [94]:
binlocation("/home/root","virtulenv")
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-94-196a21ae29de> in <module>()
----> 1 binlocation("/home/root","virtulenv")

TypeError: binlocation() takes from 0 to 1 positional arguments but 2 were given
In [95]:
max([1,2,3,4,5])
Out[95]:
5

variable number of arguments

In [96]:
def func(*args):
    for arg in args:
        print(arg)
In [97]:
func(1,2,3)
1
2
3
In [98]:
func(1)
1
In [99]:
func(2,3,4,5,67,5)
2
3
4
5
67
5

problem

  • Write a function mysum which returns sum of all the arguments passed
In [100]:
def mysum(*args):
    s = 0
    for i in args:
        s += i
    return s
In [101]:
mysum(1,2,3,4,5)
Out[101]:
15
In [102]:
"one" + "two"
Out[102]:
'onetwo'
In [103]:
mysum("one","two","three")
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-103-40c7bc1a19e9> in <module>()
----> 1 mysum("one","two","three")

<ipython-input-100-4f54083a7e17> in mysum(*args)
      2     s = 0
      3     for i in args:
----> 4         s += i
      5     return s

TypeError: unsupported operand type(s) for +=: 'int' and 'str'
In [105]:
def mysum(*args, start=0):
    s = start
    for i in args:
        s += i
    return s
In [106]:
mysum("one","two","three",start="")
Out[106]:
'onetwothree'
In [107]:
mysum(1,2,3,4,5)
Out[107]:
15
In [108]:
mysum(start="", "one","tweo","ada")
  File "<ipython-input-108-7dd66fd8f02e>", line 1
    mysum(start="", "one","tweo","ada")
                   ^
SyntaxError: positional argument follows keyword argument

variable number of named arguments

In [120]:
def make_person(**properties):
    person = {}
    print(properties)
    for k,v in properties.items():
        person[k] = v
    return person
In [121]:
make_person(name="Alice",email="alice@wonder.land")
{'name': 'Alice', 'email': 'alice@wonder.land'}
Out[121]:
{'email': 'alice@wonder.land', 'name': 'Alice'}
In [122]:
person
Out[122]:
{'email': 'alice@example.com', 'name': 'Alice'}
In [123]:
for item in person:
    print(item)
name
email
In [124]:
for k,v in person.items():
    print(k,v)
name Alice
email alice@example.com
In [126]:
def mixed(*args, **kwargs):
    print(args)
    print(kwargs)
In [127]:
mixed(1,2,3,name="x",last="y")
(1, 2, 3)
{'name': 'x', 'last': 'y'}
In [128]:
mixed(1,name="hello")
(1,)
{'name': 'hello'}
In [129]:
x,y = [1,2]
In [130]:
x
Out[130]:
1
In [131]:
y
Out[131]:
2
In [135]:
def mysum(*args, start=0):
    print(args)
    s = start
    for i in args:
        s += i
    return s

def make_plot_data(*data, **annotaions):
    """
    takes numeric data as variable number of positional arguments
    also takes  annotations as variable number of named arguments
    it computes sum of data...
    """
    print(type(data))
    ploting_data = {}
    plotig_data["sum"] = mysum(data)
    return ploting_data
    
In [136]:
make_plot_data(1,2,3,4,5,name="x",exp="y",abc="123")
<class 'tuple'>
((1, 2, 3, 4, 5),)
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-136-10786e95b4f7> in <module>()
----> 1 make_plot_data(1,2,3,4,5,name="x",exp="y",abc="123")

<ipython-input-135-f89371ec9511> in make_plot_data(*data, **annotaions)
     14     print(type(data))
     15     ploting_data = {}
---> 16     plotig_data["sum"] = mysum(data)
     17     return ploting_data
     18 

<ipython-input-135-f89371ec9511> in mysum(start, *args)
      3     s = start
      4     for i in args:
----> 5         s += i
      6     return s
      7 

TypeError: unsupported operand type(s) for +=: 'int' and 'tuple'
In [139]:
def mysum(*args, start=0):
    print(args)
    s = start
    for i in args:
        s += i
    return s

def make_plot_data(*data, **annotaions):
    """
    takes numeric data as variable number of positional arguments
    also takes  annotations as variable number of named arguments
    it computes sum of data...
    """
    print(type(data))
    ploting_data = {}
    ploting_data["sum"] = mysum(*data)
    return ploting_data
    
In [140]:
make_plot_data(1,2,3,4,5,name="x",exp="y",abc="123")
<class 'tuple'>
(1, 2, 3, 4, 5)
Out[140]:
{'sum': 15}
In [141]:
numbers
Out[141]:
range(0, 10)
In [142]:
numbers = list(numbers)
In [143]:
numbers
Out[143]:
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
In [144]:
mysum(numbers)
([0, 1, 2, 3, 4, 5, 6, 7, 8, 9],)
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-144-99598c72445d> in <module>()
----> 1 mysum(numbers)

<ipython-input-139-6a92ed1db865> in mysum(start, *args)
      3     s = start
      4     for i in args:
----> 5         s += i
      6     return s
      7 

TypeError: unsupported operand type(s) for +=: 'int' and 'list'
In [145]:
mysum(*numbers)
(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)
Out[145]:
45
In [146]:
def add(x,y):
    return x+y
In [147]:
add(2,3)
Out[147]:
5
In [148]:
add(*[2,3])
Out[148]:
5
In [149]:
mysum(*{"start":0, "a":1,"b":2})
('start', 'a', 'b')
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-149-c1f879169a26> in <module>()
----> 1 mysum(*{"start":0, "a":1,"b":2})

<ipython-input-139-6a92ed1db865> in mysum(start, *args)
      3     s = start
      4     for i in args:
----> 5         s += i
      6     return s
      7 

TypeError: unsupported operand type(s) for +=: 'int' and 'str'
In [151]:
mysum(**{"start":0})
()
Out[151]:
0
In [152]:
mysum("start","a","b")
('start', 'a', 'b')
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-152-9cd98157641e> in <module>()
----> 1 mysum("start","a","b")

<ipython-input-139-6a92ed1db865> in mysum(start, *args)
      3     s = start
      4     for i in args:
----> 5         s += i
      6     return s
      7 

TypeError: unsupported operand type(s) for +=: 'int' and 'str'
In [153]:
mysum(*{1:"one",2:"two",3:"three"})
(1, 2, 3)
Out[153]:
6
In [154]:
mysum(**{1:"one",2:"two",3:"three"})
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-154-b2fbf77371cd> in <module>()
----> 1 mysum(**{1:"one",2:"two",3:"three"})

TypeError: mysum() keywords must be strings

Functions as arguments

In [155]:
def fun_():
    print("Fun!")
In [156]:
fun_
Out[156]:
<function __main__.fun_>
In [157]:
x = 2
In [158]:
y = x
In [159]:
fun_
Out[159]:
<function __main__.fun_>
In [160]:
x
Out[160]:
2
In [161]:
type(fun_)
Out[161]:
function
In [162]:
aliasfun = fun_
In [163]:
fun_()
Fun!
In [164]:
aliasfun()
Fun!
In [165]:
def square(x):
    return x*x

def sumofsquares(start,end):
    s = 0
    for i in range(start, end):
        s += square(i)
    return s

def cube(x):
    return x*x*x

def sumofcubes(start, end):
    s = 0
    for i in range(start, end):
        s += cube(i)
    return s
In [166]:
def sumof(f, start, end):
    s = 0
    for i in range(start, end):
        s += f(i)
    return s
In [167]:
sumofsquares(1,20)
Out[167]:
2470
In [168]:
sumof(square, 1, 20)
Out[168]:
2470
In [169]:
def f(x,y):
    return x*y
In [170]:
f = lambda x,y: x*y
In [171]:
f(2,3)
Out[171]:
6
In [172]:
sumof(lambda x:x*x, 1, 20)
Out[172]:
2470
In [173]:
sumof(lambda x:x**3, 1, 20)
Out[173]:
36100

with this small abstraction of sumation we can do lots of magic in one line. for example The series 8/1*3 + 8/5*7 + 8/9*11 + ....converges slowly to pi. we can compute pi!

In [176]:
sumof(lambda n:8/((4*n+1)*(4*n+3)), 0, 10000)
Out[176]:
3.1415426535898203
In [177]:
max([1,2,3,4,5])
Out[177]:
5
In [179]:
nums = ["one","two","three","four","five","six"]
In [180]:
max(nums)
Out[180]:
'two'
In [182]:
max(nums, key=len)
Out[182]:
'three'
In [183]:
min(nums, key=len)
Out[183]:
'one'
In [184]:
sorted(nums)
Out[184]:
['five', 'four', 'one', 'six', 'three', 'two']
In [185]:
sorted(nums, key=len)
Out[185]:
['one', 'two', 'six', 'four', 'five', 'three']
In [186]:
records = [("A",6.7, 100),
           ("B", 7.8, 110),
           ("C",8.0, 90),
           ("D",7.0, 99),
           ("E", 7.7, 105)
          ]
In [188]:
max(records)
Out[188]:
('E', 7.7, 105)
In [189]:
max(records, key=lambda r:r[1])
Out[189]:
('C', 8.0, 90)
In [190]:
max(records, key=lambda r:r[2])
Out[190]:
('B', 7.8, 110)

Functions as return value

In [197]:
def make_logger(loggertype):
    
    def logger(msg):
        print("[{type}]: {msg}".format(type=loggertype, msg=msg))
    
    return logger
In [198]:
info = make_logger("INFO")
warn = make_logger("WARNING")
error = make_logger("ERROR")
In [199]:
info
Out[199]:
<function __main__.make_logger.<locals>.logger>
In [200]:
warn
Out[200]:
<function __main__.make_logger.<locals>.logger>
In [201]:
error
Out[201]:
<function __main__.make_logger.<locals>.logger>
In [202]:
info("This is just for your information")
[INFO]: This is just for your information
In [203]:
warn("Something might be wrong...")
[WARNING]: Something might be wrong...
In [204]:
error("Something is wrong..crash")
[ERROR]: Something is wrong..crash
In [205]:
def column(index):
    return lambda r:r[index]
In [206]:
max(records, key=column(0))
Out[206]:
('E', 7.7, 105)
In [207]:
max(records, key=column(1))
Out[207]:
('C', 8.0, 90)
In [208]:
max(records, key=column(2))
Out[208]:
('B', 7.8, 110)
In [209]:
records
Out[209]:
[('A', 6.7, 100),
 ('B', 7.8, 110),
 ('C', 8.0, 90),
 ('D', 7.0, 99),
 ('E', 7.7, 105)]

problems

  • implement function reduce which reduces given sequence in single item based on combiner function
    >>> reduce([1,2,3,4], lambda x,y:x*y, default=1)
    24
  • using above function write a function to compute factorial of given number.
  • write a function zip_with which zips two different list in to single list based on combiner functions
    >>> zip_with([1,2,3],[2,3,4], lambda x,y:x+y)
    [3, 5, 7]
  • Write a function compose which takes two functions as argument and returns a function which will apply two functions in sequecnce.
    >>> fg = compose(f,g)
    >>> fg(2) ===> f(g(2))
In [210]:
def max_(seq, key=lambda x:x):
    m = seq[0]
    for item in seq[1:]:
        if key(item) > key(m):
            m = item
    return m
            
In [211]:
max(person)
Out[211]:
'name'
In [212]:
def reduce(seq, func, default):
    r = default
    for item in seq:
        r = func(r, item)
    return r
In [214]:
 reduce(range(10),lambda x,y:x+y, 0) #sum
Out[214]:
45
In [215]:
reduce(range(1,10), lambda x,y:x*y, 1) # mutliply items from list
Out[215]:
362880
In [216]:
reduce(range(100), lambda x,y: x if x>y else y, default=0) #max
Out[216]:
99
In [217]:
def factorial(n):
    return reduce(range(1,n+1), lambda x,y:x*y, 1)
In [218]:
factorial(5)
Out[218]:
120
In [219]:
import functools
In [220]:
functools.reduce
Out[220]:
<function _functools.reduce>
In [223]:
def zip_with(combiner, seq1, seq2):
    return [combiner(i, j) for i,j in zip(seq1, seq2)]
        
In [225]:
zip_with(lambda x,y:x+y, range(10), range(10))
Out[225]:
[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]
In [226]:
def compose(f, g):
    def fg(x):
        return f(g(x))
    return fg
In [227]:
fg = compose(square, lambda x:2*x)
In [228]:
fg(3)
Out[228]:
36
In [229]:
compose(lambda x:2*x, square)(3)
Out[229]:
18

decorators

In [230]:
def square(x):
    return x**2
In [231]:
def square(x):
    print("Begining .. square ({x})".format(x=x))
    return x**2
In [237]:
def debug(f):
    
    def wrapper(*args, **kwargs):
        print("Beginning ", f.__name__, args, kwargs)
        r = f(*args, **kwargs)
        print("Returning ", r)
        return r
    
    return wrapper
    
In [238]:
def add(x,y):
    return x+y
In [239]:
add(3,4)
Out[239]:
7
In [240]:
add_ = debug(add)
In [241]:
add_(3,4)
Beginning  add (3, 4) {}
Returning  7
Out[241]:
7
In [242]:
add(3,4)
Out[242]:
7
In [244]:
add = debug(add)
add(3,4)
In [251]:
def sumofsquares(x,y):
    return square(x) + square(y)

def square(x):
    return x*x
In [247]:
square = debug(square)
In [248]:
sumofsquares(3,4)
Beginning  square (3,) {}
Returning  9
Beginning  square (4,) {}
Returning  16
Out[248]:
25
In [249]:
def sumofcubes(x,y):
    return cube(x) + cube(y)

@debug
def cube(x):
    return x*x*x
In [250]:
sumofcubes(3,4)
Beginning  cube (3,) {}
Returning  27
Beginning  cube (4,) {}
Returning  64
Out[250]:
91

problem

  • Write a decorators timeit which times the functions. make use of time.time() function to get timestamp at given time.
@timeit
def saxpy(n=10000):
    s = 1.0
    for i in range(1,n):
        s = s + i*i*1.0
    return s

>>> saxpy()
time taken by saxy = 2.0 sec

decorators with parameters

In [252]:
@login_required(role="admin")
def add_user(name):
    pass
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
<ipython-input-252-bdaf5d207667> in <module>()
----> 1 @login_required(role="admin")
      2 def add_user(name):
      3     pass

NameError: name 'login_required' is not defined
In [253]:
add
Out[253]:
<function __main__.debug.<locals>.wrapper>
In [254]:
add(2,3)
Beginning  add (2, 3) {}
Returning  5
Out[254]:
5
In [255]:
def with_retries(tries=5):
    def decor(f):
        def wrapper(*args, **kwargs):
            for i in range(tries):
                try:
                    return f(*args, **kwargs)
                except Exception as e:
                    print("Retrying ...")
            print("Giving up...")
        return wrapper
    
    return decor
        
In [256]:
import requests
@with_retries(tries=3)
def download(url):
    return requests.get(url)
In [257]:
download("http://googl.nosuchurl/junk")
Retrying ...
Retrying ...
Retrying ...
Giving up...
In [258]:
def fib(n):
    if n in [1,2]:
        return 1
    else:
        return fib(n-1) + fib(n-2)
In [265]:
for i in range(1,15):
    print(fib(i), end=",")
1,1,2,3,5,8,13,21,34,55,89,144,233,377,
In [260]:
fib(10)
Out[260]:
55
In [306]:
%%file trace.py
level = 0


def trace(f):
    
    def wrapper(*args, **kwargs):
        global level
        print("| " * level + "|-- " + f.__qualname__, args)
        level += 1
        value = f(*args)
        level -= 1
        print("| " * level + "|-- " + "return", value)
        return value
    return wrapper
Overwriting trace.py
In [307]:
import imp
import trace
imp.reload(trace)
Out[307]:
<module 'trace' from '/home/vikrant/trainings/2018/vmware-advanced-apr/trace.py'>
In [284]:
def square(x):
    return x*x
In [285]:
sumofsquares(3,4)
Out[285]:
25
In [308]:
from trace import trace
@trace
def square(x):
    return x*x

@trace
def sumofsquares(x,y):
    return square(x) + square(y)
In [309]:
sumofsquares(3,4)
|-- sumofsquares (3, 4)
| |-- square (3,)
| |-- return 9
| |-- square (4,)
| |-- return 16
|-- return 25
Out[309]:
25
In [288]:
print(*[1,2,3,4])
1 2 3 4
In [276]:
print(1,2,3,4)
1 2 3 4
In [310]:
@trace
def fib(n):
    if n in [1,2]:
        return 1
    else:
        return fib(n-1) + fib(n-2)
In [311]:
fib(5)
|-- fib (5,)
| |-- fib (4,)
| | |-- fib (3,)
| | | |-- fib (2,)
| | | |-- return 1
| | | |-- fib (1,)
| | | |-- return 1
| | |-- return 2
| | |-- fib (2,)
| | |-- return 1
| |-- return 3
| |-- fib (3,)
| | |-- fib (2,)
| | |-- return 1
| | |-- fib (1,)
| | |-- return 1
| |-- return 2
|-- return 5
Out[311]:
5
In [312]:
fib(7)
|-- fib (7,)
| |-- fib (6,)
| | |-- fib (5,)
| | | |-- fib (4,)
| | | | |-- fib (3,)
| | | | | |-- fib (2,)
| | | | | |-- return 1
| | | | | |-- fib (1,)
| | | | | |-- return 1
| | | | |-- return 2
| | | | |-- fib (2,)
| | | | |-- return 1
| | | |-- return 3
| | | |-- fib (3,)
| | | | |-- fib (2,)
| | | | |-- return 1
| | | | |-- fib (1,)
| | | | |-- return 1
| | | |-- return 2
| | |-- return 5
| | |-- fib (4,)
| | | |-- fib (3,)
| | | | |-- fib (2,)
| | | | |-- return 1
| | | | |-- fib (1,)
| | | | |-- return 1
| | | |-- return 2
| | | |-- fib (2,)
| | | |-- return 1
| | |-- return 3
| |-- return 8
| |-- fib (5,)
| | |-- fib (4,)
| | | |-- fib (3,)
| | | | |-- fib (2,)
| | | | |-- return 1
| | | | |-- fib (1,)
| | | | |-- return 1
| | | |-- return 2
| | | |-- fib (2,)
| | | |-- return 1
| | |-- return 3
| | |-- fib (3,)
| | | |-- fib (2,)
| | | |-- return 1
| | | |-- fib (1,)
| | | |-- return 1
| | |-- return 2
| |-- return 5
|-- return 13
Out[312]:
13
In [313]:
def square(s):
    """
    returns square of a number
    """
    return s*s
In [314]:
help(square)
Help on function square in module __main__:

square(s)
    returns square of a number

In [315]:
@debug
def square(s):
    """
    returns square of a number
    """
    return s*s
In [316]:
help(square)
Help on function wrapper in module __main__:

wrapper(*args, **kwargs)

In [317]:
from functools import  wraps
def debug(f):
    
    @wraps(f)
    def wrapper(*args, **kwargs):
        print("Beginning ", f.__name__, args, kwargs)
        r = f(*args, **kwargs)
        print("Returning ", r)
        return r
    
    return wrapper
    
In [318]:
@debug
def square(s):
    """
    returns square of a number
    """
    return s*s
In [319]:
help(square)
Help on function square in module __main__:

square(s)
    returns square of a number

generators and iterators

In [320]:
for i in range(5):
    print(i)
0
1
2
3
4
In [323]:
r = iter(range(5))
In [324]:
next(r)
Out[324]:
0
In [325]:
next(r)
Out[325]:
1
In [326]:
next(r)
Out[326]:
2
In [327]:
next(r)
Out[327]:
3
In [328]:
next(r)
Out[328]:
4
In [329]:
next(r)
---------------------------------------------------------------------------
StopIteration                             Traceback (most recent call last)
<ipython-input-329-0b5056469c9c> in <module>()
----> 1 next(r)

StopIteration: 
In [330]:
def squares(n):
    for i in range(n):
        yield i
In [331]:
s = squares(5)
In [332]:
s
Out[332]:
<generator object squares at 0x7f20a66743b8>
In [333]:
next(s)
Out[333]:
0
In [334]:
next(s)
Out[334]:
1
In [335]:
def squares(n):
    print("Begining...")
    for i in range(n):
        print("yielding ...")
        yield i
        print("control is back")
    print("Exiting....")
In [336]:
s = squares(3)
In [337]:
next(s)
Begining...
yielding ...
Out[337]:
0
In [338]:
next(s)
control is back
yielding ...
Out[338]:
1
In [339]:
next(s)
control is back
yielding ...
Out[339]:
2
In [340]:
next(s)
control is back
Exiting....
---------------------------------------------------------------------------
StopIteration                             Traceback (most recent call last)
<ipython-input-340-bc0566bea448> in <module>()
----> 1 next(s)

StopIteration: 
In [341]:
for i in squares(10):
    print(i, end=",")
Begining...
yielding ...
0,control is back
yielding ...
1,control is back
yielding ...
2,control is back
yielding ...
3,control is back
yielding ...
4,control is back
yielding ...
5,control is back
yielding ...
6,control is back
yielding ...
7,control is back
yielding ...
8,control is back
yielding ...
9,control is back
Exiting....

problem

  • Write a generator function countdown which will yield numbers in reverse fashion.
    >>> for i in countdown(5):
          print(i)
    5
    4
    3
    2
    1
In [343]:
for i in [1,2,3,4,5]:
    print(i)
1
2
3
4
5
In [344]:
l = [1,2,3,4,5]
In [345]:
l1 = range(1, 6)
In [346]:
sum([i for i in range(1000)])
Out[346]:
499500
In [348]:
l = [i for i in range(1000)]
In [349]:
g = (i for i in range(1000))
In [350]:
g
Out[350]:
<generator object <genexpr> at 0x7f20a665da98>
In [353]:
g = (i for i in range(100000))
In [354]:
sum(g)
Out[354]:
4999950000
In [355]:
def ones():
    while True:
        yield 1
In [356]:
def take(seq, n):
    return [next(seq) for i in range(n)]
        
In [358]:
take(iter(range(1000)), 10)
Out[358]:
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
In [359]:
o = ones()
In [360]:
o
Out[360]:
<generator object ones at 0x7f20bc226fc0>
In [361]:
next(o)
Out[361]:
1
In [362]:
take(o, 10)
Out[362]:
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
In [363]:
def gen():
    for i in range(4):
        yield
In [364]:
g = gen()
In [365]:
next(g)
In [ ]: