Sep 25-27, 2019 Vikrant Patil
These notes are available online at http://notes.pipal.in/2020/arcesium_advanced_feb/day1.html
© Pipal Academy LLP
We will be using python 3.7 from anaconda for this training. You can download it from
primes = [2, 3, 5, 7, 11, 13, 17]
primes[0]
primes[-1]
primes[2:5] # start at 2nd index and stop at 5th index (exlude 5th)
primes[1:5:2]#start at 1 end at 5 at interval of 2
primes[:3] # start at zero end at 3 ...... take first 3
primes[3:] # drop first 3
primes[:] # copy
primes[::2]
primes[::-1]
def is_palindrome(word):
return word == word[::-1]
is_palindrome("hello")
is_palindrome("madam")
table = [[1,1,1,],[2,2,2],[3,3,3]]
table[0]
table[-1]
We want to access column instead of row!
for p in primes:
print(p*p)
sqrs = []
for p in primes:
sqrs.append(p*p)
print(sqrs)
[p*p for p in primes]
nums = list(range(10))
nums
evens = [e for e in nums if e%2==0]
evens
Lets get back to column access!
table
table[0]
table[0][0]
table[1][0]
table[2][0]
def column(datamatrix, colnum):
rowcount = len(datamatrix)
return [datamatrix[r][colnum] for r in range(rowcount)]
column(table, 0)
problem
transpose which makes a transpose of 2d matrix (given as list of lists)def transpose(datamatrix):
colcount = len(datamatrix[0])
return [column(datamatrix, c) for c in range(colcount)]
transpose(table)
list(range(5))
%%file data.csv
A1,A2,A3
B1,B2,B3
C1,C2,C3
def csvparser(filename):
with open(filename) as f:
return [line.strip().split(",") for line in f]
csvparser("data.csv")
def parseints(line):
return [int(item) for item in line.strip().split(",")]
def csvparseints(filename):
with open(filename) as f:
return [parseints(line) for line in f]
%%file numeric.csv
1,2,3
4,5,6
7,8,9
csvparseints("numeric.csv")
def quick(dataarray):
if not dataarray:
return []
pivote = dataarray[0]
less = [n for n in dataarray[1:] if n < pivote]
greater = [n for n in dataarray[1:] if n >= pivote]
return quick(less) + [pivote] + quick(greater)
quick([23,45,1,6,23,6,2])
Positional arguments
def cylinder_volume(radius, height):
return 3.14*radius**2*height
cylinder_volume(1.0, 11)
cylinder_volume(11, 1.0)
Named arguments
cylinder_volume(radius=1.0, height=11)
cylinder_volume(height=11, radius=1.0)
cylinder_volume(1.0, height=11)
cylinder_volume(height=11, 1.0)
cylinder_volume(radius=1.0, 11.0)
default arguments
def cylinder_volume(radius=1.0, height=1.0):
return 3.14*radius**2*height
cylinder_volume()
cylinder_volume(radius=5.0)
cylinder_volume(1.0)
cylinder_volume(1.0, 5.0)
cylinder_volume(radius=2.0, 10)
import random
def jitter(value, dev=random.random()):
return value + dev
for i in range(5):
print(jitter(i))
def jitter(value, dev=None):
if dev == None:
dev = random.random()
return value + dev
for i in range(5):
print(jitter(i))
jitter(5, 0.6)
Default arguments are initialised only once, at the time of defination
Do not use mutable datatype as default arguments
def extend(data, default=[]):
default.extend(data)
return default
extend([2,3,4])
extend([3,4,5])
def extend1(data, default=None):
if not default:
default = []
default.extend(data)
return default
extend1([1,1,1], [2,2,2])
extend1([1,1,1])
Named only arguments
word = ["one", "two", "three", "four", "five"]
max(word)
max(word, key=len)
max(word, len)
def foobar(x, y, *, name="bar"):
pass
foobar(2,3)
foobar(2,3,"foo")
foobar(3,4, name="foo")
def foo(a, b, *, name="foo", surname="bar"):
pass
foo(1,2)
foo(1,2,name="abc", surname="xyz")
foo(1, 2, "abc")
variable number of number arguments
import os
os.path.join("/", "home", "vikrant")
os.path.join("/","home","vikrant","trainings","2020")
def myjoin(*args): # * is for variable number of pisitional arguments
sep = "_"
return sep.join(args)
myjoin("This","is","variable","number")
myjoin("of","arguments")
def func(*args):
print(args)
func(1,2,3)
func([1,2,3])
func()
def pizza(bread, cheez, *toppings):
print("bread", bread)
print("cheez", cheez)
print("toppings", toppings)
pizza("brown","cottage", "papper", "chilli")
def make_account(initial_balance, name, **personal_details):
account = {"balance":initial_balance,
"name":name}
for k,v in personal_details.items():
account[k] = v
return account
make_account(0, "vikrant", address="Maharashtra", phone="90909323092")
def mysum(*args):
s = 0
for item in args:
s += item
return s
mysum(1, 2, 3,4)
numbers = [1,1,2,3,5,8,13
]
mysum(numbers)
mysum(*numbers)
problem
table
list(zip([1,2,3],['a','b','c']))
list(zip(*table))
transpose(table)
mysum(*numbers)
zip(*table) ---> zip([1,1,1], [2,2,2], [3,3,3])
def make_adder(x):
def adder(y):
return x+y
return adder
adder5 = make_adder(5)
adder5
adder5(7)
def make_logger(type_):
def logger(msg):
print(type_.upper(), ":", msg)
return logger
info = make_logger("info")
warning = make_logger("warn")
error = make_logger("error")
info("This is for your information")
warning("Something might be wrong!")
error("Definately..somethng went wrong !")
max(word, key=len)
sorted(word, key=len)
records = [
("HCL", 500.0, 10.0),
("INFY", 800.0, 8.0),
("RELIANCE", 245, 3.5),
("BOSCH", 540, -5.0)
]
def getprice(r):
return r[1]
max(records, key=getprice)
def getgain(r):
return r[2]
max(records, key=getgain)
max(records, key=lambda r:r[1])
add = lambda x, y : x+y
add(3.4)
add(3,4)
def sumofsquares(a, b):
return square(a) + square(b)
def sumofcubes(a, b):
return cube(a) + cube(b)
def square(x):
return x*x
def cube(x):
return x*x
def sumof(f, a, b):
return f(a) + f(b)
sumof(square, 6, 8)
sumofsquares(6, 8)
def sumofterms(term, start, end):
s = 0
for n in range(start, end):
s += term(n)
return s
sumofterms(lambda x:x , 1, 101)
problems
repeat which takes a func as first argument , initial value as second argument and an integer n as third argument. This function repeats func on value n times.
>>> repeat(lambda x:2*x, 1, 5
32
compose which composes two functions
>>> f = lambda x: 2*x
>>> g = lambda x: x*x
>>> fg = compose(f, g)
>>> fg(2) # f(g(2))
8
8/1.3 + 8/5.7 + 8/9.11 .....def compose(f, g):
def fg(value):
return f(g(value))
return fg
fg = compose(lambda x:2*x, lambda x: 2*x+1)
fg(3)
def make_poly(coeff):
def poly(x):
s = 0
for i, c in enumerate(coeff):
s += c*x**i
return s
return poly
P2 = make_poly([1, 2, 1])
P2(3)
foo = lambda x, f: f(x)
foo(2, adder5)
def compose(f, g):
return lambda x: f(g(x))
def add(a, b):
return a+b
def add_(a, b):
print("Begin add", a, b)
r = a+b
print("End add", r)
return r
add(4, 5)
add(3, 4)
add_(3,4)
add = add_
def sumofsquares(a, b):
return add(square(a), square(b))
sumofsquares(3, 4)
def debug(f):
def wrapper(*args):
print("Before", args)
r = f(*args)
print("After", r)
return r
return wrapper
def cube(x):
return x**3
cube(4)
def sumofcubes(x, y):
return cube(x) + cube(y)
cube = debug(cube)
sumofcubes(5,6)
def debug(f):
def wrapper(*args):
print("Before ",f.__qualname__, args)
r = f(*args)
print("After", f.__qualname__, r)
return r
return wrapper
def cube(x):
return x**3
cube = debug(cube)
sumofcubes(4, 6)
def square(x):
return x*x
def somemathsfunc(x, y):
return square(x)*cube(y) + cube(x+y)
square = debug(square)
somemathsfunc(5, 8)
@debug
def add(a,b):
return a+b
# add = debug(add)
(1, "string", [], {}, func)
def debugdecor(f):
def wrapper(*args, **kwargs):
print(f.__qualname__, args, kwargs)
#print
r = f(*args, **kwargs)
print(f.__qualname__ , r)
# postprocessing
return r
return wrapper
add.__name__
add.__qualname__
def foo():
pass
foo.__name__
foo.__qualname__
def foo():
"""
Help for foo
"""
pass
help(foo)
help(add)
from functools import wraps
def debugdecor(f):
@wraps(f)
def wrapper(*args, **kwargs):
print(f.__qualname__, args, kwargs)
#print
r = f(*args, **kwargs)
print(f.__qualname__ , r)
# postprocessing
return r
return wrapper
import math
dir(math) ## to find attributes of given object
callable(foo) # to chek whether given object is function/class
callable(4)
@debugdecor
def square(x):
"computes squares"
return x*x
help(square)
problems
deprecated which prints deprecation message when the function is called.with_retries which rtries the decorated function 5 times. After five tries it prints message of "Giving up!" and quits.timeit which times execution of decorated functionimport time
time.time()
import requests
def downlaod(url):
resp = requests.get(url)
return resp.text
downlaod("http://httpbin.org/htm")
downlaod("http://nosuchurl.sdsd/sakjhdl/sfdkjhdsfk")
def deprecated(f):
@wraps(f)
def wrapper(*args, **kwargs):
print("Deprecation warning: {func} is deprecated!".format(func=f.__qualname__))
return f(*args, **kwargs)
return wrapper
@deprecated
def hello():
pass
hello()
from functools import wraps
import time
def with_retries(f):
@wraps(f)
def wrapper(*args):
for i in range(5):
try:
return f(*args)
except:
time.sleep(1)
print("Retrying {} .. {}".format(f.__qualname__, i+1))
print("Giving up!")
return wrapper
@with_retries
def wget(url):
return requests.get(url).text
wget("http://nosuchurl/skdj/jsdj")
def foo():
pass
foo
@with_retries(tries, delay)
def wget(url):
pass
foo
foo()
def with_retries(tries, delay):
def decor(f):
def wrapper(*args):
for i in range(tries):
try:
return f(*args)
except:
time.sleep(delay)
print("Retrying {} ... {}".format(f.__qualname__, i+1))
print("Giving up!")
return wrapper
return decor
@with_retries(5, 0.5)
def wget(url):
return requests.get(url).text
def with_retries(tries=5, delay=0.5):
def decor(f):
def wrapper(*args):
for i in range(tries):
try:
return f(*args)
except:
time.sleep(delay)
print("Retrying {} ... {}".format(f.__qualname__, i+1))
print("Giving up!")
return wrapper
return decor
@with_retries()
def wget(url):
return requests.get(url).text
from functools import partial
def add(x, y):
return x+y
f = partial(add, 2)
f
f(3)
%%file trace.py
from functools import wraps
level = 0
def trace(f):
@wraps(f)
def wrapper(*args):
global level
print("| "*level + "|--" + f.__qualname__ , args)
level +=1
r = f(*args)
level -= 1
print("| "*level + "|--" + "return" , r )
return r
return wrapper
import trace
@trace.trace
def square(a):
return a*a
@trace.trace
def sumofsquares(x, y):
return square(x) + square(y)
sumofsquares(5,6)
%%file fib.py
from trace import trace
import sys
@trace
def fib(n):
if n in [1,2]:
return 1
else:
return fib(n-1) + fib(n-2)
if __name__ == "__main__":
print(fib(int(sys.argv[1])))
!python fib.py 5
!python fib.py 8
%%file memoize.py
from functools import wraps
def memoize(f):
cache = {}
@wraps(f)
def wrapper(*args):
if args not in cache:
cache[args] = f(*args)
return cache[args]
return wrapper
%%file fib1.py
import sys
from trace import trace
from memoize import memoize
@memoize
@trace
def fib(n):
if n in [1,2]:
return 1
else:
return fib(n-1) + fib(n-2)
if __name__ == "__main__":
print(fib(int(sys.argv[1])))
!python fib1.py 5
!python fib1.py 8
!python fib1.py 30
!grep --help
click is python library which makes use of decorators to make commandline applications
def insertion_sort(data):
result = []
for value in data:
insert_value(result, value)
return result
def insert_value(array, value):
for i,item in enumerate(array):
if item > value:
array.insert(i, value)
return
array.append(value)
from random import randint
maxsize = 10**4
data = [randint(0, maxsize) for _ in range(maxsize)]
test =lambda :insertion_sort(data)
from cProfile import Profile
profiler = Profile()
profiler.runcall(test)
from pstats import Stats
stats = Stats(profiler)
stats.sort_stats("cumulative")
stats.print_stats()
from bisect import bisect_left
def insert_value(array, value):
i = bisect_left(array, value)
array.insert(i, value)
profiler = Profile()
profiler.runcall(test)
from pstats import Stats
stats = Stats(profiler)
stats.sort_stats("cumulative")
stats.print_stats()
def my_utility(a, b):
pass
def foo():
for _ in range(1000):
my_utility(4, 5)
def bar():
for _ in range(10): my_utility(1, 3)
def main_program():
for _ in range(20):
foo()
bar()
profiler = Profile()
profiler.runcall(main_program)
stats = Stats(profiler)
stats.print_stats()
stats.print_callers()
import math
math.pi
math.sin
from math import sin
sin
%%timeit
s = 0
for i in range(10000):
for j in range(1000):
s = s + math.sin(i+j*math.pi/2)
%%timeit
from math import pi, sin
s = 0
for i in range(10000):
for j in range(1000):
s = s + sin(i+j*pi/2)
%%file looping.py
nums = [500, 600, 700]
strvalues = ['x', 'y', 'z']
def loop():
for number in nums:
print(number)
for s in strvalues:
print(s)
if __name__ == "__main__":
from pdb import set_trace
loop()
nums = [500, 600, 700]
strvalues = ['x', 'y', 'z']
def loop():
for number in nums:
print(number)
for s in strvalues:
print(s)
from pdb import set_trace
set_trace()
loop()
? - > help ..will list down all commands
l/list -> show code with line numbers
b linenum - > set breakpoint at linenum
r - > run the code and stop at break point
step -> will go and stop inside function call
next - > execute complete line and go to next line
at any point you can examin local variables ...print or modify