[go: up one dir, main page]

0% found this document useful (0 votes)
49 views28 pages

ENGR 101 Introduction To Programming: Week 13

1. The document defines a Time class to represent time values and includes methods to initialize times, print times, convert between time and integer representations, increment times, and add times. 2. It demonstrates defining special methods like __str__ and __add__ to customize how objects are printed and how operators like + work on custom classes. 3. It shows how to implement operator overloading and type-based dispatch to allow different behaviors when operating on different types.

Uploaded by

hudhaifa
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
49 views28 pages

ENGR 101 Introduction To Programming: Week 13

1. The document defines a Time class to represent time values and includes methods to initialize times, print times, convert between time and integer representations, increment times, and add times. 2. It demonstrates defining special methods like __str__ and __add__ to customize how objects are printed and how operators like + work on custom classes. 3. It shows how to implement operator overloading and type-based dispatch to allow different behaviors when operating on different types.

Uploaded by

hudhaifa
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 28

ENGR 101

Introduction to
Programming
Week 13
class Time:
def __init__(self, hour=0, min=0, sec=0):
self.hour = hour
self.minute = min
self.second = sec

def print_time(self):
print str(self.hour) + ':' + str(self.minute) + \
':' + str(self.second)

def time_to_int(self):
total_seconds = self.hour * 3600
total_seconds += self.minute * 60
total_seconds += self.second
return total_seconds

def int_to_time(self, tot_secs):


hour = tot_secs / 3600
minute = (tot_secs % 3600) / 60
second = tot_secs % 60

t = Time(hour, minute, second)


return t

def increment(self, x):


total_secs = self.time_to_int() + x
return self.int_to_time(total_secs)
The __str__ method
• __str__ is a special method, like __init__, that is
supposed to return a string representation of an
object.
# inside class Time:
def __str__(self):
return str(self.hour) + ‘:’ + \
str(self.minute) + ‘:’ + \
str(self.second)
time = Time(9, 45)
print time
9:45:0
Exercise

• Write a str method for the Point class. Create a


Point object and print it.
Operator overloading
• By defining other special methods, you can specify
the behavior of operators on user-defined types.
• For example, if you define a method named
__add__ for the Time class, you can use the +
operator on Time objects.

# inside class Time:


def __add__(self, other):
seconds = self.time_to_int() + other.time_to_int()
return int_to_time(seconds)
Operator overloading
start = Time(9, 45)
duration = Time(1, 35)
print start + duration
11:20:0
• When you apply the + operator to Time objects,
Python invokes __add__.
• When you print the result, Python invokes __str__.
• So there is quite a lot happening behind the
scenes!
Exercise

• Write an add method for the Point class.


Operator overloading

• Changing the behavior of an operator so that it


works with user-defined types is called operator
overloading.
• For every operator in Python there is a
corresponding special method, like __add__.
• For more details, see
https://docs.python.org/2/reference/datamodel.html
Type-based dispatch
• Here are examples that use the + operator with
different types:
start = Time(9, 45)
duration = Time(1, 35)
print start + duration
11:20:0
print start + 5700
????
Type-based dispatch
# inside class Time:
def __add__(self, other):
if isinstance(other, Time):
return self.add_time(other)
else:
return self.increment(other)

def add_time(self, other):


...

def increment(self, seconds):


...
Type-based dispatch
• Here are examples that use the + operator with
different types:
start = Time(9, 45)
duration = Time(1, 35)
print start + duration
11:20:0
print start + 5700
11:20:0
Type-based dispatch
• This implementation of addition is not commutative.
print start + 1337
10:7:17
print 1337 + start
TypeError: unsupported operand type(s)
for +: 'int' and 'instance‘
• The problem is, instead of asking the Time object to
add an integer, Python is asking an integer to add a
Time object, and it doesn’t know how to do that.
Type-based dispatch
• There is a clever solution for this problem:
• Overload a special method __radd__, which
stands for “right-side add.”
# inside class Time:
def __radd__(self, other):
return self.__add__(other)
And here’s how it’s used:
print 1337 + start
10:7:17
Take-home Assignment
• Write an add method for Point that works with either
a Point object or a tuple:
• If the second operand is a Point, the method
should return a new Point whose x coordinate is
the sum of the x coordinates of the operands, and
likewise for the y coordinates.
• If the second operand is a tuple, the method
should add the first element of the tuple to the x
coordinate and the second element to the y
coordinate, and return a new Point with the result.
Debugging
• good idea to initialize all attributes in the init
method.
• not sure whether an object has a particular
attribute?
• hasattr(obj, attr)
• getattr(obj, attr)
• __dict__
Debugging
p = Point(3, 4)
print p.__dict__
{'y': 4, 'x': 3}

def print_attributes(obj):
for attr in obj.__dict__:
print attr, getattr(obj, attr)
Car Dealership
• Imagine we run a car dealership!
• Selling all types of vehicles from trucks to motorcycles.
• Vehicle price: $5000 x num_of_wheels
• Buying back a car: flat_rate – 10% of kilometers
• flat_rate = 10,000 for trucks
• flat_rate = 8,000 for cars
• flat_rate = 4,000 for motorcycles
Car Dealership

• Design an object oriented sales system for the


dealership!
• What would be the objects?
• Car, Truck, Motorcycle
• Sale, Customer, Inventory, …
Dealership - Car Class
class Car:
wheels = 4
def __init__(self, kilometers, make, model, year):
"""Return a new Car object."""
self.kilometers = kilometers
self.make = make
self.model = model
self.year = year

def sale_price(self):
"""Return the sale price for this car."""
return 5000 * self.wheels

def purchase_price(self):
"""Return the price for which we would pay to purchase the car."""
return 8000 - (0.10 * self.kilometers)
Dealership - Truck Class
class Truck:
wheels = 10
def __init__(self, kilometers, make, model, year):
"""Return a new Truck object."""
self.kilometers = kilometers
self.make = make
self.model = model
self.year = year

def sale_price(self):
"""Return the sale price for this truck"""
return 5000 * self.wheels

def purchase_price(self):
"""Return the price for which we would pay to purchase the truck."""
return 10000 - (0.10 * self.kilometers)
Problems?
• Any problems so far?
• The two classes are almost identical!
• DRY (Don’t Repeat Yourself!)
• Solution:
• Inheritance!: Introduce base class that will capture
the shared parts of Car and Truck classes
• Add Vehicle class as our base class
• Derive Car and Truck classes from Vehicle
Inheritance
Dealership – Vehicle Class
class Vehicle:
base_sale_price = 0
wheels = 0
def __init__(self, kilometers, make, model, year):
"""Return a new Vehicle object."""
self.kilometers = kilometers
self.make = make
self.model = model
Our Base
self.year = year Class for
Inheritance
def sale_price(self):
return 5000 * self.wheels

def purchase_price(self):
return self.base_sale_price - (0.10 * self.kilometers)

def vehicle_type(self):
pass
Inheritance: Car and Truck
class Car(Vehicle):
base_sale_price = 8000
wheels = 4
Syntax for
inheritance
def vehicle_type(self):
return ‘Car’

class Truck(Vehicle):
base_sale_price = 10000
wheels = 10
Method
def vehicle_type(self): overriding
return ‘Truck’
Adding a new class: Motorcycle

class Motorcycle(Vehicle):
base_sale_price = 4000
wheels = 2

def vehicle_type(self):
return ‘Motorcycle’
Problems?
• Did we solve all problems?
• Not quite!
v = Vehicle(0, 'Honda', 'Civic', 2018)

• The above should be disallowed as Vehicle class is an


“abstract” class only created for inheritance.
• It is not meant for real object creation!
Solution: Abstract Base Class
from abc import ABCMeta, abstractmethod
class Vehicle:
base_sale_price = 0
wheels = 0
Turning a class into ABC
(Abstract Base Class):
__metaclass__ = ABCMeta
def __init__(self, kilometers, make, model, year): 1. Set __metaclass__
"""Return a new Vehicle object.""" attribute
self.miles = kilometers
2. Label at least one
self.make = make method as abstract
self.model = model
The above changes will
self.year = year
make sure that Vehicle
cannot be instantiated.
def sale_price(self):
return 5000 * self.wheels

def purchase_price(self):
return self.base_sale_price - (0.10 * self.kilometers)

@abstractmethod
def vehicle_type(self):
pass
Inheritance - Thoughts

• Inheritance is a useful feature.


• Inheritance can facilitate code reuse, since you can
customize the behavior of parent classes without
having to modify them.
• Inheritance can make programs difficult to read.
When a method is invoked, it is sometimes not
clear where to find its definition. The relevant code
may be scattered among several modules.

You might also like