Advanced Python Training at Arcesium - Day 1

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

Day 1 | Day 2 | Day 3

We will be using python 3.7 from anaconda for this training. You can download it from

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

Contents of this course

  • Generators
  • Decorators
  • Debugging
  • Classes in detail
  • Descriptors
  • Databases & SQLAlchemy
  • XML & JSON
  • Profiling
  • Numpy
  • Pandas
  • Matplotlib
  • C-extensions, cython and numba
  • Multi-threading

Warmup

In [1]:
primes = [2, 3, 5, 7, 11, 13, 17]
In [2]:
primes[0]
Out[2]:
2
In [3]:
primes[-1]
Out[3]:
17
In [5]:
primes[2:5] # start at 2nd index and stop at 5th index (exlude 5th)
Out[5]:
[5, 7, 11]
In [7]:
primes[1:5:2]#start at 1 end at 5 at interval of 2
Out[7]:
[3, 7]
In [8]:
primes[:3] # start at zero end at 3 ...... take first 3
Out[8]:
[2, 3, 5]
In [10]:
primes[3:] # drop first 3
Out[10]:
[7, 11, 13, 17]
In [11]:
primes[:] # copy
Out[11]:
[2, 3, 5, 7, 11, 13, 17]
In [12]:
primes[::2]
Out[12]:
[2, 5, 11, 17]
In [13]:
primes[::-1]
Out[13]:
[17, 13, 11, 7, 5, 3, 2]
In [15]:
def is_palindrome(word):
    return word == word[::-1]
In [16]:
is_palindrome("hello")
Out[16]:
False
In [18]:
is_palindrome("madam")
Out[18]:
True
In [19]:
table = [[1,1,1,],[2,2,2],[3,3,3]]
In [20]:
table[0]
Out[20]:
[1, 1, 1]
In [21]:
table[-1]
Out[21]:
[3, 3, 3]

We want to access column instead of row!

In [22]:
for p in primes:
    print(p*p)
4
9
25
49
121
169
289
In [23]:
sqrs = []
for p in primes:
    sqrs.append(p*p)
print(sqrs)
[4, 9, 25, 49, 121, 169, 289]
In [24]:
[p*p for p in primes]
Out[24]:
[4, 9, 25, 49, 121, 169, 289]
In [25]:
nums = list(range(10))
In [26]:
nums
Out[26]:
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
In [27]:
evens = [e for e in nums if e%2==0]
In [28]:
evens
Out[28]:
[0, 2, 4, 6, 8]

Lets get back to column access!

In [29]:
table
Out[29]:
[[1, 1, 1], [2, 2, 2], [3, 3, 3]]
In [30]:
table[0]
Out[30]:
[1, 1, 1]
In [31]:
table[0][0]
Out[31]:
1
In [32]:
table[1][0]
Out[32]:
2
In [33]:
table[2][0]
Out[33]:
3
In [36]:
def column(datamatrix, colnum):
    rowcount = len(datamatrix)
    return [datamatrix[r][colnum] for r in range(rowcount)]
In [37]:
column(table, 0)
Out[37]:
[1, 2, 3]

problem

  • Write a function transpose which makes a transpose of 2d matrix (given as list of lists)
In [38]:
def transpose(datamatrix):
    colcount = len(datamatrix[0])
    return [column(datamatrix, c) for c in range(colcount)]
In [39]:
transpose(table)
Out[39]:
[[1, 2, 3], [1, 2, 3], [1, 2, 3]]
In [40]:
list(range(5))
Out[40]:
[0, 1, 2, 3, 4]

Writing/Reading files

In [42]:
%%file data.csv
A1,A2,A3
B1,B2,B3
C1,C2,C3
Writing data.csv
In [43]:
def csvparser(filename):
    with open(filename) as f:
        return [line.strip().split(",") for line in f]
            
In [44]:
csvparser("data.csv")
Out[44]:
[['A1', 'A2', 'A3'], ['B1', 'B2', 'B3'], ['C1', 'C2', 'C3']]
In [45]:
def parseints(line):
    return [int(item) for item in line.strip().split(",")]
In [46]:
def csvparseints(filename):
    with open(filename) as f:
        return [parseints(line) for line in f]
In [47]:
%%file numeric.csv
1,2,3
4,5,6
7,8,9
Writing numeric.csv
In [48]:
csvparseints("numeric.csv")
Out[48]:
[[1, 2, 3], [4, 5, 6], [7, 8, 9]]

Example - quicksort

In [52]:
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)
In [53]:
quick([23,45,1,6,23,6,2])
Out[53]:
[1, 2, 6, 6, 23, 23, 45]

Functions

Positional arguments

In [54]:
def cylinder_volume(radius, height):
    return 3.14*radius**2*height
In [55]:
cylinder_volume(1.0, 11)
Out[55]:
34.54
In [56]:
cylinder_volume(11, 1.0)
Out[56]:
379.94

Named arguments

In [57]:
cylinder_volume(radius=1.0, height=11)
Out[57]:
34.54
In [58]:
cylinder_volume(height=11, radius=1.0)
Out[58]:
34.54
In [59]:
cylinder_volume(1.0, height=11)
Out[59]:
34.54
In [60]:
cylinder_volume(height=11, 1.0)
  File "<ipython-input-60-046a0059a92d>", line 1
    cylinder_volume(height=11, 1.0)
                              ^
SyntaxError: positional argument follows keyword argument
In [61]:
cylinder_volume(radius=1.0, 11.0)
  File "<ipython-input-61-8fdd8358dea2>", line 1
    cylinder_volume(radius=1.0, 11.0)
                               ^
SyntaxError: positional argument follows keyword argument

default arguments

In [62]:
def cylinder_volume(radius=1.0, height=1.0):
    return 3.14*radius**2*height
In [63]:
cylinder_volume()
Out[63]:
3.14
In [64]:
cylinder_volume(radius=5.0)
Out[64]:
78.5
In [65]:
cylinder_volume(1.0)
Out[65]:
3.14
In [66]:
cylinder_volume(1.0, 5.0)
Out[66]:
15.700000000000001
In [67]:
cylinder_volume(radius=2.0, 10)
  File "<ipython-input-67-56af28ff5421>", line 1
    cylinder_volume(radius=2.0, 10)
                               ^
SyntaxError: positional argument follows keyword argument
In [68]:
import random

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

for i in range(5):
    print(jitter(i))
0.027572360234280557
1.0275723602342806
2.027572360234281
3.027572360234281
4.027572360234281
In [69]:
def jitter(value, dev=None):
    if dev == None:
        dev = random.random()
    return value + dev
In [70]:
for i in range(5):
    print(jitter(i))
0.5293625949084335
1.9631153416070162
2.5074477661543626
3.623000552773116
4.667416143808197
In [71]:
jitter(5, 0.6)
Out[71]:
5.6

Default arguments are initialised only once, at the time of defination

Do not use mutable datatype as default arguments

In [72]:
def extend(data, default=[]):
    default.extend(data)
    return default
In [73]:
extend([2,3,4])
Out[73]:
[2, 3, 4]
In [74]:
extend([3,4,5])
Out[74]:
[2, 3, 4, 3, 4, 5]
In [75]:
def extend1(data, default=None):
    if not default:
        default = []
    default.extend(data)
    return default
In [76]:
extend1([1,1,1], [2,2,2])
Out[76]:
[2, 2, 2, 1, 1, 1]
In [77]:
extend1([1,1,1])
Out[77]:
[1, 1, 1]

Named only arguments

In [78]:
word = ["one", "two", "three", "four", "five"]
In [79]:
max(word)
Out[79]:
'two'
In [80]:
max(word, key=len)
Out[80]:
'three'
In [81]:
max(word, len)
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-81-90c21b1ac872> in <module>
----> 1 max(word, len)

TypeError: '>' not supported between instances of 'builtin_function_or_method' and 'list'
In [82]:
def foobar(x, y, *, name="bar"):
    pass
In [83]:
foobar(2,3)
In [84]:
foobar(2,3,"foo")
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-84-8e92004e62dd> in <module>
----> 1 foobar(2,3,"foo")

TypeError: foobar() takes 2 positional arguments but 3 were given
In [85]:
foobar(3,4, name="foo")
In [86]:
def foo(a, b, *, name="foo", surname="bar"):
    pass
In [87]:
foo(1,2)
In [88]:
foo(1,2,name="abc", surname="xyz")
In [89]:
foo(1, 2, "abc")
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-89-c5fcd76c08ea> in <module>
----> 1 foo(1, 2, "abc")

TypeError: foo() takes 2 positional arguments but 3 were given

variable number of number arguments

In [90]:
import os
In [91]:
os.path.join("/", "home", "vikrant")
Out[91]:
'/home/vikrant'
In [92]:
os.path.join("/","home","vikrant","trainings","2020")
Out[92]:
'/home/vikrant/trainings/2020'
In [93]:
def myjoin(*args): # * is for variable number of pisitional arguments
    sep = "_"
    return sep.join(args)

    
In [94]:
myjoin("This","is","variable","number")
Out[94]:
'This_is_variable_number'
In [95]:
myjoin("of","arguments")
Out[95]:
'of_arguments'
In [96]:
def func(*args):
    print(args)
In [97]:
func(1,2,3)
(1, 2, 3)
In [98]:
func([1,2,3])
([1, 2, 3],)
In [99]:
func()
()
In [100]:
def pizza(bread, cheez, *toppings):
    print("bread", bread)
    print("cheez", cheez)
    print("toppings", toppings)
In [101]:
pizza("brown","cottage", "papper", "chilli")
bread brown
cheez cottage
toppings ('papper', 'chilli')
In [102]:
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
In [103]:
make_account(0, "vikrant", address="Maharashtra", phone="90909323092")
Out[103]:
{'balance': 0,
 'name': 'vikrant',
 'address': 'Maharashtra',
 'phone': '90909323092'}
In [104]:
def mysum(*args):
    s = 0
    for item in args:
        s += item
    return s
In [105]:
mysum(1, 2, 3,4)
Out[105]:
10
In [106]:
numbers = [1,1,2,3,5,8,13
          ]
In [107]:
mysum(numbers)
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-107-4532ebed166b> in <module>
----> 1 mysum(numbers)

<ipython-input-104-f6b73201251d> in mysum(*args)
      2     s = 0
      3     for item in args:
----> 4         s += item
      5     return s

TypeError: unsupported operand type(s) for +=: 'int' and 'list'
In [108]:
mysum(*numbers)
Out[108]:
33

problem

  • Make use of unpacking of arguments and zip function to find transpose of a 2d data
In [109]:
table
Out[109]:
[[1, 1, 1], [2, 2, 2], [3, 3, 3]]
In [111]:
list(zip([1,2,3],['a','b','c']))
Out[111]:
[(1, 'a'), (2, 'b'), (3, 'c')]
In [112]:
list(zip(*table))
Out[112]:
[(1, 2, 3), (1, 2, 3), (1, 2, 3)]
In [113]:
transpose(table)
Out[113]:
[[1, 2, 3], [1, 2, 3], [1, 2, 3]]
In [114]:
mysum(*numbers)
Out[114]:
33
zip(*table)  ---> zip([1,1,1], [2,2,2], [3,3,3])

Functions that return function

In [115]:
def make_adder(x):
    
    def adder(y):
        return x+y
    
    return adder
In [116]:
adder5 = make_adder(5)
In [117]:
adder5
Out[117]:
<function __main__.make_adder.<locals>.adder(y)>
In [118]:
adder5(7)
Out[118]:
12
In [122]:
def make_logger(type_):
    
    def logger(msg):
        print(type_.upper(), ":", msg)

    return logger
In [123]:
info = make_logger("info")
warning = make_logger("warn")
error = make_logger("error")
In [124]:
info("This is for your information")
INFO : This is for your information
In [125]:
warning("Something might be wrong!")
WARN : Something might be wrong!
In [126]:
error("Definately..somethng went wrong !")
ERROR : Definately..somethng went wrong !

Functions which take functions as argument

In [127]:
max(word, key=len)
Out[127]:
'three'
In [128]:
sorted(word, key=len)
Out[128]:
['one', 'two', 'four', 'five', 'three']
In [129]:
records = [
    ("HCL", 500.0, 10.0),
    ("INFY", 800.0, 8.0),
    ("RELIANCE", 245, 3.5),
    ("BOSCH", 540, -5.0)
]
In [130]:
def getprice(r):
    return r[1]
max(records, key=getprice)
Out[130]:
('INFY', 800.0, 8.0)
In [131]:
def getgain(r):
    return r[2]
In [132]:
max(records, key=getgain)
Out[132]:
('HCL', 500.0, 10.0)
In [133]:
max(records, key=lambda r:r[1])
Out[133]:
('INFY', 800.0, 8.0)
In [134]:
add = lambda x, y : x+y
In [135]:
add(3.4)
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-135-e15131b1b010> in <module>
----> 1 add(3.4)

TypeError: <lambda>() missing 1 required positional argument: 'y'
In [136]:
add(3,4)
Out[136]:
7
In [137]:
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
In [139]:
def sumof(f, a, b):
    return f(a) + f(b)
In [140]:
sumof(square, 6, 8)
Out[140]:
100
In [141]:
sumofsquares(6, 8)
Out[141]:
100
In [142]:
def sumofterms(term, start, end):
    s = 0
    for n in range(start, end):
        s += term(n)
    return s
In [143]:
sumofterms(lambda x:x , 1, 101)
Out[143]:
5050

problems

  • Write a function 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
  • 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
  • make use of sumofterms to sum pi series 8/1.3 + 8/5.7 + 8/9.11 .....
In [144]:
def compose(f, g):
    
    def fg(value):
        return f(g(value))
    
    return fg
In [146]:
fg = compose(lambda x:2*x, lambda x: 2*x+1)
In [147]:
fg(3)
Out[147]:
14
In [148]:
def make_poly(coeff):
    
    def poly(x):
        s = 0
        for i, c in enumerate(coeff):
            s += c*x**i
        return s
    return poly
In [149]:
P2 = make_poly([1, 2, 1])
In [150]:
P2(3)
Out[150]:
16
In [151]:
foo = lambda x, f: f(x)
In [152]:
foo(2, adder5)
Out[152]:
7
In [153]:
def compose(f, g):
    return lambda x: f(g(x))

decorators

In [157]:
def add(a, b):
    return a+b
In [158]:
def add_(a, b):
    print("Begin add", a, b)
    r = a+b
    print("End add", r)
    return r
In [156]:
add(4, 5)
Begin add 4 5
End add 9
Out[156]:
9
In [159]:
add(3, 4)
Out[159]:
7
In [160]:
add_(3,4)
Begin add 3 4
End add 7
Out[160]:
7
In [161]:
add = add_
In [162]:
def sumofsquares(a, b):
    return add(square(a), square(b))
In [163]:
sumofsquares(3, 4)
Begin add 9 16
End add 25
Out[163]:
25
In [164]:
def debug(f):
    
    def wrapper(*args):
        print("Before", args)
        r = f(*args)
        print("After", r)
        return r
    return wrapper
In [165]:
def cube(x):
    return x**3
In [166]:
cube(4)
Out[166]:
64
In [167]:
def sumofcubes(x, y):
    return cube(x) + cube(y)
In [168]:
cube = debug(cube)
In [169]:
sumofcubes(5,6)
Before (5,)
After 125
Before (6,)
After 216
Out[169]:
341
In [171]:
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)
In [172]:
sumofcubes(4, 6)
Before  cube (4,)
After cube 64
Before  cube (6,)
After cube 216
Out[172]:
280
In [173]:
def square(x):
    return x*x

def somemathsfunc(x, y):
    return square(x)*cube(y) + cube(x+y)
In [174]:
square = debug(square)
In [175]:
somemathsfunc(5, 8)
Before  square (5,)
After square 25
Before  cube (8,)
After cube 512
Before  cube (13,)
After cube 2197
Out[175]:
14997
In [177]:
@debug
def add(a,b):
    return a+b
# add = debug(add)
In [178]:
(1, "string", [], {}, func)
Out[178]:
(1, 'string', [], {}, <function __main__.func(*args)>)
In [179]:
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
In [180]:
add.__name__
Out[180]:
'wrapper'
In [181]:
add.__qualname__
Out[181]:
'debug.<locals>.wrapper'
In [182]:
def foo():
    pass
In [183]:
foo.__name__
Out[183]:
'foo'
In [184]:
foo.__qualname__
Out[184]:
'foo'
In [185]:
def foo():
    """
    Help for foo
    """
    pass
In [186]:
help(foo)
Help on function foo in module __main__:

foo()
    Help for foo

In [187]:
help(add)
Help on function wrapper in module __main__:

wrapper(*args)

In [189]:
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
In [190]:
import math
In [ ]:
 
In [194]:
dir(math) ## to find attributes of given object
Out[194]:
['__doc__',
 '__file__',
 '__loader__',
 '__name__',
 '__package__',
 '__spec__',
 'acos',
 'acosh',
 'asin',
 'asinh',
 'atan',
 'atan2',
 'atanh',
 'ceil',
 'copysign',
 'cos',
 'cosh',
 'degrees',
 'e',
 'erf',
 'erfc',
 'exp',
 'expm1',
 'fabs',
 'factorial',
 'floor',
 'fmod',
 'frexp',
 'fsum',
 'gamma',
 'gcd',
 'hypot',
 'inf',
 'isclose',
 'isfinite',
 'isinf',
 'isnan',
 'ldexp',
 'lgamma',
 'log',
 'log10',
 'log1p',
 'log2',
 'modf',
 'nan',
 'pi',
 'pow',
 'radians',
 'remainder',
 'sin',
 'sinh',
 'sqrt',
 'tan',
 'tanh',
 'tau',
 'trunc']
In [195]:
callable(foo) # to chek whether given object is function/class
Out[195]:
True
In [193]:
callable(4)
Out[193]:
False
In [197]:
@debugdecor
def square(x):
    "computes squares"
    return x*x
In [198]:
help(square)
Help on function square in module __main__:

square(x)
    computes squares

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 [199]:
import time
In [200]:
time.time()
Out[200]:
1581936706.397387
In [201]:
import requests
In [202]:
def downlaod(url):
    resp = requests.get(url)
    return resp.text
In [204]:
downlaod("http://httpbin.org/htm")
Out[204]:
'<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">\n<title>404 Not Found</title>\n<h1>Not Found</h1>\n<p>The requested URL was not found on the server.  If you entered the URL manually please check your spelling and try again.</p>\n'
In [205]:
downlaod("http://nosuchurl.sdsd/sakjhdl/sfdkjhdsfk")
---------------------------------------------------------------------------
gaierror                                  Traceback (most recent call last)
~/anaconda3/lib/python3.7/site-packages/urllib3/connection.py in _new_conn(self)
    158             conn = connection.create_connection(
--> 159                 (self._dns_host, self.port), self.timeout, **extra_kw)
    160 

~/anaconda3/lib/python3.7/site-packages/urllib3/util/connection.py in create_connection(address, timeout, source_address, socket_options)
     56 
---> 57     for res in socket.getaddrinfo(host, port, family, socket.SOCK_STREAM):
     58         af, socktype, proto, canonname, sa = res

~/anaconda3/lib/python3.7/socket.py in getaddrinfo(host, port, family, type, proto, flags)
    747     addrlist = []
--> 748     for res in _socket.getaddrinfo(host, port, family, type, proto, flags):
    749         af, socktype, proto, canonname, sa = res

gaierror: [Errno -2] Name or service not known

During handling of the above exception, another exception occurred:

NewConnectionError                        Traceback (most recent call last)
~/anaconda3/lib/python3.7/site-packages/urllib3/connectionpool.py in urlopen(self, method, url, body, headers, retries, redirect, assert_same_host, timeout, pool_timeout, release_conn, chunked, body_pos, **response_kw)
    599                                                   body=body, headers=headers,
--> 600                                                   chunked=chunked)
    601 

~/anaconda3/lib/python3.7/site-packages/urllib3/connectionpool.py in _make_request(self, conn, method, url, timeout, chunked, **httplib_request_kw)
    353         else:
--> 354             conn.request(method, url, **httplib_request_kw)
    355 

~/anaconda3/lib/python3.7/http/client.py in request(self, method, url, body, headers, encode_chunked)
   1228         """Send a complete request to the server."""
-> 1229         self._send_request(method, url, body, headers, encode_chunked)
   1230 

~/anaconda3/lib/python3.7/http/client.py in _send_request(self, method, url, body, headers, encode_chunked)
   1274             body = _encode(body, 'body')
-> 1275         self.endheaders(body, encode_chunked=encode_chunked)
   1276 

~/anaconda3/lib/python3.7/http/client.py in endheaders(self, message_body, encode_chunked)
   1223             raise CannotSendHeader()
-> 1224         self._send_output(message_body, encode_chunked=encode_chunked)
   1225 

~/anaconda3/lib/python3.7/http/client.py in _send_output(self, message_body, encode_chunked)
   1015         del self._buffer[:]
-> 1016         self.send(msg)
   1017 

~/anaconda3/lib/python3.7/http/client.py in send(self, data)
    955             if self.auto_open:
--> 956                 self.connect()
    957             else:

~/anaconda3/lib/python3.7/site-packages/urllib3/connection.py in connect(self)
    180     def connect(self):
--> 181         conn = self._new_conn()
    182         self._prepare_conn(conn)

~/anaconda3/lib/python3.7/site-packages/urllib3/connection.py in _new_conn(self)
    167             raise NewConnectionError(
--> 168                 self, "Failed to establish a new connection: %s" % e)
    169 

NewConnectionError: <urllib3.connection.HTTPConnection object at 0x7fd3906aa588>: Failed to establish a new connection: [Errno -2] Name or service not known

During handling of the above exception, another exception occurred:

MaxRetryError                             Traceback (most recent call last)
~/anaconda3/lib/python3.7/site-packages/requests/adapters.py in send(self, request, stream, timeout, verify, cert, proxies)
    448                     retries=self.max_retries,
--> 449                     timeout=timeout
    450                 )

~/anaconda3/lib/python3.7/site-packages/urllib3/connectionpool.py in urlopen(self, method, url, body, headers, retries, redirect, assert_same_host, timeout, pool_timeout, release_conn, chunked, body_pos, **response_kw)
    637             retries = retries.increment(method, url, error=e, _pool=self,
--> 638                                         _stacktrace=sys.exc_info()[2])
    639             retries.sleep()

~/anaconda3/lib/python3.7/site-packages/urllib3/util/retry.py in increment(self, method, url, response, error, _pool, _stacktrace)
    398         if new_retry.is_exhausted():
--> 399             raise MaxRetryError(_pool, url, error or ResponseError(cause))
    400 

MaxRetryError: HTTPConnectionPool(host='nosuchurl.sdsd', port=80): Max retries exceeded with url: /sakjhdl/sfdkjhdsfk (Caused by NewConnectionError('<urllib3.connection.HTTPConnection object at 0x7fd3906aa588>: Failed to establish a new connection: [Errno -2] Name or service not known'))

During handling of the above exception, another exception occurred:

ConnectionError                           Traceback (most recent call last)
<ipython-input-205-27f3e8caf312> in <module>
----> 1 downlaod("http://nosuchurl.sdsd/sakjhdl/sfdkjhdsfk")

<ipython-input-202-51abdf812d37> in downlaod(url)
      1 def downlaod(url):
----> 2     resp = requests.get(url)
      3     return resp.text

~/anaconda3/lib/python3.7/site-packages/requests/api.py in get(url, params, **kwargs)
     73 
     74     kwargs.setdefault('allow_redirects', True)
---> 75     return request('get', url, params=params, **kwargs)
     76 
     77 

~/anaconda3/lib/python3.7/site-packages/requests/api.py in request(method, url, **kwargs)
     58     # cases, and look like a memory leak in others.
     59     with sessions.Session() as session:
---> 60         return session.request(method=method, url=url, **kwargs)
     61 
     62 

~/anaconda3/lib/python3.7/site-packages/requests/sessions.py in request(self, method, url, params, data, headers, cookies, files, auth, timeout, allow_redirects, proxies, hooks, stream, verify, cert, json)
    531         }
    532         send_kwargs.update(settings)
--> 533         resp = self.send(prep, **send_kwargs)
    534 
    535         return resp

~/anaconda3/lib/python3.7/site-packages/requests/sessions.py in send(self, request, **kwargs)
    644 
    645         # Send the request
--> 646         r = adapter.send(request, **kwargs)
    647 
    648         # Total elapsed time of the request (approximately)

~/anaconda3/lib/python3.7/site-packages/requests/adapters.py in send(self, request, stream, timeout, verify, cert, proxies)
    514                 raise SSLError(e, request=request)
    515 
--> 516             raise ConnectionError(e, request=request)
    517 
    518         except ClosedPoolError as e:

ConnectionError: HTTPConnectionPool(host='nosuchurl.sdsd', port=80): Max retries exceeded with url: /sakjhdl/sfdkjhdsfk (Caused by NewConnectionError('<urllib3.connection.HTTPConnection object at 0x7fd3906aa588>: Failed to establish a new connection: [Errno -2] Name or service not known'))
In [206]:
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
In [208]:
hello()
Deprecation warning: hello is deprecated!
In [216]:
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
In [217]:
@with_retries
def wget(url):
    return requests.get(url).text
In [218]:
wget("http://nosuchurl/skdj/jsdj")
Retrying wget .. 1
Retrying wget .. 2
Retrying wget .. 3
Retrying wget .. 4
Retrying wget .. 5
Giving up!
In [219]:
def foo():
    pass
In [220]:
foo
Out[220]:
<function __main__.foo()>

decorators with parameters

@with_retries(tries, delay)
def wget(url):
    pass
In [222]:
foo
foo()
In [233]:
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
In [234]:
@with_retries(5, 0.5)
def wget(url):
    return requests.get(url).text
In [235]:
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
In [236]:
@with_retries()
def wget(url):
    return requests.get(url).text
In [237]:
from functools import partial
In [238]:
def add(x, y):
    return x+y
In [239]:
f = partial(add, 2)
In [240]:
f
Out[240]:
functools.partial(<function add at 0x7fd383b6b510>, 2)
In [232]:
f(3)
Out[232]:
5

Example - trace

In [241]:
%%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
Writing trace.py
In [242]:
import trace
In [243]:
@trace.trace
def square(a):
    return a*a

@trace.trace
def sumofsquares(x, y):
    return square(x) + square(y)
In [244]:
sumofsquares(5,6)
|--sumofsquares (5, 6)
| |--square (5,)
| |--return 25
| |--square (6,)
| |--return 36
|--return 61
Out[244]:
61
In [245]:
%%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])))
Writing fib.py
In [246]:
!python fib.py 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
5
In [247]:
!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 [248]:
%%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
Writing memoize.py
In [251]:
%%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 [252]:
!python fib1.py 5
|--fib (5,)
| |--fib (4,)
| | |--fib (3,)
| | | |--fib (2,)
| | | |--return 1
| | | |--fib (1,)
| | | |--return 1
| | |--return 2
| |--return 3
|--return 5
5
In [254]:
!python fib1.py 8
|--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
21
In [257]:
!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 [258]:
!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/>

click is python library which makes use of decorators to make commandline applications

profiling

In [259]:
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)
In [260]:
from cProfile import Profile
In [261]:
profiler = Profile()
profiler.runcall(test)

from pstats import Stats
stats = Stats(profiler)
stats.sort_stats("cumulative")
stats.print_stats()
         20003 function calls in 1.154 seconds

   Ordered by: cumulative time

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
        1    0.000    0.000    1.154    1.154 <ipython-input-259-8fc96669f48f>:18(<lambda>)
        1    0.003    0.003    1.154    1.154 <ipython-input-259-8fc96669f48f>:1(insertion_sort)
    10000    1.134    0.000    1.152    0.000 <ipython-input-259-8fc96669f48f>:8(insert_value)
     9984    0.017    0.000    0.017    0.000 {method 'insert' of 'list' objects}
       16    0.000    0.000    0.000    0.000 {method 'append' of 'list' objects}
        1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}


Out[261]:
<pstats.Stats at 0x7fd383afe320>
In [262]:
from bisect import bisect_left

def insert_value(array, value):
    i = bisect_left(array, value)
    array.insert(i, value)
In [263]:
profiler = Profile()
profiler.runcall(test)

from pstats import Stats
stats = Stats(profiler)
stats.sort_stats("cumulative")
stats.print_stats()
         30003 function calls in 0.034 seconds

   Ordered by: cumulative time

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
        1    0.000    0.000    0.034    0.034 <ipython-input-259-8fc96669f48f>:18(<lambda>)
        1    0.002    0.002    0.034    0.034 <ipython-input-259-8fc96669f48f>:1(insertion_sort)
    10000    0.004    0.000    0.032    0.000 <ipython-input-262-9e3ac1f6182f>:3(insert_value)
    10000    0.021    0.000    0.021    0.000 {method 'insert' of 'list' objects}
    10000    0.007    0.000    0.007    0.000 {built-in method _bisect.bisect_left}
        1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}


Out[263]:
<pstats.Stats at 0x7fd390d0c518>

Finding number of calls

In [264]:
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()
In [265]:
profiler = Profile()
profiler.runcall(main_program)
stats = Stats(profiler)
stats.print_stats()
stats.print_callers()
         20242 function calls in 0.005 seconds

   Random listing order was used

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
       20    0.004    0.000    0.005    0.000 <ipython-input-264-f304b95a54a2>:4(foo)
        1    0.000    0.000    0.005    0.005 <ipython-input-264-f304b95a54a2>:11(main_program)
       20    0.000    0.000    0.000    0.000 <ipython-input-264-f304b95a54a2>:8(bar)
    20200    0.001    0.000    0.001    0.000 <ipython-input-264-f304b95a54a2>:1(my_utility)
        1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}


   Random listing order was used

Function                                           was called by...
                                                       ncalls  tottime  cumtime
<ipython-input-264-f304b95a54a2>:4(foo)            <-      20    0.004    0.005  <ipython-input-264-f304b95a54a2>:11(main_program)
<ipython-input-264-f304b95a54a2>:11(main_program)  <- 
<ipython-input-264-f304b95a54a2>:8(bar)            <-      20    0.000    0.000  <ipython-input-264-f304b95a54a2>:11(main_program)
<ipython-input-264-f304b95a54a2>:1(my_utility)     <-   20000    0.001    0.001  <ipython-input-264-f304b95a54a2>:4(foo)
                                                          200    0.000    0.000  <ipython-input-264-f304b95a54a2>:8(bar)
{method 'disable' of '_lsprof.Profiler' objects}   <- 


Out[265]:
<pstats.Stats at 0x7fd383477358>
In [266]:
import math
In [267]:
math.pi
Out[267]:
3.141592653589793
In [268]:
math.sin
Out[268]:
<function math.sin(x, /)>
In [269]:
from math import sin
In [270]:
sin
Out[270]:
<function math.sin(x, /)>
  • For optimizing code consider removing attribute access
  • local is faster than global
In [272]:
%%timeit
s = 0
for i in range(10000):
    for j in range(1000):
        s = s + math.sin(i+j*math.pi/2)
2.35 s ± 31.2 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
In [273]:
%%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)
1.53 s ± 34.6 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

debugging

In [275]:
%%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()
    
Overwriting looping.py
In [ ]:
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()
--Return--
> <ipython-input-277-67377c2c30bc>(11)<module>()->None
-> set_trace()
(Pdb) l
  6  	        print(number)
  7  	        for s in strvalues:
  8  	            print(s)
  9  	
 10  	from pdb import set_trace
 11  ->	set_trace()
 12  	loop()
[EOF]
(Pdb) b 6
Breakpoint 1 at <ipython-input-277-67377c2c30bc>:6
(Pdb) n
> /home/vikrant/anaconda3/lib/python3.7/site-packages/IPython/core/interactiveshell.py(3328)run_code()
-> sys.excepthook = old_excepthook
(Pdb) r
Internal StopIteration: False
> /home/vikrant/anaconda3/lib/python3.7/site-packages/IPython/core/interactiveshell.py(3248)run_ast_nodes()
-> if (await self.run_code(code, result,  async_=asy)):
(Pdb) step
> /home/vikrant/anaconda3/lib/python3.7/site-packages/IPython/core/interactiveshell.py(3240)run_ast_nodes()
-> for node,mode in to_run:
(Pdb) r
> <ipython-input-277-67377c2c30bc>(6)loop()
-> print(number)

Basic commands of interactive debugger

? - > 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

In [ ]: