Advanced Python Training at Arcesium - Day 3

Oct 22-24, 2018 Vikrant Patil

These notes are available online at http://notes.pipal.in/2018/arcesium-advanced-oct/day3.html

© Pipal Academy LLP

Day 1 | Day 2 | Day 3

Classes

In [1]:
class BankAccount:
    
    def __init__(self, balance=0):
        self.balance = balance
        
    def get_balance(self):
        return self.balance
    
    def deposit(self, amount):
        self.balance += amount
        
    def withdraw(self, amount):
        self.balance -= amount
    
        
In [2]:
!cat bank1.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

    
In [6]:
a1 = BankAccount() # this actually calls __init__ method
In [4]:
a2 = BankAccount(balance=100)
In [5]:
type(a1)
Out[5]:
__main__.BankAccount
In [7]:
a1.get_balance()
Out[7]:
0
In [8]:
def foo():
    pass
In [9]:
foo
Out[9]:
<function __main__.foo>
In [15]:
class Point:
    
    def __init__(self, x, y):
        self.x = x
        self.y = y
        
    def getX(self):
        return self.x
    
    def getY(self):
        return self.y
In [16]:
Point
Out[16]:
__main__.Point
In [17]:
type(Point)
Out[17]:
type
In [18]:
type(Point.getX)
Out[18]:
function
In [19]:
p1 = Point(3,4)
In [20]:
p1.x
Out[20]:
3
In [21]:
type(p1.getX)
Out[21]:
method
In [22]:
func = Point.getX
In [23]:
func()
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-23-08a2da4138f6> in <module>()
----> 1 func()

TypeError: getX() missing 1 required positional argument: 'self'
In [24]:
func(p1)
Out[24]:
3
In [25]:
methodx = p1.getX
In [26]:
methodx()
Out[26]:
3
In [27]:
class Point:
    color = "red"
    
    def __init__(self, x, y):
        self.x = x
        self.y = x
        
In [28]:
p = Point(0,0)
In [29]:
p.x
Out[29]:
0
In [30]:
p.y
Out[30]:
0
In [31]:
p.color
Out[31]:
'red'
In [32]:
Point.color
Out[32]:
'red'
In [33]:
Point.x
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-33-7a7eae6dfbec> in <module>()
----> 1 Point.x

AttributeError: type object 'Point' has no attribute 'x'
In [34]:
class RedPoint:
    color = "red"
    
    def __init__(self, x, y):
        self.x = x
        self.y = x
        
In [35]:
r1 = RedPoint(0,0)
In [37]:
r2 = RedPoint(1,2)
In [38]:
r1.color
Out[38]:
'red'
In [39]:
r2.color
Out[39]:
'red'
In [40]:
RedPoint.color = "white"
In [41]:
r1.color
Out[41]:
'white'
In [42]:
r2.color
Out[42]:
'white'
In [43]:
r1.x
Out[43]:
0
In [44]:
r1.z = 30
In [45]:
r1.color = "red"
In [46]:
del r1.color
In [47]:
r1.color
Out[47]:
'white'
In [48]:
r1.__dict__
Out[48]:
{'x': 0, 'y': 0, 'z': 30}
In [50]:
Point.__dict__
Out[50]:
mappingproxy({'__dict__': <attribute '__dict__' of 'Point' objects>,
              '__doc__': None,
              '__init__': <function __main__.Point.__init__>,
              '__module__': '__main__',
              '__weakref__': <attribute '__weakref__' of 'Point' objects>,
              'color': 'red'})
In [52]:
type(Point.__dict__['__weakref__'])
Out[52]:
getset_descriptor
In [53]:
class Circle:
    
    def __init__(self, radius):
        self.radius = radius
        
    def area(self):
        return 3.14*self.radius**2
    
    
class Sqaure:
    
    def __init__(self, side):
        self.side = side
        
    def area(self):
        return self.side**2
    
    
class Traingle:
    
    def __init__(self, a, b, c):
        self.a = a
        self.b = b
        self.c = c
In [54]:
shapes = [Circle(1), Circle(2), Sqaure(2), Sqaure(5)]
In [56]:
areas = [s.area() for s in shapes ]
In [57]:
areas
Out[57]:
[3.14, 12.56, 4, 25]
In [58]:
shapes = [Circle(1), Circle(2), Sqaure(2), Sqaure(5), Traingle(2,3,4)]
In [60]:
[s.area() for s in shapes]
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-60-a9e10ed9f43d> in <module>()
----> 1 [s.area() for s in shapes]

<ipython-input-60-a9e10ed9f43d> in <listcomp>(.0)
----> 1 [s.area() for s in shapes]

AttributeError: 'Traingle' object has no attribute 'area'
In [70]:
def list_attributes(obj):
    for k,v in vars(obj).items():
        if not callable(v) and not k.startswith("_"):
            print(type(v), k)
In [71]:
list_attributes(r1)
<class 'int'> x
<class 'int'> y
<class 'int'> z
In [72]:
list_attributes(Point)
<class 'str'> color

Cutomizing classes

In [73]:
class Pair:
    
    def __init__(self, x, y):
        self.x = x
        self.y = y
        
    def __repr__(self):
        return "Pair({}, {})".format(self.x, self.y)
    
    def __str__(self):
        return "<{},{}>".format(self.x, self.y)
In [75]:
p1 = Pair(2,3)
In [76]:
p1
Out[76]:
Pair(2, 3)
In [77]:
print(p1)
<2,3>
In [78]:
class Pair:
    
    def __init__(self, x, y):
        self.x = x
        self.y = y
        
    def __repr__(self):
        return "Pair({0.x!r}, {0.y!r})".format(self)
    
    def __str__(self):
        return "<{},{}>".format(self.x, self.y)
In [79]:
Pair(2,3)
Out[79]:
Pair(2, 3)
In [80]:
Pair(Pair(0,0), Pair(2,3))
Out[80]:
Pair(Pair(0, 0), Pair(2, 3))
In [87]:
class Pair:
    
    def __init__(self, x, y):
        self.x = x
        self.y = y
        
    def __repr__(self):
        return "Pair({0.x!r}, {0.y!r})".format(self)
    
    def __str__(self):
        return "<{},{}>".format(self.x, self.y)
    
    def __mul__(self, n):
        return Pair(self.x*n, self.y*n)
    
    def __rmul__(self, n):
        return Pair(self.x*n, self.y*n)
    
    def __eq__(self, p):
        return p.x==self.x and p.y==self.y
In [84]:
Pair(2,3)*2
Out[84]:
Pair(4, 6)
In [86]:
2*Pair(3,4)
Out[86]:
Pair(6, 8)
In [88]:
Pair(2,3) == Pair(4,5)
Out[88]:
False
In [89]:
Pair(2,3) == Pair(2,3)
Out[89]:
True
In [123]:
class Pair:
    
    def __init__(self, x, y):
        self.x = x
        self.y = y
        
    def __repr__(self):
        return "Pair({0.x!r}, {0.y!r})".format(self)
    
    def __str__(self):
        return "<{},{}>".format(self.x, self.y)
    
    def __mul__(self, n):
        return Pair(self.x*n, self.y*n)
    
    def __rmul__(self, n):
        return Pair(self.x*n, self.y*n)
    """
    def __eq__(self, p):
        return p.x==self.x and p.y==self.y
    """
    def __getitem__(self, key):
        if key in ['x','y']:
            return getattr(self, key)
        else:
            raise AttributeError("No such attribute", key)
    def __setitem__(self, key, value):
        if key in ['x','y']:
            setattr(self, key, value)
        else:
            raise AttributeError("You can not set ", key)
In [124]:
p1 = Pair(3,4)
p2 = Pair(3,4)
In [125]:
p1 == p2
Out[125]:
False
In [126]:
Pair(3,4) == Pair(3,4)
Out[126]:
False
In [127]:
p1['x'] 
Out[127]:
3
In [128]:
p1['x'] = 5
In [129]:
p1
Out[129]:
Pair(5, 4)
In [122]:
getattr(p1, "x")
Out[122]:
5
In [130]:
p1['z']
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-130-de46436dab54> in <module>()
----> 1 p1['z']

<ipython-input-123-439eb0301717> in __getitem__(self, key)
     24             return getattr(self, key)
     25         else:
---> 26             raise AttributeError("No such attribute", key)
     27     def __setitem__(self, key, value):
     28         if key in ['x','y']:

AttributeError: ('No such attribute', 'z')

controlling attributes

In [142]:
class Sealed:
    
    def __init__(self, x):
        self.x = x
        
    def __setattr__(self, key, value):
        if key != "x":
            raise AttributeError("No chance!")
        self.__dict__['x'] = value
In [143]:
s = Sealed(3)
In [144]:
s.x = 5
In [141]:
s.y = 4
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-141-756fd3447993> in <module>()
----> 1 s.y = 4

<ipython-input-138-4d490099ddcc> in __setattr__(self, key, value)
      6     def __setattr__(self, key, value):
      7         if key != "x":
----> 8             raise AttributeError("No chance!")
      9         self.__dict__['x'] = value

AttributeError: No chance!

Problem

  • Write a class which is immutable
In [145]:
t = (1,2,3,4)
In [146]:
t[0] = 2
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-146-29b3302c4f70> in <module>()
----> 1 t[0] = 2

TypeError: 'tuple' object does not support item assignment
In [147]:
class Immutable:
    
    def __init__(self, x, y):
        self.__dict__['x'] = x
        self.__dict__['y'] = y
        
    def __setattr__(self, k, v):
        raise AttributeError("You can not edit this object!")
In [148]:
class Upper:
        
    def __getattr__(self, key):
        return key.upper()
In [149]:
u = Upper()
In [150]:
u.python
Out[150]:
'PYTHON'
In [151]:
m = Immutable(2,3)
In [152]:
m.x = 3
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-152-12141dafc717> in <module>()
----> 1 m.x = 3

<ipython-input-147-8673846021d0> in __setattr__(self, k, v)
      6 
      7     def __setattr__(self, k, v):
----> 8         raise AttributeError("You can not edit this object!")

AttributeError: You can not edit this object!

Inheritance

In [160]:
class A:
    def __init__(self):
        print("A.__init__")
        
class B(A):
    
    def __init__(self):
        print("B__init__")
        #A.__init__(self)
        super().__init__()
        
        
class C(B):
    
    def __init__(self):
        print("C.__init__")
        #B.__init__(self)
        super().__init__()
        
class D(C,B):
    
    def __init__(self):
        print("D.__init__")
        #C.__init__(self)
        #B.__init__(self)
        super().__init__()
In [161]:
c = D()
D.__init__
C.__init__
B__init__
A.__init__
In [163]:
D.mro()
Out[163]:
[__main__.D, __main__.C, __main__.B, __main__.A, object]
In [164]:
type(D)
Out[164]:
type
In [165]:
type(r1)
Out[165]:
__main__.RedPoint
In [166]:
type(RedPoint)
Out[166]:
type
In [175]:
class Meta(type):
    pass
In [176]:
type(Meta)
Out[176]:
type
In [177]:
class SubMeta(Meta):
    pass
In [178]:
type(SubMeta)
Out[178]:
type
In [179]:
class SubMeta1(metaclass=Meta):
    pass
In [180]:
type(SubMeta1)
Out[180]:
__main__.Meta
In [198]:
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 SingltonTop(metaclass=Singleton):
    
    pass
('SingltonTop', (), {'__module__': '__main__', '__qualname__': 'SingltonTop'})
{}
In [199]:
x = SingltonTop()
In [195]:
y = X()
In [196]:
x == y
Out[196]:
True
In [197]:
x is y
Out[197]:
True
In [ ]:
 
In [202]:
XYZ = type('XYZ', (), {'__module__': '__main__', '__qualname__': 'XYZ'})
In [203]:
type(XYZ)
Out[203]:
type
In [205]:
x = XYZ()
In [206]:
type(x)
Out[206]:
__main__.XYZ
In [207]:
from abc import ABCMeta, abstractmethod
In [208]:
class Shape(metaclass=ABCMeta):
    
    @abstractmethod
    def area(self):
        
In [209]:
class Circle(Shape):
    
    def __init__(self, radius):
        self.radius = radius
        
    
In [210]:
Circle()
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-210-17593ff9741d> in <module>()
----> 1 Circle()

TypeError: Can't instantiate abstract class Circle with abstract methods area
In [212]:
class Circle(Shape):
    
    def __init__(self, radius):
        self.radius = radius
        
    def area(self):
        return 3.14*radius**2
In [214]:
c = Circle(3)
In [215]:
type(Circle)
Out[215]:
abc.ABCMeta
In [217]:
class Foo(metaclass=Shape):
    pass
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-217-d60d5445277d> in <module>()
----> 1 class Foo(metaclass=Shape):
      2     pass

TypeError: object() takes no parameters

Descriptors

In [223]:
class Integer:
    
    def __init__(self, name):
        self.name = name
        
    def __get__(self, instance, cls):
        print("__get__", instance, cls)
        if not instance:
            return self
        else:
            return instance.__dict__[self.name]
        
    def __set__(self, instance, value):
        print("__set__", instance, value)
        if isinstance(value, int):
            instance.__dict__[self.name] = value
            
    def __delete__(self, instance):
        print("__delete__")
        raise Exception("Can not delete", self.name)
        
        

class Point:
    x = Integer('x')
    y = Integer('y')
    
    def __init__(self, x, y):
        self.x = x
        self.y = y
In [224]:
p = Point(2, 3)
__set__ <__main__.Point object at 0x7fde683359e8> 2
__set__ <__main__.Point object at 0x7fde683359e8> 3
In [222]:
p.x
__get__ <__main__.Point object at 0x7fde683096d8> <class '__main__.Point'>
Out[222]:
2
In [225]:
x = 2
In [226]:
Point.x
__get__ None <class '__main__.Point'>
Out[226]:
<__main__.Integer at 0x7fde683358d0>
In [227]:
p.x
__get__ <__main__.Point object at 0x7fde683359e8> <class '__main__.Point'>
Out[227]:
2
In [228]:
Point.__dict__['x'] = 
Out[228]:
<__main__.Integer at 0x7fde683358d0>
In [229]:
data = 5
In [230]:
type(data)
Out[230]:
int

problem

  • Implement your own property decorator using decriptor
In [231]:
x = Integer('x')
In [232]:
x = 3
In [233]:
type(x)
Out[233]:
int
In [259]:
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 set email like this")
        
class my_property:
    
    def __init__(self, f):
        self.func = f
        
    def __get__(self, instance, cls):
        if not instance:
            return self
        return self.func(instance)
    
    
class Person_:
    
    def __init__(self, name):
        self.name = name
        
    @my_property
    def email(self):
        return "@".join([self.name.split()[0], "arcesium.com"])
    
    @my_property
    def surname(self):
        return self.name.split()[1]
    
In [260]:
p = Person("vikrant")
In [261]:
p.email
Out[261]:
'vikrant@arcesium.com'
In [262]:
p.email = "xyz@xyz.com"
---------------------------------------------------------------------------
Exception                                 Traceback (most recent call last)
<ipython-input-262-e1448b4c5390> in <module>()
----> 1 p.email = "xyz@xyz.com"

<ipython-input-259-c4c594f60e7f> in email(self, value)
     10     @email.setter
     11     def email(self, value):
---> 12         raise Exception("You can not set email like this")
     13 
     14 class my_property:

Exception: You can not set email like this
In [263]:
p = Person_("Vikrant Patil")
In [264]:
p.email
Out[264]:
'Vikrant@arcesium.com'
In [265]:
p.surname
Out[265]:
'Patil'
In [2]:
import time
class Timer:
    
    def __init__(self):
        self._start = 0
        self._end = 0
    
    def start(self):
        self._start = time.time()
        
    def stop(self):
        self._end = time.time()
        
    def get_time_taken(self):
        return self._end - self._start
    
class ContextTimer(Timer):
    
    def reset(self):
        self._start = 0
        self._end = 0
    
    def __enter__(self):
        self.reset()
        self.start()
        return self
        
    def __exit__(self, exc_ty, exc_val, tb):
        self.stop()
        print("Time take in with block: ", self.get_time_taken())
        
In [267]:
with ContextTimer() as t:
    time.sleep(2)
Time take in with block:  2.001568555831909

Threading

In [268]:
import threading
In [270]:
def tick(n):
    for i in range(n):
        print("Tick-",i)
        time.sleep(1)

def reversetick(n):
    for i in range(n):
        print("Tick-down-",n-i)
        time.sleep(1)

t1 = threading.Thread(target=tick, args=(10,))
t2 = threading.Thread(target=reversetick, args=(10,))
t1.start()
t2.start()
Tick- 0
Tick-down- 10
Tick- 1
Tick-down- 9
Tick- 2
Tick-down- 8
Tick- 3
Tick-down- 7
Tick-Tick-down-  46

Tick-down-Tick-  55

Tick-down-Tick-  46

Tick-down- 3
Tick- 7
Tick-down- Tick-2 
8
Tick-down-Tick-  9
1
In [271]:
def tick(n):
    for i in range(n):
        print("Tick-",i)
        time.sleep(1)

def reversetick(n):
    for i in range(n):
        print("Tick-down-",n-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()
Tick- 0
Tick-down- 10
Tick- 1
Tick-down- 9
Tick- 2
Tick-down- 8
Tick- 3
Tick-down- 7
Tick- 4
Tick-down- 6
Tick- 5
Tick-down- 5
Tick- 6
Tick-down- 4
Tick- 7
Tick-down- 3
Tick- 8
Tick-down- 2
Tick-Tick-down- 9
 1
In [272]:
event = threading.Event()
In [273]:
event.isSet()
Out[273]:
False
In [274]:
event.set()
In [275]:
event.isSet()
Out[275]:
True
In [281]:
def tick(event):
    i = 0
    while not event.isSet():
        print("Tick-", i)
        time.sleep(2)
        i += 1
In [282]:
event = threading.Event()
In [283]:
t = threading.Thread(target=tick, args=(event,))
In [284]:
t.start()
Tick- 0
Tick- 1
Tick- 2
Tick- 3
In [285]:
event.set()
In [286]:
lock = threading.Lock()
In [288]:
def producer(data, lock, event):
    
    while not event.isSet():
        lock.acquire()
        data.append(1) # protected by lock
        print("Produced ...")
        lock.release()
        time.sleep(1)
        
def consumer(data, lock, event):
    
    while not event.isSet():
        lock.acquire()
        data.pop()
        print("Consumed ...")
        lock.release()
        time.sleep(1)
        
def observer(data, lock, event):
    while not event.isSet():
        print(data)
        time.sleep(1)
In [289]:
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, 1, 2, 3, 4, 1]
Produced ...
[0, 1, 2, 3, 4, 1, 1]
Produced ...
[0, 1, 2, 3, 4, 1, 1, 1]
Produced ...
Consumed ...
[0, 1, 2, 3, 4, 1, 1, 1]
Produced ...
Consumed ...
[0, 1, 2, 3, 4, 1, 1, 1]
Produced ...
Consumed ...
[0, 1, 2, 3, 4, 1, 1, 1]
Produced ...
Consumed ...
[0, 1, 2, 3, 4, 1, 1, 1]Produced ...
Consumed ...

Produced ...[0, 1, 2, 3, 4, 1, 1, 1, 1]

Consumed ...
[0, 1, 2, 3, 4, 1, 1, 1]
Produced ...
Consumed ...
[0, 1, 2, 3, 4, 1, 1, 1]
Produced ...
Consumed ...
[0, 1, 2, 3, 4, 1, 1, 1]
Produced ...
Consumed ...
[0, 1, 2, 3, 4, 1, 1, 1]
Produced ...
Consumed ...
[0, 1, 2, 3, 4, 1, 1, 1]
Produced ...
Consumed ...
In [290]:
event.set()

threadpool/processpool

In [3]:
from multiprocessing.pool import ThreadPool
In [292]:
pool = ThreadPool(4)
In [295]:
import requests

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

def threaded_download(n):
    pool = ThreadPool(n)
    url = "http://httpbin.org/get"
    urls = [url]*10
    res = pool.map(download, urls)
    
for i in range(1,9):
    with ContextTimer() as t:
        threaded_download(i)
    
Time take in with block:  46.421125173568726
Time take in with block:  5.2353270053863525
Time take in with block:  4.18519139289856
Time take in with block:  2.894702434539795
Time take in with block:  3.0628936290740967
Time take in with block:  3.4596893787384033
Time take in with block:  2.669735908508301
Time take in with block:  4.912823677062988
In [294]:
type(res)
Out[294]:
list
In [296]:
help(pool.apply_async)
Help on method apply_async in module multiprocessing.pool:

apply_async(func, args=(), kwds={}, callback=None, error_callback=None) method of multiprocessing.pool.ThreadPool instance
    Asynchronous version of `apply()` method.

In [4]:
from multiprocessing.pool import Pool # process pool
In [299]:
import requests

def saxpy(n):
    s = 0
    for i in range(n):
        for j in range(n):
            s += i*j*1.0
    return s

def threaded_task(n):
    pool = ThreadPool(n)
    args = [1000]*10
    res = pool.map(saxpy, args)
    
for i in range(1,5):
    with ContextTimer() as t:
        threaded_task(i)
    
Time take in with block:  1.9235901832580566
Time take in with block:  1.7628319263458252
Time take in with block:  1.7623236179351807
Time take in with block:  1.69461989402771
In [5]:
def saxpy(n):
    s = 0
    for i in range(n):
        for j in range(n*10):
            s += i*j*1.0
    return s

def threaded_task(n):
    pool = Pool(n)
    args = [1000]*10
    res = pool.map(saxpy, args)
    
for i in range(1,5):
    with ContextTimer() as t:
        threaded_task(i)
Time take in with block:  16.886324405670166
Time take in with block:  10.361664056777954
Time take in with block:  9.685546398162842
Time take in with block:  9.993159055709839

cython

In [7]:
%load_ext Cython
In [9]:
%%cython --annotate

def add(x, y):
    return x+y
Out[9]:
Cython: _cython_magic_c66d8063f3feb6567150f363c7e4e7ac.pyx

Generated by Cython 0.26

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 add(x, y):
/* Python wrapper */
static PyObject *__pyx_pw_46_cython_magic_c66d8063f3feb6567150f363c7e4e7ac_1add(PyObject *__pyx_self, PyObject *__pyx_args, PyObject *__pyx_kwds); /*proto*/
static PyMethodDef __pyx_mdef_46_cython_magic_c66d8063f3feb6567150f363c7e4e7ac_1add = {"add", (PyCFunction)__pyx_pw_46_cython_magic_c66d8063f3feb6567150f363c7e4e7ac_1add, METH_VARARGS|METH_KEYWORDS, 0};
static PyObject *__pyx_pw_46_cython_magic_c66d8063f3feb6567150f363c7e4e7ac_1add(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("add (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] = PyDict_GetItem(__pyx_kwds, __pyx_n_s_x)) != 0)) kw_args--;
        else goto __pyx_L5_argtuple_error;
        CYTHON_FALLTHROUGH;
        case  1:
        if (likely((values[1] = PyDict_GetItem(__pyx_kwds, __pyx_n_s_y)) != 0)) kw_args--;
        else {
          __Pyx_RaiseArgtupleInvalid("add", 1, 2, 2, 1); __PYX_ERR(0, 2, __pyx_L3_error)
        }
      }
      if (unlikely(kw_args > 0)) {
        if (unlikely(__Pyx_ParseOptionalKeywords(__pyx_kwds, __pyx_pyargnames, 0, values, pos_args, "add") < 0)) __PYX_ERR(0, 2, __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("add", 1, 2, 2, PyTuple_GET_SIZE(__pyx_args)); __PYX_ERR(0, 2, __pyx_L3_error)
  __pyx_L3_error:;
  __Pyx_AddTraceback("_cython_magic_c66d8063f3feb6567150f363c7e4e7ac.add", __pyx_clineno, __pyx_lineno, __pyx_filename);
  __Pyx_RefNannyFinishContext();
  return NULL;
  __pyx_L4_argument_unpacking_done:;
  __pyx_r = __pyx_pf_46_cython_magic_c66d8063f3feb6567150f363c7e4e7ac_add(__pyx_self, __pyx_v_x, __pyx_v_y);

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

static PyObject *__pyx_pf_46_cython_magic_c66d8063f3feb6567150f363c7e4e7ac_add(CYTHON_UNUSED PyObject *__pyx_self, PyObject *__pyx_v_x, PyObject *__pyx_v_y) {
  PyObject *__pyx_r = NULL;
  __Pyx_RefNannyDeclarations
  __Pyx_RefNannySetupContext("add", 0);
/* … */
  /* function exit code */
  __pyx_L1_error:;
  __Pyx_XDECREF(__pyx_t_1);
  __Pyx_AddTraceback("_cython_magic_c66d8063f3feb6567150f363c7e4e7ac.add", __pyx_clineno, __pyx_lineno, __pyx_filename);
  __pyx_r = NULL;
  __pyx_L0:;
  __Pyx_XGIVEREF(__pyx_r);
  __Pyx_RefNannyFinishContext();
  return __pyx_r;
}
/* … */
  __pyx_tuple_ = PyTuple_Pack(2, __pyx_n_s_x, __pyx_n_s_y); 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_c66d8063f3feb6567150f363c7e4e7ac_1add, NULL, __pyx_n_s_cython_magic_c66d8063f3feb65671); 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_add, __pyx_t_1) < 0) __PYX_ERR(0, 2, __pyx_L1_error)
  __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+3:     return x+y
  __Pyx_XDECREF(__pyx_r);
  __pyx_t_1 = PyNumber_Add(__pyx_v_x, __pyx_v_y); 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;
In [10]:
%%timeit 
add(100,200)
169 ns ± 17.4 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
In [11]:
def add_(x,y):
    return x+y
In [12]:
%%timeit 
add_(100,200)
207 ns ± 18.9 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
In [23]:
%%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=",")
Overwriting cfib.pyx
In [24]:
%%file setup.py
from distutils.core import setup
from Cython.Build import cythonize

setup(ext_modules = cythonize("cfib.pyx"))
Overwriting setup.py
In [25]:
!python setup.py build_ext --inplace
Compiling cfib.pyx because it changed.
[1/1] Cythonizing cfib.pyx
running build_ext
building 'cfib' extension
gcc -pthread -Wsign-compare -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes -fPIC -I/home/vikrant/usr/local/anaconda3/include/python3.6m -c cfib.c -o build/temp.linux-x86_64-3.6/cfib.o
gcc -pthread -shared -L/home/vikrant/usr/local/anaconda3/lib -Wl,-rpath=/home/vikrant/usr/local/anaconda3/lib,--no-as-needed build/temp.linux-x86_64-3.6/cfib.o -L/home/vikrant/usr/local/anaconda3/lib -lpython3.6m -o /home/vikrant/trainings/2018/arcesium-advanced-oct/cfib.cpython-36m-x86_64-linux-gnu.so
In [26]:
!ls cfib.cpython-36m-x86_64-linux-gnu.so
cfib.cpython-36m-x86_64-linux-gnu.so
In [27]:
import cfib
In [28]:
cfib.fib(100)
2,3,5,8,13,21,34,55,89,144,
In [29]:
cfib.fib(1000)
2,3,5,8,13,21,34,55,89,144,233,377,610,987,1597,

Calling c function in python

In [30]:
%%file sample.c

int add(int x, int y){
    return(x+y);
}
Writing sample.c
In [31]:
!gcc -c sample.c
In [32]:
!gcc -shared -o libsample.so sample.o
In [33]:
!ls libsample.so
libsample.so
In [34]:
import ctypes
In [37]:
import ctypes
import os

_file = "libsample.so"
_path = os.path.join(os.getcwd(), _file)
_mod = ctypes.cdll.LoadLibrary(_path)

add = _mod.add
add.argtypes = (ctypes.c_int, ctypes.c_int)
add.restype = ctypes.c_int

add(5, 8)
Out[37]:
13
In [38]:
%%file struct.c

typedef struct Pair{
    double x,y;
} Pair;
Writing struct.c
In [39]:
class Pair(ctypes.Structure):
    _fields_ = [('x', ctypes.c_double),
                ('y', ctypes.c_double)]

Numba

In [40]:
from numba import jit
In [41]:
@jit
def add(x, y):
    return x+y
In [45]:
@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 [46]:
@jit(nopython=True)
def saxpy(n):
    s = 0
    for i in range(n):
        for j in range(n):
            s += i*j*1.0
    return s
In [48]:
@jit(nopython=True, parallel=True)
def saxpy(n):
    s = 0
    for i in range(n):
        for j in range(n):
            s += i*j*1.0
    return s

References

  • python docs
  • google for meta prgramming by david beazley (viedo/slides)
  • Python Cookbook by David Beazly and other
  • pandas, numba, cython refer library home page
  • SICP (book)
  • Anand Chitipoth Python Practice Book

Coventions

  • functions and attributes are usually small case
  • Class names are Camel case
  • private attributes start with _
  • get_
In [ ]: