Aug 16-18, 2017
Anand Chitipothu & Vikrant Patil
These notes are available online at http://notes.pipal.in/2017/vmware-advpy
© Pipal Academy LLP
%%file sq1.py
def square(x):
return x*x
def test():
print(square(3))
if __name__ == "__main__":
test()
!python sq1.py
%%file sq2.py
def square(x):
return x*x
def test():
if square(3) == 9:
print("PASSED")
if __name__ == "__main__":
test()
!python sq2.py
Python has an assert statement for doing this.
%%file sq2.py
def square(x):
return x*x
def test_square():
assert square(0) == 0
assert square(3) == 9
assert square(-3) == 9
if __name__ == "__main__":
test_square()
!python sq2.py
!py.test sq2.py
You can install py.test using:
pip install pytest
%%file now.py
import datetime
def now():
return datetime.datetime.now()
def weekday():
t = now()
return t.strftime("%A")
if __name__ == "__main__":
print(weekday())
!python now.py
%%file test_now.py
import now
import datetime
def test_weekday(monkeypatch):
faketime = 2010, 1, 1
def fakenow():
return datetime.datetime(*faketime)
monkeypatch.setattr(now, "now", fakenow)
faketime = 2010, 1, 1
assert now.weekday() == "Friday"
faketime = 2010, 1, 2
assert now.weekday() == "Saturday"
!py.test test_now.py
def wordcount(fileobj):
"""Counts the number of words in the given file.
"""
return len(fileobj.read().split())
What happens if I pass a string as argument to wordcount?
wordcount("hello world")
class FakeFile:
def read(self):
return "Hello world"
wordcount(FakeFile())
Let us look at a simple class.
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
def getx(self):
return self.x
p = Point(3, 4)
print(p.x, p.y)
isinstance(p, Point)
p.getx()
The above code is equivalant to:
Point.getx(p)
p.__dict__
p.__class__
Point.__dict__
Let us dig a bit deeper...
p = Point(3, 4)
p.x
p.y
p.z = 5
class Point2:
pass
def init_point2(self, x, y):
self.x = x
self.y = y
p2 = Point2()
init_point2(p2, 3, 4)
print(p2.x, p2.y)
p2.xx
Q: How to stop adding new attributes objects of a class.
class Point(object):
__slots__ = ["x", "y"]
p = Point()
p.x = 1
p.y = 2
p.z = 3
Q: Does python has private methods or attributes?
No, but anything that starts with an underscore is considered private implementation detail.
class Point:
x = 0
y = 0
p1 = Point()
p2 = Point()
print(p1.x, p1.y)
print(p2.x, p2.y)
p1.x = 1
p1.y = 2
print(p1.x, p1.y)
print(p2.x, p2.y)
Point.x = 10
print(p1.x, p1.y)
print(p2.x, p2.y)
import os
def count_lines(dirpath):
total = 0
for f in os.listdir(dirpath):
if os.path.isfile(f):
path = os.path.join(dirpath, f)
lines = len(open(path).readlines())
total += lines
return total
count_lines(".")
count_lines(".")
count_lines(".")
import os
import time
def count_lines(dirpath):
total = 0
t0 = time.time()
for f in os.listdir(dirpath):
if os.path.isfile(f):
path = os.path.join(dirpath, f)
lines = len(open(path).readlines())
total += lines
t1 = time.time()
print("time taken", t1-t0)
return total
count_lines(".")
%%file timer.py
import time
tstart = 0
tstop = 0
def start():
global tstart
tstart = time.time()
def stop():
global tstop
tstop = time.time()
def time_taken():
return tstop-tstart
%%file a.py
import os
import timer
def count_lines(dirpath):
total = 0
timer.start()
for f in os.listdir(dirpath):
if os.path.isfile(f):
path = os.path.join(dirpath, f)
lines = len(open(path).readlines())
total += lines
timer.stop()
print("time taken", timer.time_taken())
return total
if __name__ == "__main__":
print(count_lines("."))
!python a.py
%%file bank0.py
balance = 0
def deposit(amount):
global balance
balance += amount
def withdraw(amount):
global balance
balance -= amount
def get_balance():
return balance
def main():
deposit(100)
withdraw(40)
print(get_balance())
deposit(20)
print(get_balance())
if __name__ == "__main__":
main()
!python bank0.py
How to have multiple bank accounts?
%%file bank1.py
def make_account():
return {"balance": 0}
def deposit(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()
deposit(a1, 100)
deposit(a2, 50)
withdraw(a1, 40)
withdraw(a2, 30)
print(get_balance(a1), get_balance(a2))
if __name__ == "__main__":
main()
!python bank1.py
Let's try to the same with classes.
%%file bank2.py
class BankAccount:
def __init__(self):
self.balance = 0
def deposit(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.deposit(100)
a2.deposit(50)
a1.withdraw(40)
a2.withdraw(30)
print(a1.get_balance(), a2.get_balance())
if __name__ == "__main__":
main()
!python bank2.py
Q: Can you make a class immutable?
Yes.
In Python, you can control every operation that is done on an object.
x = 3
x + 2
x.__add__(2)
class UpperCase:
def __getattr__(self, name):
return name.upper()
x = UpperCase()
x.name
class Sealed:
def __setattr__(self, name, value):
raise Exception("No chance!")
s = Sealed()
s.x = 1
Problem: Write a class Timer to measure the time taken in a task. The class should have start and stop methods and it should be able to find time taken between them.
t = Timer()
t.start()
do_something()
t.stop()
print("Time taken:", t.time_taken())
%%file five.txt
one
two
three
four
five
class Formatter:
def format_text(self, text):
"""Formats the given text.
This implementation returns the same text,
but sub classes can override this method to
provide different way 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"))
Let us extend the formatting functionality to support formatting individul lines.
class LineFormatter(Formatter):
def format_text(self, text):
lines = text.splitlines()
lines = [self.format_line(line) for line in lines]
return "\n".join(lines)
def format_line(self, line):
return line
class PrefixFormatter(LineFormatter):
def __init__(self, prefix):
self.prefix = prefix
def format_line(self, line):
return self.prefix + line
f = PrefixFormatter("[INFO] ")
print(f.format_line("hello"))
print(f.format_text("a\nb\nc"))
print(f.format_file("five.txt"))
class Person(object):
def __init__(self, firstname, lastname):
self.firstname = firstname
self.lastname = lastname
@property
def fullname(self):
print("calling fullname ...")
return self.firstname + " " + self.lastname
p = Person("Alice", "whoever")
p.firstname
p.lastname
p.fullname
p.lastname = "xyz"
p.fullname
class Person(object):
def __init__(self, firstname, lastname, email):
self.firstname = firstname
self.lastname = lastname
self.email = email
@property
def fullname(self):
print("calling fullname ...")
return self.firstname + " " + self.lastname
p = Person("Alice", "Bob", "alice@example.com")
p.email
p.email = "not-an-email?"
class Person(object):
def __init__(self, firstname, lastname, email):
self.firstname = firstname
self.lastname = lastname
self._email = email
@property
def email(self):
return self._email
@email.setter
def email(self, value):
if "@" not in value:
raise ValueError("Invalid email: " + repr(value))
self._email = value
@property
def fullname(self):
print("calling fullname ...")
return self.firstname + " " + self.lastname
p = Person("Alice", "Bob", "alice@example.com")
p.email
p.email = "foo@example.com"
What if I want to add a work_email field to the Person class?
Descriptors are special kind of objects.
class Zero(object):
def __get__(self, obj, cls):
if obj is None:
return self
print("Zero.__get__")
return 0
def __repr__(self):
return "<Descriptor Zero>"
class Foo:
x = Zero()
f = Foo()
f.x
When an attribute of an object is called and it's value is a descriptor, then __get__ of that descriptor is called.
Foo.__dict__
Foo.x
Foo.x.__get__(f, Foo)
class Email(object):
def __get__(self, obj, cls):
if obj is None:
return self
print("__get__")
return obj.__dict__[self]
def __set__(self, obj, value):
print("__set__", value)
if "@" not in value:
raise ValueError("Not a valid e-mail: " + repr(email))
obj.__dict__[self] = value
class Person(object):
email = Email()
work_email = Email()
def __init__(self, email, work_email):
self.email = email
self.work_email = work_email
p = Person("alice@example.com", "work@example.com")
p.email
Problem: Implement a my_property decorator that works like built-in property.
class my_property(object):
def __init__(self, func):
self.func = func
def __get__(self, obj, cls):
if obj is None:
return self
print("my_property.__get__")
return self.func(obj)
class Person(object):
@my_property
def hello(self):
return "Helllo"
p = Person()
p.hello
class Person(object):
def __init__(self, firstname, lastname):
self.firstname = firstname
self.lastname = lastname
@staticmethod
def parse(fullname):
first, last = fullname.split(" ", 1)
return Person(first, last)
p = Person.parse("Guido van Rossum")
p.parse("Guido van Rossum")
Person.__dict__
import datetime
class MyDateTime(datetime.datetime):
pass
datetime.datetime.now()
MyDateTime.now()
class A(object):
def __init__(self, x):
self.x = x
def __repr__(self):
return "<{} {}>".format(self.__class__.__name__, repr(self.x))
@staticmethod
def parse1(value):
return A(int(value))
@classmethod
def parse2(cls, value):
return cls(int(value))
class B(A):
pass
a = A.parse1("123")
a
b = B.parse1("123")
b
a = A.parse2("123")
b = B.parse2("123")
print(a, b)
Major differences:
%%file py2.py
for c in "helloworld":
print c,
f = open("/tmp/a.txt", "w")
for c in "helloworld":
print >> f, c
f.close()
!python2.7 py2.py
!cat /tmp/a.txt
%%file py3.py
for c in "helloworld":
print(c, end=" ")
f = open("/tmp/b.txt", "w")
for c in "helloworld":
print(c, file=f)
f.close()
!python3 py3.py
!cat /tmp/b.txt
%%file x1.py
from __future__ import print_function
print("hello", "world", sep="-")
!python3 x1.py
!python2.7 x1.py
%%file x2.py
try:
from urllib.request import urlopen
except ImportError:
# python 2
from urllib import urlopen
response = urlopen("http://httpbin.org/get")
print(response.read().decode('utf-8'))
!python3 x2.py
!python2.7 x2.py
%%file utils.py
try:
from urllib.request import urlopen
except ImportError:
# python 2
from urllib import urlopen
%%file x2a.py
from utils import urlopen
response = urlopen("http://httpbin.org/get")
print(response.read().decode('utf-8'))
%%file t1.py
import threading
def task():
print("Hello ", threading.currentThread().getName())
def main():
t1 = threading.Thread(target=task)
t1.start()
t1.join()
if __name__ == "__main__":
main()
!python t1.py
Let us run it 10 threads
%%file t2.py
import threading
def task():
print("Hello ", threading.currentThread().getName())
def main():
threads = [threading.Thread(target=task) for i in range(10)]
for t in threads:
t.start()
for t in threads:
t.join()
if __name__ == "__main__":
main()
!python t2.py
%%file counter.py
import threading
class Counter:
def __init__(self):
self.count = 0
def tick(self):
self.count += 1
def task(counter, n):
for i in range(n):
counter.tick()
def main():
counter = Counter()
n = 100000
nthreads = 10
threads = [threading.Thread(target=task, args=(counter, n)) for i in range(nthreads)]
for t in threads:
t.start()
for t in threads:
t.join()
print(counter.count)
if __name__ == "__main__":
main()
!python counter.py
!python counter.py
%%file counter2.py
import threading
class Counter:
def __init__(self):
self.count = 0
self.lock = threading.Lock()
def tick(self):
with self.lock:
self.count += 1
def task(counter, n):
for i in range(n):
counter.tick()
def main():
counter = Counter()
n = 100000
nthreads = 10
threads = [threading.Thread(target=task, args=(counter, n)) for i in range(nthreads)]
for t in threads:
t.start()
for t in threads:
t.join()
print(counter.count)
if __name__ == "__main__":
main()
!python counter2.py
!time python counter.py
!time python counter2.py
!seq 10 | xargs printf "http://httpbin.org/get?x=%d\n"
!seq 10 | xargs printf "http://httpbin.org/get?x=%d\n">urls.txt
%%file sget.py
import sys
from urllib.request import urlopen
def get_urls(filename):
return [line.strip() for line in open(filename)]
def wget(url):
return urlopen(url).read()
def main():
filename = sys.argv[1]
for url in get_urls(filename):
wget(url)
if __name__ == "__main__":
main()
!time python sget.py urls.txt
%%file pget.py
import sys
from sget import get_urls, wget
from multiprocessing.pool import ThreadPool
def main():
filename = sys.argv[1]
concurrency = int(sys.argv[2])
urls = get_urls(filename)
pool = ThreadPool(concurrency)
pool.map(wget, urls)
if __name__ == "__main__":
main()
!time python pget.py urls.txt 1
!time python pget.py urls.txt 2
!time python pget.py urls.txt 4
!time python pget.py urls.txt 5
%%file pcpu.py
import sys
from multiprocessing.pool import Pool
def task(dummy):
sum = 0
for i in range(1000):
for j in range(10000):
sum += 1.0* i *j
return sum
def main():
conc = int(sys.argv[1])
pool = Pool(conc)
pool.map(task, range(10))
if __name__ == "__main__":
main()
!time python pcpu.py 1
!time python pcpu.py 4
!time python pcpu.py 5
!time python pcpu.py 2
Please fill the feedback form to let us know how you felt about the course.
Google for: