Advanced Python Training at Arcesium - Day 3

Sep 25-27, 2019 Vikrant Patil

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

© Pipal Academy LLP

Day 1 | Day 2 | Day 3

Notebooks available at git repository https://github.com/vikipedia/advpythonsep19.git

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

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

In [1]:
%%file bank.py

def make_account():
    return {"balance":0}

def get_balance(account):
    return account['balance']

def withdraw(account, amount):
    account['balance'] -= amount
    
    
def deposit(account, amount):
    account['balance'] += amount
Writing bank.py
In [2]:
import bank
In [3]:
a1 = bank.make_account()
In [4]:
bank.get_balance(a1)
Out[4]:
0
In [5]:
bank.deposit(a1, 1000)
In [6]:
bank.withdraw(a1, 343)
In [8]:
bank.get_balance(a1)
Out[8]:
657
In [9]:
a1['balance'] = 4545
In [10]:
class BankAccount:
    
    def __init__(self):
        self.balance = 0
        
    def get_balance(self):
        return self.balance
    
    def withdraw(self, amount):
        self.balance -= amount
        
    def deposit(self, amount):
        self.balance += amount
        
In [11]:
a = BankAccount()
In [12]:
class BankAccount:
    
    def __init__(self, accountno, balance):
        self.balance = balance
        self.accountno = accountno
        
    def get_balance(self):
        return self.balance
    
    def withdraw(self, amount):
        self.balance -= amount
        
    def deposit(self, amount):
        self.balance += amount
        
In [13]:
a2 = BankAccount(42, 0)
In [14]:
a2.get_balance()
Out[14]:
0
In [15]:
a2.deposit(5000)
In [16]:
a2.withdraw(2323)
In [18]:
a2.get_balance()
Out[18]:
2677
In [19]:
BankAccount
Out[19]:
__main__.BankAccount
In [20]:
a2
Out[20]:
<__main__.BankAccount at 0x7ff2519a0828>
In [21]:
BankAccount.get_balance
Out[21]:
<function __main__.BankAccount.get_balance(self)>
In [22]:
a2.get_balance
Out[22]:
<bound method BankAccount.get_balance of <__main__.BankAccount object at 0x7ff2519a0828>>
In [23]:
type(a2)
Out[23]:
__main__.BankAccount
In [24]:
type(BankAccount)
Out[24]:
type
In [25]:
BankAccount.get_balance()
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-25-5fbd7fa756b7> in <module>
----> 1 BankAccount.get_balance()

TypeError: get_balance() missing 1 required positional argument: 'self'
In [26]:
BankAccount.get_balance(a2)
Out[26]:
2677
In [27]:
a2.get_balance
Out[27]:
<bound method BankAccount.get_balance of <__main__.BankAccount object at 0x7ff2519a0828>>
In [28]:
a2.get_balance(a2)
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-28-9e5410eb7223> in <module>
----> 1 a2.get_balance(a2)

TypeError: get_balance() takes 1 positional argument but 2 were given
In [29]:
x = 10

def foo():
    print(x)
    
foo()
10
In [30]:
x = 10

def foo():
    x = 20
    
print(x)
10
In [31]:
x = 10

def foo():
    print(x)
    x = 15
    
foo()
---------------------------------------------------------------------------
UnboundLocalError                         Traceback (most recent call last)
<ipython-input-31-c71ff97ce25d> in <module>
      5     x = 15
      6 
----> 7 foo()

<ipython-input-31-c71ff97ce25d> in foo()
      2 
      3 def foo():
----> 4     print(x)
      5     x = 15
      6 

UnboundLocalError: local variable 'x' referenced before assignment
In [32]:
x = 10

def foo():
    global x
    print(x)
    x = 15
    
foo()
10

Class variables Vs Instance Variables

In [33]:
class Foo:
    name = "bar"
    
    def __init__(self, x):
        self.x = x
In [36]:
f1 = Foo(5)
In [37]:
f2 = Foo(10)
In [38]:
f1.name
Out[38]:
'bar'
In [39]:
f2.name
Out[39]:
'bar'
In [40]:
Foo.name
Out[40]:
'bar'
In [41]:
Foo.name = "foo"
In [42]:
f1.name
Out[42]:
'foo'
In [43]:
f2.name
Out[43]:
'foo'
In [44]:
f1.name = "xyz"
In [45]:
Foo.name
Out[45]:
'foo'
In [46]:
f2.name
Out[46]:
'foo'
In [47]:
f1.name
Out[47]:
'xyz'
In [49]:
class Bar:
    b = 10
    
    def changeg(self, x):
        global b
        b = x
        
    def chnagewithaccess(self, x):
        print(b)
        b = x
        
In [50]:
b = Bar()
In [51]:
b.changeg(50)
In [52]:
Bar.b
Out[52]:
10

Customising classes

In [53]:
class Point:
    
    def __init__(self, x, y):
        self.x = x
        self.y = y
In [54]:
Point(2,3)
Out[54]:
<__main__.Point at 0x7ff2516f91d0>
In [55]:
[1,2,3]
Out[55]:
[1, 2, 3]
In [56]:
Point
Out[56]:
__main__.Point
In [57]:
type(Point)
Out[57]:
type
In [63]:
class Point:
    
    def __init__(self, x, y):
        self.x = x
        self.y = y
    
    def __repr__(self):
        """
        This will shown when you examine object 
        from interpreter
        """
        return "Point({},{})".format(self.x, self.y)
    
    def __str__(self):
        """
        This string is shown when you print the object
        """
        return "({}, {})".format(self.x, self.y)
In [60]:
p = Point(2,3)
In [61]:
p
Out[61]:
Point(2,3)
In [62]:
print(p)
(2, 3)
In [64]:
str(p)
Out[64]:
'(2, 3)'
In [189]:
class Point:
    
    def __init__(self, x, y):
        self.x = x
        self.y = y
    
    def __repr__(self):
        """
        This will shown when you examine object 
        from interpreter
        """
        return "Point({},{})".format(self.x, self.y)
    
    def __str__(self):
        """
        This string is shown when you print the object
        """
        return "({0.x!s}, {0.y!s})".format(self)
    
    def __len__(self):
        return 2
In [191]:
p = Point(3,5)
In [192]:
print(p)
(3, 5)
In [67]:
len(p)
Out[67]:
2
In [68]:
Point.__add__ = lambda self, p: Point(self.x+p.x, self.y+p.y)
In [69]:
Point(2,3) + Point(1,1)
Out[69]:
Point(3,4)

problem

Write a class to represnt complex number. Support proper printing and following operators

  • + -> __add__(self, c):
  • - -> __sub__(self, c):
  • == -> __eq__(self, c):
  • [] -> __getitem__(self, name):
In [70]:
d = {"a":1}
In [71]:
d['b']
---------------------------------------------------------------------------
KeyError                                  Traceback (most recent call last)
<ipython-input-71-dee353b142fb> in <module>
----> 1 d['b']

KeyError: 'b'
In [72]:
d['a']
Out[72]:
1
In [76]:
class mydictionary:
    
    def __init__(self):
        self.keys = []
        self.values = []
    
    def __setitem__(self, name, value):
        if name in self.keys:
            ind = self.keys.index(name)
            self.values[ind] = value
        else:
            self.keys.append(name)
            self.values.append(value)
    
    def __getitem__(self, name):
        if name in self.keys:
            ind = self.keys.index(name)
            return self.values[ind]
        raise KeyError("No such key, {}!".format(name))
In [77]:
d = mydictionary()
In [78]:
d['x'] = 1
In [79]:
d['x']
Out[79]:
1
In [80]:
d['y']
---------------------------------------------------------------------------
KeyError                                  Traceback (most recent call last)
<ipython-input-80-7ae159b89943> in <module>
----> 1 d['y']

<ipython-input-76-5750fb5c052c> in __getitem__(self, name)
     17             ind = self.keys.index(name)
     18             return self.values[ind]
---> 19         raise KeyError("No such key, {}!".format(name))

KeyError: 'No such key, y!'

Where is object data stored?

In [81]:
p
Out[81]:
Point(3,5)
In [82]:
type(p)
Out[82]:
__main__.Point
In [83]:
p.x
Out[83]:
3
In [84]:
p.y
Out[84]:
5
In [85]:
p.__dict__
Out[85]:
{'x': 3, 'y': 5}
In [86]:
p.z = 30
In [87]:
p.z
Out[87]:
30
In [88]:
p.__dict__
Out[88]:
{'x': 3, 'y': 5, 'z': 30}
In [89]:
import time

class Timer:
    
 
    def start(self):
        self._start = time.time()
        
    def stop(self):
        self._stop = time.time()
        
    def time_taken(self):
        return self._end - self._start
In [90]:
t = Timer()
In [91]:
class Timer:
    
    def __init__(self):
        self._start = 0
        self._stop = 0

    def start(self):
        self._start = time.time()
        
    def stop(self):
        self._stop = time.time()
        
    def time_taken(self):
        return self._end - self._start    

Controlling attributes

In [92]:
d['x']
Out[92]:
1
In [93]:
p.s = 5
In [94]:
class Sealed:
    
    def __setattr__(self, key, value):
        raise Exception("No chance!")
In [95]:
s = Sealed()
In [96]:
s.x = 12
---------------------------------------------------------------------------
Exception                                 Traceback (most recent call last)
<ipython-input-96-14d1bddf1da7> in <module>
----> 1 s.x = 12

<ipython-input-94-e84a51763f46> in __setattr__(self, key, value)
      2 
      3     def __setattr__(self, key, value):
----> 4         raise Exception("No chance!")

Exception: No chance!

problem

  • Write immutable Point class. once created can not be modified.
    p = Point(3, 4)
    p.x =45 <== should throw exception
In [97]:
class Point:
    
    def __init__(self, x, y):
        self.x = x
        self.y = y
        
    def __setattr__(self, key, value):
        raise Exception("No chance!")
In [98]:
Point(3, 4)
---------------------------------------------------------------------------
Exception                                 Traceback (most recent call last)
<ipython-input-98-41f8f73e26df> in <module>
----> 1 Point(3, 4)

<ipython-input-97-861474d080b7> in __init__(self, x, y)
      2 
      3     def __init__(self, x, y):
----> 4         self.x = x
      5         self.y = y
      6 

<ipython-input-97-861474d080b7> in __setattr__(self, key, value)
      6 
      7     def __setattr__(self, key, value):
----> 8         raise Exception("No chance!")

Exception: No chance!
In [99]:
class Point:
    
    def __init__(self, x, y):
        self.__dict__['x'] = x
        self.__dict__['y'] = y
        
    def __setattr__(self, key, value):
        raise Exception("No chance!")
In [100]:
p = Point(3,4)
In [101]:
p.x = 34
---------------------------------------------------------------------------
Exception                                 Traceback (most recent call last)
<ipython-input-101-0d9f79f7e8ae> in <module>
----> 1 p.x = 34

<ipython-input-99-6a6b6a3b752f> in __setattr__(self, key, value)
      6 
      7     def __setattr__(self, key, value):
----> 8         raise Exception("No chance!")

Exception: No chance!
In [102]:
class Point:
    
    def __init__(self, x, y):
        self.init = True
        self.x = x
        self.y = y
        
    def __setattr__(self, key, value):
        if self.init and key in ['x','y']:
            self.init = False
            self.__dict__[key] = value
        else:
            raise Exception("No chance!")
In [103]:
p = Point(3, 4)
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-103-34bfce4f5037> in <module>
----> 1 p = Point(3, 4)

<ipython-input-102-1e3c2832cb36> in __init__(self, x, y)
      2 
      3     def __init__(self, x, y):
----> 4         self.init = True
      5         self.x = x
      6         self.y = y

<ipython-input-102-1e3c2832cb36> in __setattr__(self, key, value)
      7 
      8     def __setattr__(self, key, value):
----> 9         if self.init and key in ['x','y']:
     10             self.init = False
     11             self.__dict__[key] = value

AttributeError: 'Point' object has no attribute 'init'
In [104]:
class Upper:
     def __getattr__(self, key):
            return key.upper()
In [105]:
u = Upper()
In [106]:
u.hello
Out[106]:
'HELLO'

inheritance

In [107]:
class Point:
    pass

class ColoredPoint(Point):
    pass
In [108]:
class A:
    pass

class B:
    pass

class C(A,B):
    pass
In [109]:
C.__mro__
Out[109]:
(__main__.C, __main__.A, __main__.B, object)
In [111]:
class A:
    def __init__(self):
        print("A.__init__")
        
class B(A):
    
    def __init__(self):
        print("B.__init__")
        A.__init__(self)
        
    
class C(B):
    
    def __init__(self):
        print("C.__init__")
        B.__init__(self)
        
class D(C,B):
    
    def __init__(self):
        print("D.__init__")
        C.__init__(self)
        B.__init__(self)
In [112]:
D()
D.__init__
C.__init__
B.__init__
A.__init__
B.__init__
A.__init__
Out[112]:
<__main__.D at 0x7ff2518a4358>
In [115]:
class A:
    def __init__(self):
        print("A.__init__")
        
class B(A):
    
    def __init__(self):
        print("B.__init__")
        super().__init__()
        
    
class C(B):
    
    def __init__(self):
        print("C.__init__")
        super().__init__()
        
class D(C,B):
    
    def __init__(self):
        print("D.__init__")
        super().__init__()
In [116]:
D()
D.__init__
C.__init__
B.__init__
A.__init__
Out[116]:
<__main__.D at 0x7ff2518caa58>
In [119]:
class Date:
    __slots__ = ['year', 'month', 'day']
    
    def __init__(self, year, month, day):
        self.year = year
        self.month = month
        self.day = day
In [120]:
d = Date(2017, 4, 21)
In [121]:
d.x = 50
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-121-20974cf00d60> in <module>
----> 1 d.x = 50

AttributeError: 'Date' object has no attribute 'x'

Metaclasses

In [122]:
type(Point)
Out[122]:
type
In [123]:
type(Date)
Out[123]:
type
In [ ]:
class Meta(type):
    pass
In [125]:
class SubMetaClass(Meta):
    pass
In [127]:
SubMetaClass()
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-127-e7ffc9210b18> in <module>
----> 1 SubMetaClass()

TypeError: type.__new__() takes exactly 3 arguments (0 given)
In [128]:
Meta()
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-128-fa9e1426f638> in <module>
----> 1 Meta()

TypeError: type.__new__() takes exactly 3 arguments (0 given)
In [129]:
class SubMetaClass1(metaclass=Meta):
    pass
In [130]:
type(SubMetaClass1)
Out[130]:
__main__.Meta
In [131]:
type(Meta)
Out[131]:
type
In [132]:
type(SubMetaClass)
Out[132]:
type
In [138]:
class Singleton(type):
    
    def __init__(self, *args, **kwargs):
        print(args)
        print(kwargs)
        self._instance = None
        
    def __call__(self, *args, **kwargs):
        if self._instance:
            return self._instance
        else:
            self._instance = super().__call__(*args, **kwargs)
            return self._instance
    

class SingletonTop(metaclass=Singleton):
    pass
('SingletonTop', (), {'__module__': '__main__', '__qualname__': 'SingletonTop'})
{}
In [139]:
class X(SingletonTop):
    pass
('X', (<class '__main__.SingletonTop'>,), {'__module__': '__main__', '__qualname__': 'X'})
{}
In [140]:
x = X()
In [141]:
y = X()
In [142]:
x == y
Out[142]:
True
In [143]:
id(x)
Out[143]:
140678722472648
In [144]:
id(y)
Out[144]:
140678722472648
In [145]:
p1 = Point()
In [146]:
p2 = Point()
In [147]:
id(p1), id(p2)
Out[147]:
(140678722263808, 140678728043656)
In [148]:
p1 is p2
Out[148]:
False
In [149]:
x is y
Out[149]:
True
In [150]:
l1 = [1,2,3]
l2 = [1,2,3]
In [151]:
l1 == l2
Out[151]:
True
In [154]:
l1 is l2
Out[154]:
False

Interface or abstract

In [155]:
p1 == p2
Out[155]:
False
In [156]:
p1 == p1
Out[156]:
True
In [157]:
class Shape:
    
    def contains(self, point):
        pass
In [158]:
from abc import ABCMeta, abstractmethod
In [160]:
class Shape(metaclass=ABCMeta):
    
    @abstractmethod
    def contains(self, point):
        pass
In [161]:
class Circle(Shape):
    
    pass
In [162]:
Circle()
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-162-883f2bfc2476> in <module>
----> 1 Circle()

TypeError: Can't instantiate abstract class Circle with abstract methods contains
In [167]:
class ShapeArea:
    
    def area(self):
        pass
In [168]:
class Circle(ShapeArea):
    
    def area(self):
        pass
    
class Square(ShapeArea):
    pass

class Rectangle:
    pass
In [169]:
shapes = [Circle(), Square() , Rectangle()]
In [170]:
for s in shapes:
    s.area()
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-170-c36c091b1931> in <module>
      1 for s in shapes:
----> 2     s.area()

AttributeError: 'Rectangle' object has no attribute 'area'

Properties

In [171]:
class Person:
    
    def __init__(self, name):
        self.name = name
    
    @property
    def email(self):
        return "@".join([self.name, "arcesium.com"])
    
    @email.setter
    def email(self, value):
        raise Exception("You can not change email")
In [172]:
p = Person("praveen")
In [173]:
p.email
Out[173]:
'praveen@arcesium.com'
In [174]:
p.email = "pravin@arcesium.com"
---------------------------------------------------------------------------
Exception                                 Traceback (most recent call last)
<ipython-input-174-cc0cd727681a> in <module>
----> 1 p.email = "pravin@arcesium.com"

<ipython-input-171-b17d90b2d51b> in email(self, value)
     10     @email.setter
     11     def email(self, value):
---> 12         raise Exception("You can not change email")

Exception: You can not change email
In [175]:
class Person1:
    
    def __init__(self, name):
        self.name = name
    
    @property
    def email(self):
        return "@".join([self.name, "arcesium.com"])
    
In [176]:
p1 = Person1("hello")
In [177]:
p1.email
Out[177]:
'hello@arcesium.com'
In [178]:
p1.email = "xyz@lskd;as"
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-178-8ddb8b34aaa2> in <module>
----> 1 p1.email = "xyz@lskd;as"

AttributeError: can't set attribute

class decorator

In [183]:
from functools import wraps, partial

def debug(func):
    prefix = "*"*5
    msg = prefix + func.__qualname__
    
    @wraps(func)
    def wrapper(*args, **kwargs):
        print(msg)
        return func(*args, **kwargs)
    return wrapper

def debugmethods(cls):
    for name, val in vars(cls).items():
        if callable(val):
            setattr(cls, name, debug(val))
    return cls

@debugmethods
class FooBar:
    x = "hello"
    
    def method1(self):
        pass
    
    def method2(self):
        pass
    
    def method3(self):
        pass
In [185]:
fb = FooBar()
In [186]:
fb.x
Out[186]:
'hello'
In [187]:
fb.method1()
*****FooBar.method1
In [188]:
fb.method2()
*****FooBar.method2

Decriptors

In [196]:
class Integer:
    
    def __init__(self, name):
        self.name = name
        
    def __get__(self, instance, cls):
        print("__get__ from", self)
        if instance is None:
            return self
        else:
            return instance.__dict__[self.name]
        
    def __set__(self, instance, value):
        print("__set__ from", self)
        if not isinstance(value, int):
            raise TyepeError("Expected int")
        instance.__dict__[self.name] = value
        
    def __delete__(self, instance):
        print("__del__ from", self)
        del instance.__dict__[self.name]
        
    def __str__(self):
        return "Integer<{0.name!s}>".format(self)
In [208]:
class Point:
    
    x = Integer('x')
    y = Integer('y')
    
    def __init__(self, x, y):
        self.x = x
        self.y = y
        
    def __setattr__(self, name, value):
        print("__setattr__ of Point")
        super().__setattr__(name, value)
In [209]:
p = Point(2,3)
__setattr__ of Point
__set__ from Integer<x>
__setattr__ of Point
__set__ from Integer<y>
In [210]:
p.x
__get__ from Integer<x>
Out[210]:
2
In [211]:
p.y
__get__ from Integer<y>
Out[211]:
3
In [203]:
p.x = 5
__set__ from Integer<x>
In [212]:
p.z = 5
__setattr__ of Point
In [205]:
1
Out[205]:
1
In [206]:
1
Out[206]:
1
In [207]:
1
Out[207]:
1
In [213]:
class my_property:
    
    def __init__(self, func):
        self.func = func
        
    def __get__(self, instance, cls):
        if instance is None:
            return self
        print("my_property.__get__")
        return self.func(instance)
In [214]:
class Person:
    def __init__(self, name):
        self.name = name
    
    @my_property
    def email(self):
        return "@".join([name, "gmail.com"])

staticmethod and classmethod

In [215]:
class MyInt:
    
    def __init__(self, strnum):
        self.value = int(strnum)
        
    def parse(strnum):
        return int(strnum)
In [216]:
MyInt.parse("454")
Out[216]:
454
In [217]:
m = MyInt("34")
In [218]:
m.parse("4")
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-218-3028246326a9> in <module>
----> 1 m.parse("4")

TypeError: parse() takes 1 positional argument but 2 were given
In [219]:
class MyInt:
    
    def __init__(self, strnum):
        self.value = int(strnum)
    
    @staticmethod
    def parse(strnum):
        return int(strnum)
In [220]:
m = MyInt("67")
In [221]:
m.parse("45")
Out[221]:
45
In [222]:
import datetime
In [223]:
class MyDatetime(datetime.datetime):
    pass
In [224]:
datetime.datetime.now()
Out[224]:
datetime.datetime(2019, 9, 27, 16, 2, 15, 309613)
In [225]:
MyDatetime.now()
Out[225]:
MyDatetime(2019, 9, 27, 16, 2, 45, 887254)
In [227]:
class Foo:
    
    def __init__(self, x):
        self.x = x
        
    def __repr__(self):
        return "{}({})".format(self.__class__.__name__, repr(self.x))
    
    @classmethod
    def instance(cls, value):
        return cls(value)
    
    @staticmethod
    def instance1(value):
        return Foo(value)

class Bar(Foo):
    pass
In [228]:
Bar.instance1(3)
Out[228]:
Foo(3)
In [229]:
Bar.instance(5)
Out[229]:
Bar(5)

Context Manager

In [230]:
with open("data.csv") as f:
    f.read()
In [239]:
class Timer:
    
    def __init__(self):
        self._start = 0
        self._stop = 0

    def start(self):
        self._start = time.time()
        
    def stop(self):
        self._stop = time.time()
        
    def time_taken(self):
        return self._stop - self._start  
    
    
class ContextTimer(Timer):
    
    def reset(self):
        self._start = 0
        self._stop = 0
        
    def __enter__(self):
        print("Beging with block ....")
        self.reset()
        self.start()
        print("Now entering...")
        return self
    
    def __exit__(self, exc_type, exc_value, traceback):
        self.stop()
        print(exc_type, exc_value, traceback)
        print("Time taken in with block: ", self.time_taken())
        
In [240]:
ct = ContextTimer()
with ct:
    print("Inside with body....")
    for i in range(10000):
        for j in range(10000):
            s = i*j*1.0
    print("End of with body...")
    
Beging with block ....
Now entering...
Inside with body....
End of with body...
None None None
Time taken in with block:  10.950100183486938
In [242]:
ct = ContextTimer()
with ct:
    print("Inside with body....")
    for i in range(10000):
        for j in range(10000):
            s = i*j*1.0
    boom
    print("End of with body...")
    
Beging with block ....
Now entering...
Inside with body....
<class 'NameError'> name 'boom' is not defined <traceback object at 0x7ff250981dc8>
Time taken in with block:  10.610075235366821
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
<ipython-input-242-1584e8f1f29b> in <module>
      5         for j in range(10000):
      6             s = i*j*1.0
----> 7     boom
      8     print("End of with body...")
      9 

NameError: name 'boom' is not defined

Threading

In [243]:
import threading
In [247]:
def tick(n):
    for i in range(n):
        print("Tick-", i)
        time.sleep(1)
        
def reversetick(n):
    for i in reversed(range(n)):
        print("Tick-down-", i)
        time.sleep(1)
        
t1 = threading.Thread(target=tick, args=(10,))
t2 = threading.Thread(target=reversetick, args=(10,))
t1.start()
t2.start()
t1.join()
t2.join()
print("Tasks are over")
Tick- 0
Tick-down- 9
Tick- 1
Tick-down- 8
Tick- 2
Tick-down- 7
Tick- 3
Tick-down- 6
Tick- 4
Tick-down- 5
Tick- 5
Tick-down- 4
Tick- 6
Tick-down- 3
Tick- 7
Tick-down- 2
Tick- 8
Tick-down- 1
Tick- 9
Tick-down- 0
Tasks are over

Cimmunicating with threads

In [248]:
event = threading.Event()
In [249]:
def tick(event):
    i = 0
    while not event.isSet():
        print("Tick-", i)
        time.sleep(2)
        i += 1
t = threading.Thread(target=tick, args=(event,))
t.start()
Tick- 0
Tick- 1
Tick- 2
Tick- 3
Tick- 4
Tick- 5
Tick- 6
In [250]:
event.set()

sharing of data among threads

In [251]:
import random

def producer(data, lock, event):
    while not event.isSet():
        with lock:
            data.append(random.choice([0,1]))
            print("Produced...", data[-1])
        time.sleep(1)
        
def consumer(data, lock, event):
    while not event.isSet():
        with lock:
            print("Consumed...", data.pop())
        time.sleep(1)
        
def observer(data, lock, event):
    while not event.isSet():
        print(data)
        time.sleep(1)
In [253]:
data = list(range(5))
lock = threading.Lock()
event = threading.Event()
p = threading.Thread(target=producer, args=(data, lock, event))
c = threading.Thread(target=consumer, args=(data, lock, event))
o = threading.Thread(target=observer, args=(data, lock, event))
o.start()
p.start()
time.sleep(3)
c.start()
[0, 1, 2, 3, 4]
Produced... 0
[0, 1, 2, 3, 4, 0]
Produced... 1
[0, 1, 2, 3, 4, 0, 1]
Produced... 0
[0, 1, 2, 3, 4, 0, 1, 0]
Consumed... 0
Produced... 0
[0, 1, 2, 3, 4, 0, 1, 0]
Consumed... 0
Produced... 0
[0, 1, 2, 3, 4, 0, 1, 0]
Consumed... 0
Produced... 0
[0, 1, 2, 3, 4, 0, 1, 0]
Consumed... 0
Produced... 1
[0, 1, 2, 3, 4, 0, 1, 1]
Consumed... 1
Produced... 1
[0, 1, 2, 3, 4, 0, 1, 1]
Consumed... 1
Produced... 1
[0, 1, 2, 3, 4, 0, 1, 1]
Consumed... 1
Produced... 1
[0, 1, 2, 3, 4, 0, 1, 1]
Consumed... 1
Produced... 1
[0, 1, 2, 3, 4, 0, 1, 1]
Consumed... 1
Produced... 1
[0, 1, 2, 3, 4, 0, 1, 1]
Consumed... 1
Produced... 0
[0, 1, 2, 3, 4, 0, 1, 0]
Consumed... 0
Produced... 1
[0, 1, 2, 3, 4, 0, 1, 1]
Consumed... 1
Produced... 1
[0, 1, 2, 3, 4, 0, 1, 1]
Consumed... 1
Produced... 0
[0, 1, 2, 3, 4, 0, 1, 0]
Consumed... 0
Produced... 0
[0, 1, 2, 3, 4, 0, 1, 0]
Consumed... 0
Produced... 1
[0, 1, 2, 3, 4, 0, 1, 1]
Consumed... 1
Produced... 0
[0, 1, 2, 3, 4, 0, 1, 0]
Consumed... 0
Produced... 0
[0, 1, 2, 3, 4, 0, 1, 0]
Consumed... 0
Produced... 0
[0, 1, 2, 3, 4, 0, 1, 0]
Consumed... 0
Produced... 1
[0, 1, 2, 3, 4, 0, 1, 1]
Consumed... 1
Produced... 1
[0, 1, 2, 3, 4, 0, 1, 1]
Consumed... 1
Produced... 0
[0, 1, 2, 3, 4, 0, 1, 0]
Consumed... 0
Produced... 1
[0, 1, 2, 3, 4, 0, 1, 1]
Consumed... 1
Produced... 0
[0, 1, 2, 3, 4, 0, 1, 0]
Consumed... 0
Produced... 1
[0, 1, 2, 3, 4, 0, 1, 1]
Consumed... 1
Produced... 1
[0, 1, 2, 3, 4, 0, 1, 1]
Consumed... 1
Produced... 1
[0, 1, 2, 3, 4, 0, 1, 1]
Consumed... 1
Produced... 0
In [254]:
event.set()
In [255]:
x , y, z = 1, 2, 3

threadpool/processpool

In [256]:
from multiprocessing.pool import ThreadPool
In [257]:
pool = ThreadPool(4)
In [258]:
import requests

def download(url):
    resp = requests.get(url)
    return resp

def threaded_download(n):
    pool = ThreadPool(n)
    url = "https://ia802902.us.archive.org/4/items/prideandprejudic01342gut/pandp12.txt"
    urls = [url]*10
    res = pool.map(download, urls)
    return res

for i in range(1,9):
    with ContextTimer() as t:
        threaded_download(i)
    
Beging with block ....
Now entering...
None None None
Time taken in with block:  26.68535351753235
Beging with block ....
Now entering...
None None None
Time taken in with block:  15.35297966003418
Beging with block ....
Now entering...
None None None
Time taken in with block:  10.838618278503418
Beging with block ....
Now entering...
None None None
Time taken in with block:  8.00178575515747
Beging with block ....
Now entering...
None None None
Time taken in with block:  5.786166191101074
Beging with block ....
Now entering...
None None None
Time taken in with block:  5.653040170669556
Beging with block ....
Now entering...
None None None
Time taken in with block:  5.813875913619995
Beging with block ....
Now entering...
None None None
Time taken in with block:  5.811497211456299

cpu tasks

In [261]:
from multiprocessing.pool import Pool
    
class ContextTimer(Timer):
    
    def reset(self):
        self._start = 0
        self._stop = 0
        
    def __enter__(self):
        self.reset()
        self.start()
        return self
    
    def __exit__(self, exc_type, exc_value, traceback):
        self.stop()
        print("Time taken in with block: ", self.time_taken())
        
def saxpy(n):
    s = 0
    for i in range(n):
        for j in range(n//10):
            s += 1*j*1.0
    return s

def process_pool(n):
    pool = Pool(n)
    args = [10000]*10
    res = pool.map(saxpy, args)
    return res

for i in range(1,9):
    with ContextTimer() as t:
        process_pool(i)
Time taken in with block:  7.130861759185791
Time taken in with block:  4.790905475616455
Time taken in with block:  3.1787478923797607
Time taken in with block:  2.647132158279419
Time taken in with block:  2.7412288188934326
Time taken in with block:  2.7993009090423584
Time taken in with block:  3.0095059871673584
Time taken in with block:  3.1058568954467773

cython

In [262]:
%load_ext Cython
In [263]:
%%cython --annotate

def square(x):
    return x*x

def sumofsquares(x,y):
    return square(x) + square(y)
Out[263]:
Cython: _cython_magic_cbd2f9420c77f89c385ed6b164e06e13.pyx

Generated by Cython 0.29.12

Yellow lines hint at Python interaction.
Click on a line that starts with a "+" to see the C code that Cython generated for it.

 1: 
+2: def square(x):
/* Python wrapper */
static PyObject *__pyx_pw_46_cython_magic_cbd2f9420c77f89c385ed6b164e06e13_1square(PyObject *__pyx_self, PyObject *__pyx_v_x); /*proto*/
static PyMethodDef __pyx_mdef_46_cython_magic_cbd2f9420c77f89c385ed6b164e06e13_1square = {"square", (PyCFunction)__pyx_pw_46_cython_magic_cbd2f9420c77f89c385ed6b164e06e13_1square, METH_O, 0};
static PyObject *__pyx_pw_46_cython_magic_cbd2f9420c77f89c385ed6b164e06e13_1square(PyObject *__pyx_self, PyObject *__pyx_v_x) {
  PyObject *__pyx_r = 0;
  __Pyx_RefNannyDeclarations
  __Pyx_RefNannySetupContext("square (wrapper)", 0);
  __pyx_r = __pyx_pf_46_cython_magic_cbd2f9420c77f89c385ed6b164e06e13_square(__pyx_self, ((PyObject *)__pyx_v_x));

  /* function exit code */
  __Pyx_RefNannyFinishContext();
  return __pyx_r;
}

static PyObject *__pyx_pf_46_cython_magic_cbd2f9420c77f89c385ed6b164e06e13_square(CYTHON_UNUSED PyObject *__pyx_self, PyObject *__pyx_v_x) {
  PyObject *__pyx_r = NULL;
  __Pyx_RefNannyDeclarations
  __Pyx_RefNannySetupContext("square", 0);
/* … */
  /* function exit code */
  __pyx_L1_error:;
  __Pyx_XDECREF(__pyx_t_1);
  __Pyx_AddTraceback("_cython_magic_cbd2f9420c77f89c385ed6b164e06e13.square", __pyx_clineno, __pyx_lineno, __pyx_filename);
  __pyx_r = NULL;
  __pyx_L0:;
  __Pyx_XGIVEREF(__pyx_r);
  __Pyx_RefNannyFinishContext();
  return __pyx_r;
}
/* … */
  __pyx_tuple_ = PyTuple_Pack(1, __pyx_n_s_x); if (unlikely(!__pyx_tuple_)) __PYX_ERR(0, 2, __pyx_L1_error)
  __Pyx_GOTREF(__pyx_tuple_);
  __Pyx_GIVEREF(__pyx_tuple_);
/* … */
  __pyx_t_1 = PyCFunction_NewEx(&__pyx_mdef_46_cython_magic_cbd2f9420c77f89c385ed6b164e06e13_1square, NULL, __pyx_n_s_cython_magic_cbd2f9420c77f89c38); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 2, __pyx_L1_error)
  __Pyx_GOTREF(__pyx_t_1);
  if (PyDict_SetItem(__pyx_d, __pyx_n_s_square, __pyx_t_1) < 0) __PYX_ERR(0, 2, __pyx_L1_error)
  __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
  __pyx_codeobj__2 = (PyObject*)__Pyx_PyCode_New(1, 0, 1, 0, CO_OPTIMIZED|CO_NEWLOCALS, __pyx_empty_bytes, __pyx_empty_tuple, __pyx_empty_tuple, __pyx_tuple_, __pyx_empty_tuple, __pyx_empty_tuple, __pyx_kp_s_home_vikrant_cache_ipython_cyth, __pyx_n_s_square, 2, __pyx_empty_bytes); if (unlikely(!__pyx_codeobj__2)) __PYX_ERR(0, 2, __pyx_L1_error)
+3:     return x*x
  __Pyx_XDECREF(__pyx_r);
  __pyx_t_1 = PyNumber_Multiply(__pyx_v_x, __pyx_v_x); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 3, __pyx_L1_error)
  __Pyx_GOTREF(__pyx_t_1);
  __pyx_r = __pyx_t_1;
  __pyx_t_1 = 0;
  goto __pyx_L0;
 4: 
+5: def sumofsquares(x,y):
/* Python wrapper */
static PyObject *__pyx_pw_46_cython_magic_cbd2f9420c77f89c385ed6b164e06e13_3sumofsquares(PyObject *__pyx_self, PyObject *__pyx_args, PyObject *__pyx_kwds); /*proto*/
static PyMethodDef __pyx_mdef_46_cython_magic_cbd2f9420c77f89c385ed6b164e06e13_3sumofsquares = {"sumofsquares", (PyCFunction)(void*)(PyCFunctionWithKeywords)__pyx_pw_46_cython_magic_cbd2f9420c77f89c385ed6b164e06e13_3sumofsquares, METH_VARARGS|METH_KEYWORDS, 0};
static PyObject *__pyx_pw_46_cython_magic_cbd2f9420c77f89c385ed6b164e06e13_3sumofsquares(PyObject *__pyx_self, PyObject *__pyx_args, PyObject *__pyx_kwds) {
  PyObject *__pyx_v_x = 0;
  PyObject *__pyx_v_y = 0;
  PyObject *__pyx_r = 0;
  __Pyx_RefNannyDeclarations
  __Pyx_RefNannySetupContext("sumofsquares (wrapper)", 0);
  {
    static PyObject **__pyx_pyargnames[] = {&__pyx_n_s_x,&__pyx_n_s_y,0};
    PyObject* values[2] = {0,0};
    if (unlikely(__pyx_kwds)) {
      Py_ssize_t kw_args;
      const Py_ssize_t pos_args = PyTuple_GET_SIZE(__pyx_args);
      switch (pos_args) {
        case  2: values[1] = PyTuple_GET_ITEM(__pyx_args, 1);
        CYTHON_FALLTHROUGH;
        case  1: values[0] = PyTuple_GET_ITEM(__pyx_args, 0);
        CYTHON_FALLTHROUGH;
        case  0: break;
        default: goto __pyx_L5_argtuple_error;
      }
      kw_args = PyDict_Size(__pyx_kwds);
      switch (pos_args) {
        case  0:
        if (likely((values[0] = __Pyx_PyDict_GetItemStr(__pyx_kwds, __pyx_n_s_x)) != 0)) kw_args--;
        else goto __pyx_L5_argtuple_error;
        CYTHON_FALLTHROUGH;
        case  1:
        if (likely((values[1] = __Pyx_PyDict_GetItemStr(__pyx_kwds, __pyx_n_s_y)) != 0)) kw_args--;
        else {
          __Pyx_RaiseArgtupleInvalid("sumofsquares", 1, 2, 2, 1); __PYX_ERR(0, 5, __pyx_L3_error)
        }
      }
      if (unlikely(kw_args > 0)) {
        if (unlikely(__Pyx_ParseOptionalKeywords(__pyx_kwds, __pyx_pyargnames, 0, values, pos_args, "sumofsquares") < 0)) __PYX_ERR(0, 5, __pyx_L3_error)
      }
    } else if (PyTuple_GET_SIZE(__pyx_args) != 2) {
      goto __pyx_L5_argtuple_error;
    } else {
      values[0] = PyTuple_GET_ITEM(__pyx_args, 0);
      values[1] = PyTuple_GET_ITEM(__pyx_args, 1);
    }
    __pyx_v_x = values[0];
    __pyx_v_y = values[1];
  }
  goto __pyx_L4_argument_unpacking_done;
  __pyx_L5_argtuple_error:;
  __Pyx_RaiseArgtupleInvalid("sumofsquares", 1, 2, 2, PyTuple_GET_SIZE(__pyx_args)); __PYX_ERR(0, 5, __pyx_L3_error)
  __pyx_L3_error:;
  __Pyx_AddTraceback("_cython_magic_cbd2f9420c77f89c385ed6b164e06e13.sumofsquares", __pyx_clineno, __pyx_lineno, __pyx_filename);
  __Pyx_RefNannyFinishContext();
  return NULL;
  __pyx_L4_argument_unpacking_done:;
  __pyx_r = __pyx_pf_46_cython_magic_cbd2f9420c77f89c385ed6b164e06e13_2sumofsquares(__pyx_self, __pyx_v_x, __pyx_v_y);

  /* function exit code */
  __Pyx_RefNannyFinishContext();
  return __pyx_r;
}

static PyObject *__pyx_pf_46_cython_magic_cbd2f9420c77f89c385ed6b164e06e13_2sumofsquares(CYTHON_UNUSED PyObject *__pyx_self, PyObject *__pyx_v_x, PyObject *__pyx_v_y) {
  PyObject *__pyx_r = NULL;
  __Pyx_RefNannyDeclarations
  __Pyx_RefNannySetupContext("sumofsquares", 0);
/* … */
  /* function exit code */
  __pyx_L1_error:;
  __Pyx_XDECREF(__pyx_t_1);
  __Pyx_XDECREF(__pyx_t_2);
  __Pyx_XDECREF(__pyx_t_3);
  __Pyx_XDECREF(__pyx_t_4);
  __Pyx_AddTraceback("_cython_magic_cbd2f9420c77f89c385ed6b164e06e13.sumofsquares", __pyx_clineno, __pyx_lineno, __pyx_filename);
  __pyx_r = NULL;
  __pyx_L0:;
  __Pyx_XGIVEREF(__pyx_r);
  __Pyx_RefNannyFinishContext();
  return __pyx_r;
}
/* … */
  __pyx_tuple__3 = PyTuple_Pack(2, __pyx_n_s_x, __pyx_n_s_y); if (unlikely(!__pyx_tuple__3)) __PYX_ERR(0, 5, __pyx_L1_error)
  __Pyx_GOTREF(__pyx_tuple__3);
  __Pyx_GIVEREF(__pyx_tuple__3);
/* … */
  __pyx_t_1 = PyCFunction_NewEx(&__pyx_mdef_46_cython_magic_cbd2f9420c77f89c385ed6b164e06e13_3sumofsquares, NULL, __pyx_n_s_cython_magic_cbd2f9420c77f89c38); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 5, __pyx_L1_error)
  __Pyx_GOTREF(__pyx_t_1);
  if (PyDict_SetItem(__pyx_d, __pyx_n_s_sumofsquares, __pyx_t_1) < 0) __PYX_ERR(0, 5, __pyx_L1_error)
  __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+6:     return square(x) + square(y)
  __Pyx_XDECREF(__pyx_r);
  __Pyx_GetModuleGlobalName(__pyx_t_2, __pyx_n_s_square); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 6, __pyx_L1_error)
  __Pyx_GOTREF(__pyx_t_2);
  __pyx_t_3 = NULL;
  if (CYTHON_UNPACK_METHODS && unlikely(PyMethod_Check(__pyx_t_2))) {
    __pyx_t_3 = PyMethod_GET_SELF(__pyx_t_2);
    if (likely(__pyx_t_3)) {
      PyObject* function = PyMethod_GET_FUNCTION(__pyx_t_2);
      __Pyx_INCREF(__pyx_t_3);
      __Pyx_INCREF(function);
      __Pyx_DECREF_SET(__pyx_t_2, function);
    }
  }
  __pyx_t_1 = (__pyx_t_3) ? __Pyx_PyObject_Call2Args(__pyx_t_2, __pyx_t_3, __pyx_v_x) : __Pyx_PyObject_CallOneArg(__pyx_t_2, __pyx_v_x);
  __Pyx_XDECREF(__pyx_t_3); __pyx_t_3 = 0;
  if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 6, __pyx_L1_error)
  __Pyx_GOTREF(__pyx_t_1);
  __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
  __Pyx_GetModuleGlobalName(__pyx_t_3, __pyx_n_s_square); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 6, __pyx_L1_error)
  __Pyx_GOTREF(__pyx_t_3);
  __pyx_t_4 = NULL;
  if (CYTHON_UNPACK_METHODS && unlikely(PyMethod_Check(__pyx_t_3))) {
    __pyx_t_4 = PyMethod_GET_SELF(__pyx_t_3);
    if (likely(__pyx_t_4)) {
      PyObject* function = PyMethod_GET_FUNCTION(__pyx_t_3);
      __Pyx_INCREF(__pyx_t_4);
      __Pyx_INCREF(function);
      __Pyx_DECREF_SET(__pyx_t_3, function);
    }
  }
  __pyx_t_2 = (__pyx_t_4) ? __Pyx_PyObject_Call2Args(__pyx_t_3, __pyx_t_4, __pyx_v_y) : __Pyx_PyObject_CallOneArg(__pyx_t_3, __pyx_v_y);
  __Pyx_XDECREF(__pyx_t_4); __pyx_t_4 = 0;
  if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 6, __pyx_L1_error)
  __Pyx_GOTREF(__pyx_t_2);
  __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
  __pyx_t_3 = PyNumber_Add(__pyx_t_1, __pyx_t_2); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 6, __pyx_L1_error)
  __Pyx_GOTREF(__pyx_t_3);
  __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
  __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
  __pyx_r = __pyx_t_3;
  __pyx_t_3 = 0;
  goto __pyx_L0;
In [264]:
!ls
bank.py     day2.ipynb	  HYDERABAD-weather.csv  pandp.txt    task.prof
data.csv    day3.html	  HYDERABD-weather.csv	 push	      task.py
data.db     day3.ipynb	  index.html		 __pycache__  templates
day1.html   fib1.py	  index.ipynb		 sample.html  trace.py
day1.ipynb  fib.py	  Makefile		 sample.txt   Untitled.html
day2.html   flask_app.py  memoize.py		 ssq.py
In [265]:
%%timeit
sumofsquares(5, 7)
90.6 ns ± 1.45 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)
In [266]:
def square(x):
    return x*x

def sumofsquares(x,y):
    return square(x) + square(y)
In [267]:
%%timeit
sumofsquares(5, 7)
196 ns ± 2.75 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
In [277]:
%%file cfib.pyx
from __future__ import print_function

def fib(n):
    cdef int cur, prev
    cur, prev = 1,1
    while cur < n:
        cur, prev = cur+prev, cur
        print(cur, end=",")
Writing cfib.pyx
In [269]:
%%file setup.py
from distutils.core import setup
from Cython.Build import cythonize

setup(ext_modules=cythonize("cfib.pyx"))
Writing setup.py
In [270]:
!python setup.py build_ext --inplace
Compiling cfib.pyx because it changed.
[1/1] Cythonizing cfib.pyx
/home/vikrant/anaconda3/lib/python3.7/site-packages/Cython/Compiler/Main.py:369: FutureWarning: Cython directive 'language_level' not set, using 2 for now (Py2). This will change in a later release! File: /home/vikrant/trainings/2019/arcesium_advanced_sep/cfib.pyx
  tree = Parsing.p_module(s, pxd, full_module_name)
running build_ext
building 'cfib' extension
creating build
creating build/temp.linux-x86_64-3.7
gcc -pthread -B /home/vikrant/anaconda3/compiler_compat -Wl,--sysroot=/ -Wsign-compare -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes -fPIC -I/home/vikrant/anaconda3/include/python3.7m -c cfib.c -o build/temp.linux-x86_64-3.7/cfib.o
gcc -pthread -shared -B /home/vikrant/anaconda3/compiler_compat -L/home/vikrant/anaconda3/lib -Wl,-rpath=/home/vikrant/anaconda3/lib -Wl,--no-as-needed -Wl,--sysroot=/ build/temp.linux-x86_64-3.7/cfib.o -o /home/vikrant/trainings/2019/arcesium_advanced_sep/cfib.cpython-37m-x86_64-linux-gnu.so
In [271]:
!ls
bank.py				      day3.ipynb	     __pycache__
build				      fib1.py		     sample.html
cfib.c				      fib.py		     sample.txt
cfib.cpython-37m-x86_64-linux-gnu.so  flask_app.py	     setup.py
cfib.pyx			      HYDERABAD-weather.csv  ssq.py
data.csv			      HYDERABD-weather.csv   task.prof
data.db				      index.html	     task.py
day1.html			      index.ipynb	     templates
day1.ipynb			      Makefile		     trace.py
day2.html			      memoize.py	     Untitled.html
day2.ipynb			      pandp.txt
day3.html			      push
In [272]:
import cfib
In [273]:
cfib.fib(1000)
2,3,5,8,13,21,34,55,89,144,233,377,610,987,1597,
In [274]:
!rm cfib.pyx 
In [275]:
cfib.fib(1000)
2,3,5,8,13,21,34,55,89,144,233,377,610,987,1597,
In [278]:
cfib.fib('5')
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-278-45ece6b42dfa> in <module>
----> 1 cfib.fib('5')

~/trainings/2019/arcesium_advanced_sep/cfib.pyx in cfib.fib()
      4     cdef int cur, prev
      5     cur, prev = 1,1
----> 6     while cur < n:
      7         cur, prev = cur+prev, cur
      8         print(cur, end=",")

TypeError: '<' not supported between instances of 'int' and 'str'

Calling c function in python

In [279]:
%%file simple.c


int add(int x, int y ){
    return(x+y);
}
Writing simple.c
In [280]:
!gcc -c simple.c
In [281]:
!gcc -shared -o libsimple.so simple.o
In [282]:
!ls libsimple.so
libsimple.so
In [283]:
import ctypes
In [284]:
import os
In [285]:
_file = "libsimple.so"
_path = os.path.join(os.getcwd(), _file)
_module = ctypes.cdll.LoadLibrary(_path)

add = _module.add
add.argtypes = [ctypes.c_int, ctypes.c_int]
add.restype = ctypes.c_int

add(5, 8)
Out[285]:
13

numba

In [286]:
from numba import jit

@jit
def add(x,y):
    return x+y
In [290]:
@jit(nogil=True)
def saxpy(n):
    s = 0
    for i in range(n):
        for j in range(n):
            s += i*j*1.0
    return s
In [288]:
def saxpy1(n):
    s = 0
    for i in range(n):
        for j in range(n):
            s += i*j*1.0
    return s
In [289]:
%%timeit
saxpy1(1000)
75.1 ms ± 788 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
In [292]:
%%timeit
saxpy(1000)
1.18 ms ± 2.6 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
In [295]:
@jit(nopython=True)
def saxpy2(n):
    s = 0
    for i in range(n):
        for j in range(n):
            s += i*j*1.0
    return s

@jit(nogil=True, parallel=True)
def twice():
    return saxpy2(1000) + saxpy2(1000)
In [296]:
%%timeit
twice()
/home/vikrant/anaconda3/lib/python3.7/site-packages/numba/compiler.py:588: NumbaPerformanceWarning: 
The keyword argument 'parallel=True' was specified but no transformation for parallel execution was possible.

To find out why, try turning on parallel diagnostics, see http://numba.pydata.org/numba-doc/latest/user/parallel.html#diagnostics for help.

File "<ipython-input-295-7dfe7a087dcc>", line 10:
@jit(nogil=True, parallel=True)
def twice():
^

  self.func_ir.loc))
2.37 ms ± 12.8 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

References

  • Python docs
  • Python Cookbook by David Beazly
  • google for meta programming by David beazly (viedo/slides)
  • SICP (programming classic!)
  • Python Practice Book by Anand Chitipothu
  • Concurrency from bottom up ... by david beazly (video)
In [ ]: