Sep 27-29, 2017 Vikrant Patil
These notes are available online at http://notes.pipal.in/2017/vmware-pune-advpy
© Pipal Academy LLP
x = 2 + 3
Names or alias is just a mechanism to get handle to computed or created object
%%file moldule.py
value = 42
novalue = 24
%%file anothermodule.py
value = 0
novalue = "No value"
import moldule
import anothermodule
moldule.value
anothermodule.value
del moldule.value
anothermodule.value
moldule.value
x
Namespace is mapping from names to objects.
amount = 10000
balance = 50000
def withdraw(balance, amount):
balance = balance - amount
return balance
fixdep = withdraw(balance, 1000)
print("balance = ", balance)
print("amount = ", amount)
%%file scopetest.py
hello = "Hello Initial"
def scope_changed():
hello = "Hello from scope_changed"
def change_local():
hello = "hello local"
def change_nonlocal():
hello = "Hello initial inside change_local"
print(hello)
def f():
nonlocal hello
hello = "hello nonlocal"
f()
print(hello)
def change_global():
global hello
hello = "Hello global"
print("Before change_local", hello)
change_local()
print("After change_local", hello)
print("Before change_nonlocal", hello)
change_local()
print("After change_nonlocal", hello)
print("Before change_global", hello)
change_local()
print("After change_global", hello)
print("Initial global hello :", hello)
scope_changed()
print("After :", hello)
!python scopetest.py
Problem : What will be output?
def f(x):
x.append(5)
def g(x):
x = [1,1,,1,1]
l = [1,2,3,4]
f(l)
print(l)
g(l)
print(l)
import math
class Circle:
def __init__(self, radius):
self.radius = radius
def area(self):
return math.pi*self.radius**2
class Square:
def __init__(self, s):
self.s = s
def area(self):
return self.s*self.s
class Triangle:
def __init__(self, base, height):
self.base = base
self.height = height
shapes = [Circle(1), Square(1), Circle(2), Square(2)]
areas = [shape.area() for shape in shapes ]
areas
shapes.append(Triangle(1, 1))
areas = [shape.area() for shape in shapes ]
t = [type(s) for s in shapes]
t
isinstance(shapes[0], Circle)
isinstance(shapes[0], Triangle)
This is called as duck-typing... It looks like duck and qwacks like duck so it must be duck!
python works of atrributes rather types. It will just access the atrribute and if it is not there it will throw exception!
%%file bank0.py
balance = 0
def deposite(amount):
global balance
balance += amount
def withdraw(amount):
global balance
balance -= amount
def get_balance():
return balance
def main():
deposite(100)
withdraw(40)
print(get_balance())
deposite(20)
print(get_balance())
if __name__ == "__main__":
main()
!python bank0.py
%%file bank1.py
def make_account():
return {"balance":0}
def deposite(account, amount):
account['balance'] += amount
def withdraw(account, amount):
account['balance'] -= amount
def get_balance(account):
return account['balance']
def main():
a1 = make_account()
a2 = make_account()
deposite(a1, 100)
withdraw(a1, 40)
deposite(a2, 200)
withdraw(a2, 50)
print(get_balance(a1))
print(get_balance(a2))
if __name__ == "__main__":
main()
!python bank1.py
%%file bank2.py
class BankAccount:
def __init__(self):
self.balance = 0
def deposite(self, amount):
self.balance += amount
def withdraw(self, amount):
self.balance -= amount
def get_balance(self):
return self.balance
def main():
a1 = BankAccount()
a2 = BankAccount()
a1.deposite(100)
a1.withdraw(20)
print(a1.get_balance())
if __name__ == "__main__":
main()
!python bank2.py
class A:
pass
%%file five.txt
one
two
three
four
five
class Formatter:
def format_text(self, text):
"""
Formats the given text
This implementation just returns same text back.
but sub classes should implement this method to
provide different kinds of formatting
"""
return text
def format_file(self, filename):
text = open(filename).read()
return self.format_text(text)
class UpperCaseFormatter(Formatter):
def format_text(self, text):
return text.upper()
f = UpperCaseFormatter()
print(f.format_text("hello"))
print(f.format_file("five.txt"))
class LineFormatter(Formatter):
def format_line(self, line):
return line
def format_text(self, text):
lines = text.splitlines()
lines = [self.format_line(l) for l in lines]
return "\n".join(lines)
class PrefixFormatter(LineFormatter):
def __init__(self, prefix):
self.prefix = prefix
def format_line(self, line):
return self.prefix + line
f = PrefixFormatter(prefix="[INFO]: ")
print(f.format_text("Hello\nworld"))
print(f.format_file("five.txt"))
class ClassA:
value = 42
def f(self):
return "Hello from ClassA"
ClassA
ClassA.value
ClassA.f
x = ClassA()
x
x.value
x.my_own_value = 43
x.f
ClassA.f()
x.f()
ClassA.f(x)
x.another_func = lambda x:x*x
method = x.f
method
method()
str() and repr()
x = (1,2)
str(x)
repr(x)
(1, 2)
class Pair:
def __init__(self, x, y):
self.x = x
self.y = y
def __str__(self):
return "({0}, {1})".format(str(self.x), str(self.y))
def __repr__(self):
return "Pair({0.x!r}, {0.y!r})".format(self)
# 0.x -> x attribute from 0th argument
# repr(x.0) -> 0.x!r for str use 0.x!s
p = Pair(2,3)
p
print(p)
arithmatic operators
class Pair:
def __init__(self, x, y):
self.x = x
self.y = y
def __str__(self):
return "({0}, {1})".format(str(self.x), str(self.y))
def __repr__(self):
return "Pair({0.x!r}, {0.y!r})".format(self)
def __add__(self, p):
return Pair(self.x+p.x, self.y + p.y)
def __sub__(self, p):
return Pair(self.x - p.x, self.y - p.y)
def __rmul__(self, c):
return Pair(self.x*c , self.y*c)
def __mul__(self, c):
return Pair(self.x*c , self.y*c)
def __eq__(self, p):
return self.x==p.x and self.y==p.y
p1 = Pair(1,2)
p2 = Pair(4,5)
p1 + p2
p2 - p1
print(p2 - p1)
p1*3
3 * p1
p1 == p2
p1 == Pair(1,2)
p1['z']
[] operator
class Pair:
def __init__(self, x, y):
self.x = x
self.y = y
def __str__(self):
return "({0}, {1})".format(str(self.x), str(self.y))
def __repr__(self):
return "Pair({0.x!r}, {0.y!r})".format(self)
def __add__(self, p):
return Pair(self.x+p.x, self.y + p.y)
def __sub__(self, p):
return Pair(self.x - p.x, self.y - p.y)
def __rmul__(self, c):
return Pair(self.x*c , self.y*c)
def __mul__(self, c):
return Pair(self.x*c , self.y*c)
def __eq__(self, p):
return self.x==p.x and self.y==p.y
def __getitem__(self, name):
return self.__dict__[name]
def __setitem__(self, name, value):
if name in ["x", "y"]:
self.__dict__[name] = value
else:
raise Exception("Only x, y values can be set")
http://localhost:8888/notebooks/day3.ipynb#p = Pair(2, 3)
p['x'] = 1
p
p['z'] = 3
p.z = 30
p.z = 30
Q: Is it possible to make class immutable?
class UpperCase:
def __getattr__(self, name):
return name.upper()
u = UpperCase()
u.name
class Sealed:
def __setattr__(self, name, value):
raise Exception("No chance!")
s = Sealed()
s.x = 3
s['x'] = 3
class Date:
__slots__ =['year', 'month', 'day']
def __init__(self, year, month, day):
self.year = year
self.month = month
self.day = day
d = Date(2017, 10, 12)
d.x = 60
problem Write a class Timer to measure time taken by some task. The class should have start and stop methods. and shooul be able to get time elapsed between start and stop.
use time.time()
t = Timer()
t.start()
do_some_stuff()
t.stop()
print(t.get_time_taken())
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(self):
return self._end - self._start
t = Timer()
t.start()
for i in range(100000):
i*i*i
t.stop()
print(t.get_time())
class Person:
def __init__(self, name, email):
self._name = name
self._email = email
#getter
@property
def name(self):
return self._name
@name.setter
def name(self, value):
if not isinstance(value, str):
raise TypeError("Expected String")
self._name = value
@name.deleter
def name(self):
raise AttributeError("Can't delete atrribute name")
p = Person("Alice", "alice@wonder.land")
p.name
p.name = "Alex"
p.name = 42
p.name = "Hatter"
del p.name
del p._email
del p._name
p.name
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 TypeError("Expected an 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)
class Point:
x = Integer('x')
y = Integer('y')
def __init__(self, x, y):
self.x = x
self.y = y
p = Point(2, 3)
Point.__dict__
p.x = 3
p.y = "3"
problem: Implement a my_property decorator that works like built in property
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)
class Person:
def __init__(self, name, email):
self._name = name
self._email = email
@my_property
def name(self):
return self._name
p = Person("Alice", "alice@wonder.land")
p.name
class Person(object):
def __init__(self, firstname, lastname):
self.firtname = firstname
self.lastname = lastname
@staticmethod
def parse(fullname):
first, last = fullname.split(" ")
return Person(first, last)
Person.__dict__
import datetime
class MydateTime(datetime.datetime):
pass
datetime.datetime.now()
MydateTime.now()
class A(object):
def __init__(self, x):
self.x = x
@staticmethod
def parse1(value):
return A(int(value))
@classmethod
def parse2(cls, value):
return cls(value)
class B(A):
pass
B.parse1(3)
B.parse2(3)
f = open("context.txt", "w")
f.write("one\n")
f.write("two\n")
f.write("three\n")
f.close()
with open("context.txt", "w") as f:
f.write("one\n")
f.write("two\n")
f.write("three\n")
from socket import socket, AF_INET, SOCK_STREAM
class LazyConnection:
def __init__(self, address, family=AF_INET, type_=SOCK_STREAM):
self.address = address
self.family = family
self.type = type_
self.sock = None
def __enter__(self):
if self.sock is not None:
raise RuntimeError("Already connected")
self.sock = socket(self.family, self.type)
self.sock.connect(self.address)
return self.sock
def __exit__(self, exception_type, exception_value, traceback):
self.sock.close()
self.sock = None
from functools import partial
conn = LazyConnection(("www.python.org", 80))
with conn as s:
#conn.__enter__() executes : connection open
s.send(b'GET /index.html HTTP/1.0\r\n')
s.send(b'Host: www.pyhton.org\r\n')
s.send(b'\r\n')
resp = b''.join(iter(partial(s.recv, 8192), b''))
#conn.__exit__() gets executed
print(resp.decode('utf-8'))
Problem: Write a class ContextTimer which extends from our previous Timer class. This new class should implement context-management protocol. Where in when you enter in **with** block timer starts fresh, and when you exit **with** block, timer stops.
and prints value of time taken within with block
t = ContextTimer()
with t as timer:
for i in range(1000):
for j in range(1000):
s = i*j*1.0
print(t.get_time_taken())
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_ty, exc_val, tb):
self.stop()
print("Time taken in with block: ", self.get_time())
with ContextTimer() as t:
for i in range(1000):
for j in range(1000):
s = i*j*1.0
doom
class Base:
def __init__(self):
print("Base.__init__")
class A(Base):
def __init__(self):
Base.__init__(self)
print("A.__init__()")
class B(Base):
def __init__(self):
Base.__init__(self)
print("B.__init__()")
class C(A,B):
def __init__(self):
#A.__init__(self)
#B.__init__(self)
super().__init__()
print("C.__init__()")
C.__mro__
from threading import Thread, Event
import time
def task(n , event):
while (not event.is_set()) and n>0:
print("T-minus", n)
n -= 1
time.sleep(5)
print("Stopping Bye..")
e = Event()
t = Thread(target=task, args=(10, e))
t.start()
e.set()
References: