Advanced Python Training at Arcesium - Day 1

Sep 25-27, 2019 Vikrant Patil

These notes are available online at http://notes.pipal.in/2019/arcesium_advanced_sep/day1.html

© Pipal Academy LLP

Day 1 | Day 2 | Day 3

We will be using python 3 (>= 3.0) from anaconda for this training. You can download it from

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

Warmup

In [1]:
digits = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
In [2]:
digits[0]
Out[2]:
0
In [3]:
digits[-1]
Out[3]:
9
In [4]:
digits[2:6]
Out[4]:
[2, 3, 4, 5]
In [5]:
digits[:]
Out[5]:
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
In [7]:
digits[3:] #drop first three
Out[7]:
[3, 4, 5, 6, 7, 8, 9]
In [8]:
digits[:5]
Out[8]:
[0, 1, 2, 3, 4]
In [9]:
digits[2:7:2]
Out[9]:
[2, 4, 6]
In [10]:
digits[::2]
Out[10]:
[0, 2, 4, 6, 8]
In [11]:
digits[:-5]
Out[11]:
[0, 1, 2, 3, 4]
In [12]:
digits[-5:]
Out[12]:
[5, 6, 7, 8, 9]
In [13]:
digits[::-1]
Out[13]:
[9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
In [14]:
def is_palindrom(word):
    return word == word[::-1]
In [15]:
is_palindrom("madam")
Out[15]:
True
In [17]:
table = [[1,1,1],[2,2,2],[3,3,3]]
In [18]:
table[0]
Out[18]:
[1, 1, 1]
In [19]:
table[-1]
Out[19]:
[3, 3, 3]

list comprehensions

In [20]:
for i in range(5):
    print(i)
0
1
2
3
4
In [21]:
range(5)
Out[21]:
range(0, 5)
In [22]:
list(range(5))
Out[22]:
[0, 1, 2, 3, 4]
In [23]:
nums = [3, 5, 1, 7, 6]
sqr = []
for n in nums:
    sqr.append(n*n)
print(sqr)
[9, 25, 1, 49, 36]
In [24]:
[n*n for n in nums]
Out[24]:
[9, 25, 1, 49, 36]
In [25]:
s = [n*n for n in nums]
In [26]:
s
Out[26]:
[9, 25, 1, 49, 36]
In [27]:
es = [n*n for n in nums if n%2==0]
In [28]:
es
Out[28]:
[36]
In [29]:
table
Out[29]:
[[1, 1, 1], [2, 2, 2], [3, 3, 3]]
In [30]:
matrix = [[1,1,1],[2,2,2],[3,3,3]]
In [31]:
def column(data, colnum):
    pass
In [32]:
matrix[0]
Out[32]:
[1, 1, 1]
In [33]:
matrix[0][0]
Out[33]:
1
In [34]:
matrix[1][0]
Out[34]:
2
In [35]:
matrix[2][0]
Out[35]:
3
In [41]:
def column(data, colnum):
    rowcount = len(data)
    return [data[row][colnum] for row in range(rowcount)]
In [42]:
def transpose(data):
    colcount = len(data[0])
    return [column(data, c) for c in range(colcount)]
In [43]:
transpose(matrix)
Out[43]:
[[1, 2, 3], [1, 2, 3], [1, 2, 3]]
In [44]:
def column(data, colnum):
    return [row[colnum] for row in data]

reading files

In [45]:
%%file data.csv
A1,A2,A3
B1,B3,B3
C1,C2,C3
Writing data.csv
In [46]:
def csvparser(filename):
    with open(filename) as f:
        csvdata = []
        for line in f:
            csvdata.append(line.strip().split(","))
        return csvdata
            
In [47]:
csvparser("data.csv")
Out[47]:
[['A1', 'A2', 'A3'], ['B1', 'B3', 'B3'], ['C1', 'C2', 'C3']]
In [48]:
def csvparser(filename):
    with open(filename) as f:
        return [line.strip().split(",") for line in f]
In [49]:
def qsort(data):
    if not data:
        return []
    
    pivot = data[0]
    
    less = [n for n in data[1:] if n <pivot]
    greater = [n for n in data[1:] if n>= pivot]
    
    return qsort(less) + [pivot] + qsort(greater)
In [50]:
qsort([343,34,1,3,23,6,778,3])
Out[50]:
[1, 3, 3, 6, 23, 34, 343, 778]
In [51]:
qsort([])
Out[51]:
[]
In [52]:
qsort([1,2,3,4,5])
Out[52]:
[1, 2, 3, 4, 5]
In [54]:
qsort(list(reversed(range(5))))
Out[54]:
[0, 1, 2, 3, 4]

Functions

Positional arguments

In [55]:
def cylinder_volume(radius, height):
    return 3.14*radius**2*height
In [56]:
cylinder_volume(1.0, 10)
Out[56]:
31.400000000000002
In [57]:
cylinder_volume(10, 1.0)
Out[57]:
314.0

named parameters

In [58]:
cylinder_volume(radius=1.0, height=10)
Out[58]:
31.400000000000002
In [59]:
cylinder_volume(height=10, radius=1)
Out[59]:
31.400000000000002
In [60]:
"*"**2
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-60-2d9957198c45> in <module>
----> 1 "*"**2

TypeError: unsupported operand type(s) for ** or pow(): 'str' and 'int'
In [61]:
"#"*2
Out[61]:
'##'
In [62]:
cylinder_volume(1, height=10)
Out[62]:
31.400000000000002
In [63]:
cylinder_volume(height=10, 1)
  File "<ipython-input-63-41de46ab70ab>", line 1
    cylinder_volume(height=10, 1)
                              ^
SyntaxError: positional argument follows keyword argument

Named argumnets can only follow after all positional arguments are over. After named argument you can not give postional argument

default arguments

In [64]:
def cylinder_volume(radius=1.0, height=1.0):
    return 3.14*radius**2*height
In [65]:
cylinder_volume()
Out[65]:
3.14
In [66]:
cylinder_volume(radius=5)
Out[66]:
78.5
In [67]:
cylinder_volume(height=10)
Out[67]:
31.400000000000002
In [69]:
cylinder_volume(3) #radius 3 and height as default
Out[69]:
28.26
In [73]:
import random

def jitter(data, dev=random.random()):
    return data + dev

for n in [23,45,6,3]:
    print(jitter(n))
23.369534022062787
45.36953402206279
6.369534022062789
3.3695340220627883
In [74]:
def jitter1(data, dev=None):
    if not dev:
        dev = random.random()
    return data + dev
In [75]:
for n in range(5):
    print(jitter1(n))
0.7433648557333602
1.652206141920452
2.408798971817391
3.2595466594573126
4.773604607895664
In [78]:
def collection(data, default=[]):
    default.extend(data)
    return default

def collection1(data, default=None):
    if not default:
        default = []
    default.extend(data)
    return default
    
In [79]:
collection([1,2,3], default=[1,1,1])
Out[79]:
[1, 1, 1, 1, 2, 3]
In [80]:
collection([1,2,3])
Out[80]:
[1, 2, 3]
In [82]:
collection([1,1,1])
Out[82]:
[1, 2, 3, 1, 1, 1]
In [84]:
collection1([1,2,3])
Out[84]:
[1, 2, 3]
In [85]:
collection1([1,1,1])
Out[85]:
[1, 1, 1]

Named only arguments

In [86]:
words = ["hello", "welcome", "Namaskar", "Ramram"]
In [87]:
max(words)
Out[87]:
'welcome'
In [88]:
max(words, key=len)
Out[88]:
'Namaskar'
In [89]:
max(words, len)
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-89-173597093508> in <module>
----> 1 max(words, len)

TypeError: '>' not supported between instances of 'builtin_function_or_method' and 'list'
In [90]:
def foo(x, y, *, name="bar"):
    pass
In [91]:
foo(1,2)
In [92]:
foo(1,2,"hello")
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-92-53b9ab7c485d> in <module>
----> 1 foo(1,2,"hello")

TypeError: foo() takes 2 positional arguments but 3 were given
In [93]:
foo(3, 4, name="hello")
In [94]:
def bar(x,y,*,name):
    pass
In [95]:
bar(1,2)
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-95-13e93fb11eef> in <module>
----> 1 bar(1,2)

TypeError: bar() missing 1 required keyword-only argument: 'name'
In [96]:
bar(1, 2, name="hello")
In [97]:
import this
The Zen of Python, by Tim Peters

Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
Special cases aren't special enough to break the rules.
Although practicality beats purity.
Errors should never pass silently.
Unless explicitly silenced.
In the face of ambiguity, refuse the temptation to guess.
There should be one-- and preferably only one --obvious way to do it.
Although that way may not be obvious at first unless you're Dutch.
Now is better than never.
Although never is often better than *right* now.
If the implementation is hard to explain, it's a bad idea.
If the implementation is easy to explain, it may be a good idea.
Namespaces are one honking great idea -- let's do more of those!

variable number of arguments

In [98]:
import os.path
In [99]:
os.path.join("/","home","vikrant","training")
Out[99]:
'/home/vikrant/training'
In [101]:
os.path.join("/","home","vikrant")
Out[101]:
'/home/vikrant'
In [102]:
def myjoin(*args):
    sep = "_"
    return sep.join(args)
In [103]:
myjoin("a","b","c","d","e")
Out[103]:
'a_b_c_d_e'
In [104]:
d = ['x','y','z','m']
In [105]:
myjoin(*d)
Out[105]:
'x_y_z_m'
In [106]:
def mysum(*args):
    s = 0
    for item in args:
        s += item
    return s
In [108]:
list(zip([1,2,3,4],['a','b','c','d']))
Out[108]:
[(1, 'a'), (2, 'b'), (3, 'c'), (4, 'd')]
In [109]:
matrix
Out[109]:
[[1, 1, 1], [2, 2, 2], [3, 3, 3]]
In [111]:
list(zip([1,2],['a','b'],['x',['y']]))
Out[111]:
[(1, 'a', 'x'), (2, 'b', ['y'])]
In [112]:
def transpose(data):
    return list(zip(*data))
In [113]:
transpose(matrix)
Out[113]:
[(1, 2, 3), (1, 2, 3), (1, 2, 3)]
In [114]:
matrix
Out[114]:
[[1, 1, 1], [2, 2, 2], [3, 3, 3]]

Variable number keyword argmuments

In [116]:
def make_person(**kwargs):
    return {"Arcesium_"+k:v for k, v in kwargs.items()}
        
In [117]:
make_person(name="Praveen", surname="Nair")
Out[117]:
{'Arcesium_name': 'Praveen', 'Arcesium_surname': 'Nair'}
In [119]:
def foo(a, b, *args, **kwargs):
    print("a,b ", a, b)
    print(args)
    print(kwargs)
In [120]:
foo(1, 2, 'x','y', name="xyz", greet="hello")
a,b  1 2
('x', 'y')
{'name': 'xyz', 'greet': 'hello'}

functions as arguments

In [121]:
min(words, key=len)
Out[121]:
'hello'
In [122]:
sorted(words, key=len)
Out[122]:
['hello', 'Ramram', 'welcome', 'Namaskar']
In [123]:
records = [
    ("HCL", 500.0, 10.0),
    ("INFY", 800.0, 8.0),
    ("TATA", 247, 3),
    ("MM", 530, -5)
]
In [124]:
def getprice(r):
    return r[1]
In [125]:
lambda r: r[1]
Out[125]:
<function __main__.<lambda>(r)>
In [126]:
max(records, key=lambda r:r[1])
Out[126]:
('INFY', 800.0, 8.0)
In [127]:
max(records, key=lambda r:r[2])
Out[127]:
('HCL', 500.0, 10.0)
In [128]:
def column(colnum):
    return lambda r: r[colnum]
In [129]:
max(records, key=column(1))
Out[129]:
('INFY', 800.0, 8.0)
In [130]:
max(records, key=column(2))
Out[130]:
('HCL', 500.0, 10.0)

problem

  • Write a function repeat which takes a function as first argument, initial value as second argument, and an integer as third argument. repeat must apply function to initial value n times.
    >>> repeat(lambda x:2*x, 1, 5)
    32
  • Write a function 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
  • Write a function zip_with which works exactly like zip, but it also takes first argument as combiner function.
    >>> zip_with(lambda x:y: x+y, [1,2,3,4], [1,1,1,1])
    [2,3,4,5]
  • write a function fold which reduces given list into single item using combiner function
    >>> fold(lambda x,y: x+y, [1,1,1,1,1] , 0)
    5
  • 8/1.3 + 8/5.7 + 8/9.11 ...... this sums to pi. Try to write a function which can sum this series.

bonus

  • can you write repeat such that it resturns a new function which is equivalent to a function applying n times.
    >>> f = repeat(lambda x:2*x, 5)
    >>> f(1)
    32
In [131]:
def make_poly():
    return lambda x: x**x + x + 1
In [132]:
p = make_poly()
In [133]:
p(3)
Out[133]:
31
In [134]:
p(1)
Out[134]:
3
In [135]:
def make_poly(coeffs):
    def poly(x):
        s = 0
        for i, c in enumerate(coeffs):
            s = s + c*x**i
        return s
    
    return poly
In [136]:
P = make_poly([1, 1, 1])
In [137]:
P(1)
Out[137]:
3
In [138]:
P(2)
Out[138]:
7
In [139]:
def make_logger(msgtype):
    def logger(msg):
        print(msgtype.upper(), ":", msg)
    return logger
In [141]:
info = make_logger("info")
In [142]:
error = make_logger("error")
In [143]:
info("THis i sjust info")
INFO : THis i sjust info
In [144]:
error("Error message....")
ERROR : Error message....
In [145]:
def repeat(f, initial, n):
    for i in range(n):
        initial = f(initial)
    return initial
In [146]:
repeat(lambda x:2*x, 1, 5)
Out[146]:
32
In [147]:
def compose(f, g):
    def fg(x):
        return f(g(x))
    
    return fg
In [148]:
f = lambda x: 2*x
g = lambda x: x**2
fg = compose(f, g)
In [149]:
fg(2)
Out[149]:
8
In [150]:
def zip_with(f, v1, v2):
    return [f(i,j) for i, j in zip(v1, v2)]
In [151]:
x = [1,2,3,4]
y = [1, 1, 1,1]
In [152]:
for i, j in zip(x, y):
    print(i+j)
2
3
4
5
In [153]:
def zip_with(f, *args):
    return [ f(*val) for val in zip(*args)]
In [154]:
zip_with(lambda x, y, z: x+y+z, [1,1,1,1],[2,2,2,2],[3,3,3,3])
Out[154]:
[6, 6, 6, 6]
In [155]:
def f(*args):
    return sum(args)
In [156]:
zip_with(f, [1,1,1,1],[2,2,2,2],[3,3,3,3])
Out[156]:
[6, 6, 6, 6]
In [157]:
zip_with(f, [1,1,1,1],[2,2,2,2])
Out[157]:
[3, 3, 3, 3]
In [159]:
def sumof(term, n):
    s = 0
    for i in range(1, n+1):
        s += term(i)
    return s
In [160]:
def piseries(n):
    return 8/((4*n-3)*(4*n-1))
In [161]:
sumof(piseries, 1000)
Out[161]:
3.141092653621038
In [1]:
def fold(f, seq, initial):
    s = initial
    for i in seq:
        s = f(s, i)
    return s
In [2]:
import operator
In [3]:
fold(operator.add, range(101), 0)
Out[3]:
5050
In [4]:
sum(range(101))
Out[4]:
5050

Decorator

In [5]:
def square(x):
    return x*x
In [6]:
def debug(f):
    
    def wrapper(*args):
        print("Begin :", f.__qualname__, args)
        r = f(*args)
        print("Returning :", f.__qualname__,"=>", r)
        
    return wrapper
In [7]:
add
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
<ipython-input-7-92beb40d1a86> in <module>
----> 1 add

NameError: name 'add' is not defined
In [8]:
def add(x, y):
    return x+y
In [9]:
add
Out[9]:
<function __main__.add(x, y)>
In [10]:
aliasadd = add
In [11]:
aliasadd
Out[11]:
<function __main__.add(x, y)>
In [12]:
add(3, 4)
Out[12]:
7
In [13]:
aliasadd(3, 4)
Out[13]:
7
In [14]:
square = debug(square)
In [15]:
square(3)
Begin : square (3,)
Returning : square => 9
In [16]:
r = square(3)
Begin : square (3,)
Returning : square => 9
In [17]:
print(r)
None
In [18]:
def square(x):
    return x*x

def debug(f):
    
    def wrapper(*args):
        print("Begin :", f.__qualname__, args)
        r = f(*args)
        print("Returning :", f.__qualname__,"=>", r)
        return r
    
    return wrapper
In [19]:
square = debug(square)
In [20]:
square(5)
Begin : square (5,)
Returning : square => 25
Out[20]:
25
In [25]:
@debug
def sumofsquares(x, y):
    return square(x) + square(y)
In [26]:
sumofsquares(3, 5)
Begin : sumofsquares (3, 5)
Begin : square (3,)
Returning : square => 9
Begin : square (5,)
Returning : square => 25
Returning : sumofsquares => 34
Out[26]:
34
In [27]:
def cube(x):
    """
    returns x*x*x
    """
    return x**3
In [28]:
help(cube)
Help on function cube in module __main__:

cube(x)
    returns x*x*x

In [30]:
@debug
def area(radius):
    """
    computes area of a circle
    """
    return 3.14*radius**2
In [31]:
area(4)
Begin : area (4,)
Returning : area => 50.24
Out[31]:
50.24
In [32]:
help(area)
Help on function wrapper in module __main__:

wrapper(*args)

In [33]:
cube.__doc__
Out[33]:
'\n    returns x*x*x\n    '
In [34]:
from functools import wraps
In [35]:
def debug(f):
    
    @wraps(f)
    def wrapper(*args):
        print("Begin :", f.__qualname__, args)
        r = f(*args)
        print("Returning :", f.__qualname__,"=>", r)
        return r
    
    return wrapper
In [36]:
@debug
def area(radius):
    """
    computes area of a circle
    """
    return 3.14*radius**2
In [37]:
help(area)
Help on function area in module __main__:

area(radius)
    computes area of a circle

In [38]:
area.__doc__
Out[38]:
'\n    computes area of a circle\n    '
In [39]:
area.__annotations__
Out[39]:
{}
In [40]:
area.__qualname__
Out[40]:
'area'
In [41]:
area.__doc__
Out[41]:
'\n    computes area of a circle\n    '
In [42]:
area.__defaults__
In [43]:
def f(x=10):
    pass
In [44]:
f.__defaults__
Out[44]:
(10,)

problems

  • Write a decorator deprecated which prints deprecation message when the function is called.
  • Write a decorator with_retries which rtries the decorated function 5 times. After five tries it prints message of "Giving up!" and quits.
  • Write a decorator timeit which times execution of decorated function
In [45]:
import requests
In [46]:
!pip install requests
Requirement already satisfied: requests in /home/vikrant/anaconda3/lib/python3.7/site-packages (2.22.0)
Requirement already satisfied: idna<2.9,>=2.5 in /home/vikrant/anaconda3/lib/python3.7/site-packages (from requests) (2.8)
Requirement already satisfied: certifi>=2017.4.17 in /home/vikrant/anaconda3/lib/python3.7/site-packages (from requests) (2019.6.16)
Requirement already satisfied: urllib3!=1.25.0,!=1.25.1,<1.26,>=1.21.1 in /home/vikrant/anaconda3/lib/python3.7/site-packages (from requests) (1.24.2)
Requirement already satisfied: chardet<3.1.0,>=3.0.2 in /home/vikrant/anaconda3/lib/python3.7/site-packages (from requests) (3.0.4)
In [51]:
try:
    requests.get("http://nosuchsite.sdjksjlk/lsdsa/")
except Exception as e:
    print("Failed to downlod!")
Failed to downlod!
In [48]:
import time
In [49]:
time.time()
Out[49]:
1569407560.7970266
In [52]:
def deprecated(f):
    
    @wraps(f)
    def wrapper(*args, **kwargs):
        print("Function {funcname} is deprecated".format(funcname=f.__qualname__))
        print("You should instead use ...foo()")
        return f(*args, **kwargs)
    
    return wrapper
In [53]:
@deprecated
def hello():
    print("hello world!")
In [54]:
hello()
Function hello is deprecated
You should instead use ...foo()
hello world!
In [60]:
def with_retries(f):
    
    @wraps(f)
    def wrapper(*args, **kwargs):
        for i in range(5):
            try:
                return f(*args, **kwargs)
            except Exception as e:
                print("Retrying ..", f.__qualname__, i, e)
            print("Giving up!")
    return wrapper
In [61]:
@with_retries
def wget(url):
    resp = requests.get(url)
    return resp.text
In [62]:
wget("http://nosuchlkjsdalks.ksajdkjsa/sdsd")
Retrying .. wget 0 HTTPConnectionPool(host='nosuchlkjsdalks.ksajdkjsa', port=80): Max retries exceeded with url: /sdsd (Caused by NewConnectionError('<urllib3.connection.HTTPConnection object at 0x7f2ddd479160>: Failed to establish a new connection: [Errno -2] Name or service not known'))
Giving up!
Retrying .. wget 1 HTTPConnectionPool(host='nosuchlkjsdalks.ksajdkjsa', port=80): Max retries exceeded with url: /sdsd (Caused by NewConnectionError('<urllib3.connection.HTTPConnection object at 0x7f2ddd479240>: Failed to establish a new connection: [Errno -2] Name or service not known'))
Giving up!
Retrying .. wget 2 HTTPConnectionPool(host='nosuchlkjsdalks.ksajdkjsa', port=80): Max retries exceeded with url: /sdsd (Caused by NewConnectionError('<urllib3.connection.HTTPConnection object at 0x7f2ddd4792b0>: Failed to establish a new connection: [Errno -2] Name or service not known'))
Giving up!
Retrying .. wget 3 HTTPConnectionPool(host='nosuchlkjsdalks.ksajdkjsa', port=80): Max retries exceeded with url: /sdsd (Caused by NewConnectionError('<urllib3.connection.HTTPConnection object at 0x7f2ddd479470>: Failed to establish a new connection: [Errno -2] Name or service not known'))
Giving up!
Retrying .. wget 4 HTTPConnectionPool(host='nosuchlkjsdalks.ksajdkjsa', port=80): Max retries exceeded with url: /sdsd (Caused by NewConnectionError('<urllib3.connection.HTTPConnection object at 0x7f2ddd479390>: Failed to establish a new connection: [Errno -2] Name or service not known'))
Giving up!
In [66]:
%%file trace.py
import os
from functools import wraps

level = 0

def log(*args):
    if os.getenv("DEBUG") == "true":
        print(*args)
        
def trace(f):
    
    @wraps(f)
    def g(*args):
        global level
        log("| "*level + "|--" + f.__qualname__, args )
        level += 1
        value = f(*args)
        level -=1
        log("| "*level + "|--" + "return" , value)
        return value
    return g
Overwriting trace.py
In [71]:
import trace
In [74]:
@trace.trace
def square(x):
    return x*x

@trace.trace
def somofsquares(x,y):
    return square(x) + square(y)
In [75]:
somofsquares(5, 6)
Out[75]:
61
In [76]:
import os
In [77]:
%%file ssq.py
from trace import trace
import sys

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

@trace
def sumofsquares(x,y):
    return square(x) + square(y)


if __name__ == "__main__":
    sumofsquares(int(sys.argv[1]), int(sys.argv[2]))
Writing ssq.py
In [78]:
!DEBUG=true python ssq.py 5 6
|--sumofsquares (5, 6)
| |--square (5,)
| |--return 25
| |--square (6,)
| |--return 36
|--return 61
In [79]:
%%file fib.py
import sys
from trace import trace

@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])))
Writing fib.py
In [82]:
!DEBUG=true python fib.py 8
|--fib (8,)
| |--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
| |--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
|--return 21
21
In [83]:
%%file memoize.py
from functools import wraps

def memoize(f):
    cache = {}
    
    @wraps(f)
    def g(*args):
        if args not in cache:
            cache[args] = f(*args)
        return cache[args]
    return g
Writing memoize.py
In [84]:
%%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])))
Writing fib1.py
In [86]:
!DEBUG=true python fib1.py 30
|--fib (30,)
| |--fib (29,)
| | |--fib (28,)
| | | |--fib (27,)
| | | | |--fib (26,)
| | | | | |--fib (25,)
| | | | | | |--fib (24,)
| | | | | | | |--fib (23,)
| | | | | | | | |--fib (22,)
| | | | | | | | | |--fib (21,)
| | | | | | | | | | |--fib (20,)
| | | | | | | | | | | |--fib (19,)
| | | | | | | | | | | | |--fib (18,)
| | | | | | | | | | | | | |--fib (17,)
| | | | | | | | | | | | | | |--fib (16,)
| | | | | | | | | | | | | | | |--fib (15,)
| | | | | | | | | | | | | | | | |--fib (14,)
| | | | | | | | | | | | | | | | | |--fib (13,)
| | | | | | | | | | | | | | | | | | |--fib (12,)
| | | | | | | | | | | | | | | | | | | |--fib (11,)
| | | | | | | | | | | | | | | | | | | | |--fib (10,)
| | | | | | | | | | | | | | | | | | | | | |--fib (9,)
| | | | | | | | | | | | | | | | | | | | | | |--fib (8,)
| | | | | | | | | | | | | | | | | | | | | | | |--fib (7,)
| | | | | | | | | | | | | | | | | | | | | | | | |--fib (6,)
| | | | | | | | | | | | | | | | | | | | | | | | | |--fib (5,)
| | | | | | | | | | | | | | | | | | | | | | | | | | |--fib (4,)
| | | | | | | | | | | | | | | | | | | | | | | | | | | |--fib (3,)
| | | | | | | | | | | | | | | | | | | | | | | | | | | | |--fib (2,)
| | | | | | | | | | | | | | | | | | | | | | | | | | | | |--return 1
| | | | | | | | | | | | | | | | | | | | | | | | | | | | |--fib (1,)
| | | | | | | | | | | | | | | | | | | | | | | | | | | | |--return 1
| | | | | | | | | | | | | | | | | | | | | | | | | | | |--return 2
| | | | | | | | | | | | | | | | | | | | | | | | | | |--return 3
| | | | | | | | | | | | | | | | | | | | | | | | | |--return 5
| | | | | | | | | | | | | | | | | | | | | | | | |--return 8
| | | | | | | | | | | | | | | | | | | | | | | |--return 13
| | | | | | | | | | | | | | | | | | | | | | |--return 21
| | | | | | | | | | | | | | | | | | | | | |--return 34
| | | | | | | | | | | | | | | | | | | | |--return 55
| | | | | | | | | | | | | | | | | | | |--return 89
| | | | | | | | | | | | | | | | | | |--return 144
| | | | | | | | | | | | | | | | | |--return 233
| | | | | | | | | | | | | | | | |--return 377
| | | | | | | | | | | | | | | |--return 610
| | | | | | | | | | | | | | |--return 987
| | | | | | | | | | | | | |--return 1597
| | | | | | | | | | | | |--return 2584
| | | | | | | | | | | |--return 4181
| | | | | | | | | | |--return 6765
| | | | | | | | | |--return 10946
| | | | | | | | |--return 17711
| | | | | | | |--return 28657
| | | | | | |--return 46368
| | | | | |--return 75025
| | | | |--return 121393
| | | |--return 196418
| | |--return 317811
| |--return 514229
|--return 832040
832040
In [88]:
os.environ['DEBUG']="true"

decorators with parameters

Write a decorator with_retries that takes two parameters , retries, delay

@debug
def foo():
    pass

@with_retries(retries=5, delay=5)
def wget(url):
    pass

`
In [89]:
def f():
    pass
In [90]:
f
Out[90]:
<function __main__.f()>
In [91]:
f()
In [92]:
from functools import wraps

def with_retries(retries=5, delay=2):
    
    def decor(f):
        
        @wraps(f)
        def wrapper(*args, **kwargs):
            for i in range(retries):
                try:
                    return f(*args, **kwargs)
                except Exception as e:
                    time.sleep(delay)
                    print("Retrying {}, attempt ".format(f.__qualname__), i+1)
        return wrapper
    
    return decor
In [93]:
@with_retries(retries=3, delay=2)
def wget(url):
    resp = requests.get(url)
    return resp.text
In [94]:
wget("htpp://sdsdsdsa.dasddsad.ghgfyrt/dfdf")
Retrying wget, attempt  1
Retrying wget, attempt  2
Retrying wget, attempt  3
In [95]:
from functools import partial
In [96]:
def add(x, y, z):
    return x+y+z
In [97]:
add2 = partial(add, x=5)
In [99]:
add2(y=2, z=3)
Out[99]:
10
In [100]:
from functools import wraps

def with_retries(f = None, retries=5, delay=2):
    
        if f==None:
            return partial(with_retries, retries=retries, delay=delay)
        
        @wraps(f)
        def wrapper(*args, **kwargs):
            for i in range(retries):
                try:
                    return f(*args, **kwargs)
                except Exception as e:
                    time.sleep(delay)
                    print("Retrying {}, attempt ".format(f.__qualname__), i+1)
        return wrapper
    
    
In [101]:
@with_retries(retries=4, delay=1)
def downlod(url):
    raise Exception("Can't download")
In [102]:
downlod()
Retrying downlod, attempt  1
Retrying downlod, attempt  2
Retrying downlod, attempt  3
Retrying downlod, attempt  4
In [105]:
!grep --help
Usage: grep [OPTION]... PATTERN [FILE]...
Search for PATTERN in each FILE.
Example: grep -i 'hello world' menu.h main.c

Pattern selection and interpretation:
  -E, --extended-regexp     PATTERN is an extended regular expression
  -F, --fixed-strings       PATTERN is a set of newline-separated strings
  -G, --basic-regexp        PATTERN is a basic regular expression (default)
  -P, --perl-regexp         PATTERN is a Perl regular expression
  -e, --regexp=PATTERN      use PATTERN for matching
  -f, --file=FILE           obtain PATTERN from FILE
  -i, --ignore-case         ignore case distinctions
  -w, --word-regexp         force PATTERN to match only whole words
  -x, --line-regexp         force PATTERN to match only whole lines
  -z, --null-data           a data line ends in 0 byte, not newline

Miscellaneous:
  -s, --no-messages         suppress error messages
  -v, --invert-match        select non-matching lines
  -V, --version             display version information and exit
      --help                display this help text and exit

Output control:
  -m, --max-count=NUM       stop after NUM selected lines
  -b, --byte-offset         print the byte offset with output lines
  -n, --line-number         print line number with output lines
      --line-buffered       flush output on every line
  -H, --with-filename       print file name with output lines
  -h, --no-filename         suppress the file name prefix on output
      --label=LABEL         use LABEL as the standard input file name prefix
  -o, --only-matching       show only the part of a line matching PATTERN
  -q, --quiet, --silent     suppress all normal output
      --binary-files=TYPE   assume that binary files are TYPE;
                            TYPE is 'binary', 'text', or 'without-match'
  -a, --text                equivalent to --binary-files=text
  -I                        equivalent to --binary-files=without-match
  -d, --directories=ACTION  how to handle directories;
                            ACTION is 'read', 'recurse', or 'skip'
  -D, --devices=ACTION      how to handle devices, FIFOs and sockets;
                            ACTION is 'read' or 'skip'
  -r, --recursive           like --directories=recurse
  -R, --dereference-recursive  likewise, but follow all symlinks
      --include=FILE_PATTERN  search only files that match FILE_PATTERN
      --exclude=FILE_PATTERN  skip files and directories matching FILE_PATTERN
      --exclude-from=FILE   skip files matching any file pattern from FILE
      --exclude-dir=PATTERN  directories that match PATTERN will be skipped.
  -L, --files-without-match  print only names of FILEs with no selected lines
  -l, --files-with-matches  print only names of FILEs with selected lines
  -c, --count               print only a count of selected lines per FILE
  -T, --initial-tab         make tabs line up (if needed)
  -Z, --null                print 0 byte after FILE name

Context control:
  -B, --before-context=NUM  print NUM lines of leading context
  -A, --after-context=NUM   print NUM lines of trailing context
  -C, --context=NUM         print NUM lines of output context
  -NUM                      same as --context=NUM
      --color[=WHEN],
      --colour[=WHEN]       use markers to highlight the matching strings;
                            WHEN is 'always', 'never', or 'auto'
  -U, --binary              do not strip CR characters at EOL (MSDOS/Windows)

When FILE is '-', read standard input.  With no FILE, read '.' if
recursive, '-' otherwise.  With fewer than two FILEs, assume -h.
Exit status is 0 if any line is selected, 1 otherwise;
if any error occurs and -q is not given, the exit status is 2.

Report bugs to: bug-grep@gnu.org
GNU grep home page: <http://www.gnu.org/software/grep/>
General help using GNU software: <http://www.gnu.org/gethelp/>

Libraries that make use of decorators

-- click is library which makes use of decorators to build commandline tools

Profile

In [106]:
%%file task.py
import math
import time
import sys


def square(x):
    return x*x

def delay():
    time.sleep(1)

def compute_task(n):
    s = 0
    for i in range(n):
        s += square(i)
    print(s)
    
    s = 0
    for i in range(n):
        s += math.sqrt(i)
    print(s)
    
    for i in range(5):
        delay()
        
if __name__ == "__main__":
    compute_task(int(sys.argv[1]))
Writing task.py
In [109]:
!python -m cProfile task.py 10000000
333333283333335000000
21081849486.439312
         20000231 function calls in 8.845 seconds

   Ordered by: standard name

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
        1    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap>:103(release)
        1    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap>:143(__init__)
        1    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap>:147(__enter__)
        1    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap>:151(__exit__)
        1    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap>:157(_get_module_lock)
        1    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap>:176(cb)
        2    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap>:211(_call_with_frames_removed)
       19    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap>:222(_verbose_message)
        1    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap>:307(__init__)
        1    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap>:311(__enter__)
        1    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap>:318(__exit__)
        4    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap>:321(<genexpr>)
        1    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap>:369(__init__)
        1    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap>:403(cached)
        1    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap>:416(parent)
        1    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap>:424(has_location)
        1    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap>:504(_init_module_attrs)
        1    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap>:576(module_from_spec)
        1    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap>:58(__init__)
        1    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap>:663(_load_unlocked)
        1    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap>:719(find_spec)
        1    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap>:78(acquire)
        1    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap>:792(find_spec)
        3    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap>:855(__enter__)
        3    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap>:859(__exit__)
        1    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap>:882(_find_spec)
        1    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap>:948(_find_and_load_unlocked)
        1    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap>:978(_find_and_load)
        1    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap_external>:1029(__init__)
        1    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap_external>:1040(create_module)
        1    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap_external>:1048(exec_module)
        5    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap_external>:1203(_path_importer_cache)
        1    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap_external>:1240(_get_spec)
        1    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap_external>:1272(find_spec)
        1    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap_external>:1351(_get_spec)
        4    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap_external>:1356(find_spec)
        1    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap_external>:369(_get_cached)
        4    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap_external>:40(_relax_case)
       16    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap_external>:56(_path_join)
        1    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap_external>:574(spec_from_file_location)
       16    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap_external>:58(<listcomp>)
        5    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap_external>:74(_path_stat)
        1    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap_external>:84(_path_is_mode_type)
        1    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap_external>:93(_path_isfile)
        1    0.000    0.000    8.845    8.845 task.py:1(<module>)
        1    2.586    2.586    8.844    8.844 task.py:12(compute_task)
 10000000    0.641    0.000    0.641    0.000 task.py:6(square)
        5    0.000    0.000    5.006    1.001 task.py:9(delay)
        5    0.000    0.000    0.000    0.000 {built-in method _imp.acquire_lock}
        1    0.000    0.000    0.000    0.000 {built-in method _imp.create_dynamic}
        1    0.000    0.000    0.000    0.000 {built-in method _imp.exec_dynamic}
        1    0.000    0.000    0.000    0.000 {built-in method _imp.is_builtin}
        1    0.000    0.000    0.000    0.000 {built-in method _imp.is_frozen}
        5    0.000    0.000    0.000    0.000 {built-in method _imp.release_lock}
        2    0.000    0.000    0.000    0.000 {built-in method _thread.allocate_lock}
        2    0.000    0.000    0.000    0.000 {built-in method _thread.get_ident}
        1    0.000    0.000    0.000    0.000 {built-in method builtins.any}
        1    0.000    0.000    8.845    8.845 {built-in method builtins.exec}
        6    0.000    0.000    0.000    0.000 {built-in method builtins.getattr}
        6    0.000    0.000    0.000    0.000 {built-in method builtins.hasattr}
        5    0.000    0.000    0.000    0.000 {built-in method builtins.isinstance}
        2    0.000    0.000    0.000    0.000 {built-in method builtins.print}
 10000000    0.612    0.000    0.612    0.000 {built-in method math.sqrt}
        1    0.000    0.000    0.000    0.000 {built-in method posix.fspath}
        1    0.000    0.000    0.000    0.000 {built-in method posix.getcwd}
        5    0.000    0.000    0.000    0.000 {built-in method posix.stat}
        5    5.006    1.001    5.006    1.001 {built-in method time.sleep}
        1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}
        2    0.000    0.000    0.000    0.000 {method 'endswith' of 'str' objects}
        2    0.000    0.000    0.000    0.000 {method 'get' of 'dict' objects}
       16    0.000    0.000    0.000    0.000 {method 'join' of 'str' objects}
        6    0.000    0.000    0.000    0.000 {method 'rpartition' of 'str' objects}
       32    0.000    0.000    0.000    0.000 {method 'rstrip' of 'str' objects}


In [110]:
import task
In [111]:
import cProfile
In [112]:
cProfile.run("task.compute_task(10000)")
333283335000
666616.4591971082
         20076 function calls in 5.021 seconds

   Ordered by: standard name

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
        1    0.000    0.000    5.021    5.021 <string>:1(<module>)
        5    0.000    0.000    0.000    0.000 iostream.py:195(schedule)
        4    0.000    0.000    0.000    0.000 iostream.py:307(_is_master_process)
        4    0.000    0.000    0.000    0.000 iostream.py:320(_schedule_flush)
        4    0.000    0.000    0.000    0.000 iostream.py:382(write)
        5    0.000    0.000    0.000    0.000 iostream.py:93(_event_pipe)
        5    0.000    0.000    0.000    0.000 socket.py:337(send)
        1    0.010    0.010    5.021    5.021 task.py:12(compute_task)
    10000    0.002    0.000    0.002    0.000 task.py:6(square)
        5    0.000    0.000    5.006    1.001 task.py:9(delay)
        5    0.000    0.000    0.000    0.000 threading.py:1038(_wait_for_tstate_lock)
        5    0.000    0.000    0.000    0.000 threading.py:1080(is_alive)
        5    0.000    0.000    0.000    0.000 threading.py:507(is_set)
        1    0.000    0.000    5.021    5.021 {built-in method builtins.exec}
        4    0.000    0.000    0.000    0.000 {built-in method builtins.isinstance}
        2    0.000    0.000    0.000    0.000 {built-in method builtins.print}
    10000    0.002    0.000    0.002    0.000 {built-in method math.sqrt}
        4    0.000    0.000    0.000    0.000 {built-in method posix.getpid}
        5    5.006    1.001    5.006    1.001 {built-in method time.sleep}
        5    0.000    0.000    0.000    0.000 {method 'acquire' of '_thread.lock' objects}
        5    0.000    0.000    0.000    0.000 {method 'append' of 'collections.deque' objects}
        1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}


In [113]:
help(cProfile.run)
Help on function run in module cProfile:

run(statement, filename=None, sort=-1)
    Run statement under profiler optionally saving results in filename
    
    This function takes a single argument that can be passed to the
    "exec" statement, and an optional file name.  In all cases this
    routine attempts to "exec" its first argument and gather profiling
    statistics from the execution. If no file name is present, then this
    function automatically prints a simple profiling report, sorted by the
    standard name string (file/line/function-name) that is presented in
    each line.

In [114]:
cProfile.run("task.compute_task(10000)", sort=0)
333283335000
666616.4591971082
         20076 function calls in 5.013 seconds

   Ordered by: call count

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
    10000    0.001    0.000    0.001    0.000 task.py:6(square)
    10000    0.001    0.000    0.001    0.000 {built-in method math.sqrt}
        5    0.000    0.000    0.000    0.000 {method 'acquire' of '_thread.lock' objects}
        5    5.007    1.001    5.007    1.001 {built-in method time.sleep}
        5    0.000    0.000    0.000    0.000 {method 'append' of 'collections.deque' objects}
        5    0.000    0.000    5.007    1.001 task.py:9(delay)
        5    0.000    0.000    0.000    0.000 iostream.py:93(_event_pipe)
        5    0.000    0.000    0.000    0.000 iostream.py:195(schedule)
        5    0.000    0.000    0.000    0.000 socket.py:337(send)
        5    0.000    0.000    0.000    0.000 threading.py:507(is_set)
        5    0.000    0.000    0.000    0.000 threading.py:1038(_wait_for_tstate_lock)
        5    0.000    0.000    0.000    0.000 threading.py:1080(is_alive)
        4    0.000    0.000    0.000    0.000 {built-in method posix.getpid}
        4    0.000    0.000    0.000    0.000 {built-in method builtins.isinstance}
        4    0.000    0.000    0.000    0.000 iostream.py:307(_is_master_process)
        4    0.000    0.000    0.000    0.000 iostream.py:320(_schedule_flush)
        4    0.000    0.000    0.000    0.000 iostream.py:382(write)
        2    0.000    0.000    0.000    0.000 {built-in method builtins.print}
        1    0.000    0.000    5.013    5.013 {built-in method builtins.exec}
        1    0.004    0.004    5.013    5.013 task.py:12(compute_task)
        1    0.000    0.000    5.013    5.013 <string>:1(<module>)
        1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}


In [115]:
cProfile.run("task.compute_task(10000)", filename="task.prof")
333283335000
666616.4591971082
In [117]:
!tail -n 5 task.prof
i~�write)rrgg0)rr� <built-in method builtins.print>)�rg�h㈵��>gOʤ�6?{�r��compute_task)rrg�h㈵��>gOʤ�6?0)r��square)i'i'g��NM?g��NM?{r)i'i'g��NM?g��NM?0)rr	r
rr)rrghUMu�>gC�8
i~r)rrgfL�g��>g1]��a?{)rrr)rrgfL�g��>g1]��a?0)�G/home/vikrant/anaconda3/lib/python3.7/site-packages/zmq/sugar/socket.pyiQ�send)rrg!>��@?g!>��@?{r)rrg!>��@?g!>��@?0)rr�0<method 'disable' of '_lsprof.Profiler' objects>)rrg�����ư>g�����ư>{0)ri��is_set)rrg�����ư>g�����ư>{�ri8�is_alive)rrg�����ư>g�����ư>0)rir)rrg�h㈵��>g�h㈵��>{r()rrg�h㈵��>g�h㈵��>0)ri8r))rrg�h㈵��>g�������>{r)rrg�h㈵��>g�������>0)rr�<built-in method math.sqrt>)i'i'g�_>Y1\M?g�_>Y1\M?{r)i'i'g�_>Y1\M?g�_>Y1\M?00
In [118]:
!python -m pdb task.py
> /home/vikrant/trainings/2019/arcesium_advanced_sep/task.py(1)<module>()
-> import math
(Pdb) 
--KeyboardInterrupt--
(Pdb) 

Database

In [119]:
import sqlite3
In [120]:
conn = sqlite3.connect("data.db") 
In [121]:
help(sqlite3.connect)
Help on built-in function connect in module _sqlite3:

connect(...)
    connect(database[, timeout, detect_types, isolation_level,
            check_same_thread, factory, cached_statements, uri])
    
    Opens a connection to the SQLite database file *database*. You can use
    ":memory:" to open a database connection to a database that resides in
    RAM instead of on disk.

In [122]:
cur = conn.cursor()
In [123]:
cur.execute("create table person (name varchar(100), email varchar(100))")
Out[123]:
<sqlite3.Cursor at 0x7f2ddc1c3ab0>
In [124]:
cur.execute("insert into person (name, email) values('alice', 'alice@wonder.land')")
Out[124]:
<sqlite3.Cursor at 0x7f2ddc1c3ab0>
In [125]:
cur = cur.execute("select * from person")
In [126]:
cur.fetchall()
Out[126]:
[('alice', 'alice@wonder.land')]
In [144]:
def find_person_(conn, email):
    ## not a good way
    q = "select * from person where email={}".format(email)
    cur = conn.cursor()
    return cur.execute(q).fetchall()
In [130]:
find_person(conn, "alice@wonder.land")
Out[130]:
[('alice', 'alice@wonder.land')]
In [132]:
def find_person(conn, email):
    q = "select * from person where email=?"
    cur = conn.cursor()
    return cur.execute(q, (email, )).fetchall()
In [133]:
find_person(conn, "alice@wonder.land")
Out[133]:
[('alice', 'alice@wonder.land')]
In [134]:
conn.commit()
In [135]:
conn.close()
In [136]:
conn = sqlite3.connect("data.db")
In [137]:
find_person(conn, "*")
Out[137]:
[]
In [146]:
find_person_(conn, "'alice' or 1=1")
Out[146]:
[('alice', 'alice@wonder.land')]
In [147]:
find_person(conn, "'alice' or 1=1")
Out[147]:
[]
In [148]:
records = [
    ("alex", "alex@zoo.us"),
    ("Elsa", "elsa@frozen"),
    ("Elisa", "elisa@hacker.com")
]
In [149]:
cur = conn.cursor()
In [150]:
cur.executemany("insert into person values(?, ?)", records)
Out[150]:
<sqlite3.Cursor at 0x7f2ddc6e9a40>
In [152]:
cur.execute("select * from person").fetchall()
Out[152]:
[('alice', 'alice@wonder.land'),
 ('alex', 'alex@zoo.us'),
 ('Elsa', 'elsa@frozen'),
 ('Elisa', 'elisa@hacker.com')]
In [ ]: