In all the programs we wrote till now, we have designed our program around functions i.e. blocks of statements which manipulate data.
This is called the procedure-oriented way of programming.
There is another way of organizing your program which is to combine data and functionality and wrap it inside something called an
object. This is called the object oriented programming paradigm.
Most of the time you can use procedural programming, but when writing large programs or have a problem that is better suited to this
method, you can use object oriented programming techniques.
File "<ipython-input-1-47bcb24d226f>", line 1 In all the programs we wrote till now, we have designed our program around functions i.e. blocks of statements which manipulate data. ^ SyntaxError: invalid syntax
Classes and objects are the two main aspects of object oriented programming.
The object-oriented programming paradigm relies on the two following fundamental rules:
Anything of the real (or mathematical) world which needs to be manipulated by the computer is modeled by an object.
Each object is an instance of some class.
In Python, every value is actually an object. Whether it be a turtle, a list, or even an integer, they are all objects.
Programs manipulate those objects either by performing computation with them or by asking them to perform methods.
To be more specific, we say that an object has a state and a collection of methods that it can perform.
The state of an object represents those things that the object knows about itself.
For example, as we have seen with turtle objects, each turtle has a state consisting of the turtle’s position, its color, its heading
and so on. Each turtle also has the ability to go forward, backward, or turn right or left. Individual turtles are different in that
even though they are all turtles, they differ in the specific values of the individual state attributes (maybe they are in a different
location or have a different heading).
A class creates a new type where objects are instances of the class.
An analogy is that you can have variables of type int which translates to saying that variables that store integers are variables
which are instances (objects) of the int class.
Objects can store data using ordinary variables that belong to the object.
Variables that belong to an object or class are referred to as fields. Fields are of two types - they can belong to each
instance/object of the class or they can belong to the class itself. They are called instance variables and class variables
respectively.
Objects can also have functionality by using functions that belong to a class. Such functions are called methods of the class.
Collectively, the fields and methods can be referred to as the attributes of that class. A class is created using the class keyword.
The fields and methods of the class are listed in an indented block.
This terminology is important because it helps us to differentiate between functions and variables which are independent and those
which belong to a class or object.
object
a portion of memory which contains the information needed to model the real world thing.
class
defines the data structure used to store the objects which are instances of the class together with their behavior.
F = CombinatorialFreeModule(QQ, Permutations())
el = 3*F([1,3,2])+ F([1,2,3])
el
type(el)
class Person:
pass # An empty block
p = Person()
print(p)
class Person:
def say_hi(self):
print('Hello, how are you?')
p = Person()
p.say_hi()
# The previous 2 lines can also be written as Person().say_hi()
class Person:
def say_hi(self):
print('Hello, how are you?')
p.say_hi()
# The previous 2 lines can also be written as Person().say_hi()
class Person:
def say_hi(self):
print('Hello, how are you?')
p = Person()
o.say_hi()
# The previous 2 lines can also be written as Person().say_hi()
class Person:
def __init__(self, name):
self.name = name
def say_hi(self):
print('Hello, my name is', self.name)
p = Person('Swaroop')
p.say_hi()
# The previous 2 lines can also be written as Person('Swaroop').say_hi()
Class And Object Variables
We have already discussed the functionality part of classes and objects (i.e. methods), now let us learn about the data part.
The data part, i.e. fields, are nothing but ordinary variables that are bound to the namespaces of the classes and objects. This means
that these names are valid within the context of these classes and objects only. That's why they are called name spaces.
Class variables are shared - they can be accessed by all instances of that class. There is only one copy of the class variable and
when any one object makes a change to a class variable, that change will be seen by all the other instances.
Object variables are owned by each individual object/instance of the class. In this case, each object has its own copy of the field
i.e. they are not shared and are not related in any way to the field by the same name in a different instance.
You can change the state of an object by making an assignment to one of its attributes.
class Robot:
"""Represents a robot, with a name."""
# A class variable, counting the number of robots
population = 0
def __init__(self, name):
"""Initializes the data."""
self.name = name
print("(Initializing {})".format(self.name))
# When this person is created, the robot adds to the population
Robot.population += 1
# Instead of Robot.population, we could have also used self.__class__.population because every object refers to its class via the self.__class__ attribute.
# Observe that the __init__ method is used to initialize the Robot instance with a name.
def die(self):
"""I am dying."""
print("{} is being destroyed!".format(self.name))
Robot.population -= 1
if Robot.population == 0:
print("{} was the last one.".format(self.name))
else:
print("There are still {:d} robots working.".format(
Robot.population))
def say_hi(self):
"""Greeting by the robot.
Yeah, they can do that."""
print("Greetings, my masters call me {}.".format(self.name))
@classmethod
def how_many(cls):
"""Prints the current population."""
print("We have {:d} robots.".format(cls.population))
# The how_many is actually a method that belongs to the class and not to the object. This means we can define it as either a classmethod or a staticmethod depending on whether we need to know which class we are part of. Since we refer to a class variable, let's use classmethod.
droid1 = Robot("R2-D2")
droid1.say_hi()
Robot.how_many()
droid2 = Robot("C-3PO")
droid2.say_hi()
Robot.how_many()
print("\nRobots can do some work here.\n")
print("Robots have finished their work. So let's destroy them.")
droid1.die()
droid2.die()
Robot.how_many()
class Point:
"""Represents a point in 2-D space."""
class Rectangle:
"""Represents a rectangle.
attributes: width, height, corner.
"""
#The docstring lists the attributes: width and height are numbers; corner is a Point object that specifies the lower-left corner.
box = Rectangle()
box.width = 100.0
box.height = 200.0
box.corner = Point()
box.corner.x = 0.0
box.corner.y = 0.0
#Functions can return instances. For example, find_center takes a Rectangle as an argument and returns a Point that contains the coordinates of the center of the Rectangle :
def find_center(rect):
p = Point()
p.x = rect.corner.x + rect.width/2
p.y = rect.corner.y + rect.height/2
return p
def print_point(p):
print('(%g, %g)' % (p.x, p.y))
center = find_center(box)
print_point(center)
def move_rectangle(rect, dx, dy):
rect.corner.x += dx
rect.corner.y += dy
p = Point()
p.x = rect.corner.x + rect.width/2
p.y = rect.corner.y + rect.height/2
return '(%g, %g)' % (p.x, p.y)
print(move_rectangle(box, 50, 200))
#Aliasing can make a program difficult to read because changes in one place might have unexpected effects in another place. It is hard to keep track of all the variables that might refer to a given object.
#Copying an object is often an alternative to aliasing. The copy module contains a function called copy that can duplicate any object:
p1 = Point()
p1.x = 3.0
p1.y = 4.0
import copy
p2 = copy.copy(p1)
#p1 and p2 contain the same data, but they are not the same Point.
print_point(p1)
print_point(p2)
p1 is p2
p1 == p2
# The is operator indicates that p1 and p2 are not the same object, which is what we expected. But you might have expected == to yield True because these points contain the same data.
# In that case, you will be disappointed to learn that for instances, the default behavior of the == operator is the same as the is operator; it checks object identity, not object
# equivalence. That’s because for programmer-defined types, Python doesn’t know what should be considered equivalent. At least, not yet.
box2 = copy.copy(box)
box2 is box
box2.corner is box.corner
box2.width is box.width
box2.height is box.height
box3 = copy.deepcopy(box)
box3 is box
box3.corner is box.corner
print(move_rectangle(box3, 50, 200))
# why why why?
print(move_rectangle(box2, 50, 200))
print(move_rectangle(box, 50, 200))
print(move_rectangle(box3, 50, 200))
print(move_rectangle(box3, 50, 200))
print(move_rectangle(box, 50, 200))
print(move_rectangle(box3, 50, 200))
isinstance(box.corner, box3)
isinstance(box3, Rectangle)
hasattr(box3, "corner")
hasattr(box, "corner")
try:
corner = box3.corner
except AttributeError:
x = 0
class Point:
""" Point class for representing and manipulating x,y coordinates. """
def __init__(self):
""" Create a new point at the origin """
self.x = 0
self.y = 0
Class definitions can appear anywhere in a program, but they are usually near the beginning (after the import statements).
The syntax rules for a class definition are the same as for other compound statements. There is a header which begins with the keyword,
class, followed by the name of the class, and ending with a colon.
If the first line after the class header is a string, it becomes the docstring of the class, and will be recognized by various tools.
(This is also the way docstrings work in functions.)
Every class should have a method with the special name __init__. This initializer method, often referred to as the constructor, is
automatically called whenever a new instance of Point is created. It gives the programmer the opportunity to set up the attributes
required within the new instance by giving them their initial state values.
The self parameter (you could choose any other name, but nobody ever does!) is automatically set to reference the newly-created object
that needs to be initialized.
p = Point() # Instantiate an object of type Point
q = Point() # and make a second point
print("Nothing seems to have happened with the points")
During the initialization of the objects, we created two attributes called x and y for each, and gave them both the value 0.
Note: The asignments are not to x and y, but to self.x and self.y.
The attributes x and y are always attached to a particular instance. The instance is always explicitly referenced with dot notation.
print(p)
print(q)
print(p is q)
The variables p and q are assigned references to two new Point objects.
A function like Turtle or Point that creates a new object instance is called a constructor.
Every class automatically uses the name of the class as the name of the constructor function.
The definition of the constructor function is done when you write the __init__ function.
It may be helpful to think of a class as a factory for making objects.
The class itself isn’t an instance of a point, but it contains the machinery to make point instances. Every time you call the
constructor, you’re asking the factory to make you a new object. As the object comes off the production line, its initialization
method is executed to get the object properly set up with its factory default settings.
The combined process of “make me a new object” and “get its settings initialized to the factory default settings” is called
instantiation.
Our constructor so far can only create points at location (0,0). To create a point at position (7, 6) requires that we provide some
additional capability for the user to pass information to the constructor.
Since constructors are simply specially named functions, we can use parameters (as we’ve seen before) to provide the specific
information.
We can make our class constructor more general by putting extra parameters into the __init__ method, as shown in this codelens example.
class Point:
""" Point class for representing and manipulating x,y coordinates. """
def __init__(self, initX, initY):
""" Create a new point at the given coordinates. """
self.x = initX
self.y = initY
p = Point(7, 6)
The key advantage of using a class like Point rather than something like a simple tuple (7, 6) now becomes apparent.
We can add methods to the Point class that are sensible operations for points. Had we chosen to use a simple tuple to represent the
point, we would not have this capability.
Creating a class like Point brings an exceptional amount of “organizational power” to our programs, and to our thinking. We can group
together the sensible operations, and the kinds of data they apply to, and each instance of the class can have its own state.
A method behaves like a function but it is invoked on a specific instance.
For example, with a turtle named tess, tess.right(90) asks the tess object to perform its right method and turn 90 degrees. Methods
are accessed using dot notation.
class Point:
""" Point class for representing and manipulating x,y coordinates. """
def __init__(self, initX, initY):
""" Create a new point at the given coordinates. """
self.x = initX
self.y = initY
def getX(self):
return self.x
def getY(self):
return self.y
p = Point(7, 6)
print(p.getX())
print(p.getY())
Let’s add two simple methods to allow a point to give us information about its state. The getX method, when invoked, will return the
value of the x coordinate. The implementation of this method is straight forward since we already know how to write functions that
return values.
One thing to notice is that even though the getX method does not need any other parameter information to do its work, there is still
one formal parameter, self.
As we stated earlier, all methods defined in a class that operate on objects of that class will have self as their first parameter.
Again, this serves as reference to the object itself which in turn gives access to the state data inside the object.
class Point:
""" Point class for representing and manipulating x,y coordinates. """
def __init__(self, initX, initY):
""" Create a new point at the given coordinates. """
self.x = initX
self.y = initY
def getX(self):
return self.x
def getY(self):
return self.y
def distanceFromOrigin(self):
return ((self.x ** 2) + (self.y ** 2)) ** 0.5
p = Point(7, 6)
print(p.distanceFromOrigin())
# Notice that the caller of distanceFromOrigin does not explicitly supply an argument to match the self parameter. This is true of all
method calls. The definition will always have one additional parameter as compared to the invocation.
import math
class Point:
""" Point class for representing and manipulating x,y coordinates. """
def __init__(self, initX, initY):
""" Create a new point at the given coordinates. """
self.x = initX
self.y = initY
def getX(self):
return self.x
def getY(self):
return self.y
def distanceFromOrigin(self):
return ((self.x ** 2) + (self.y ** 2)) ** 0.5
def distance(point1, point2):
xdiff = point2.getX() - point1.getX()
ydiff = point2.getY() - point1.getY()
dist = math.sqrt(xdiff**2 + ydiff**2)
return dist
p = Point(4, 3)
q = Point(0, 0)
print(distance(p, q))
# distance takes two points and returns the distance between them. Note that distance is not a method of the Point class. You can see
this by looking at the indentation pattern. It is not inside the class definition. The other way we can know that distance is not a
method of Point is that self is not included as a formal parameter. In addition, we do not invoke distance using the dot notation.
When we’re working with classes and objects, it is often necessary to print an object (that is to print the state of an object).
Consider the example below:
class Point:
""" Point class for representing and manipulating x,y coordinates. """
def __init__(self, initX, initY):
""" Create a new point at the given coordinates. """
self.x = initX
self.y = initY
def getX(self):
return self.x
def getY(self):
return self.y
def distanceFromOrigin(self):
return ((self.x ** 2) + (self.y ** 2)) ** 0.5
p = Point(7, 6)
print(p)
The print function shown above produces a string representation of the Point p. The default functionality provided by Python tells
you that p is an object of type Point. However, it does not tell you anything about the specific state of the point.
We can improve on this representation if we include a special method call __str__.
Notice that this method uses the same naming convention as the constructor, that is two underscores before and after the name. It is
common that Python uses this naming technique for special methods.
The __str__ method is responsible for returning a string representation as defined by the class creator. In other words, you as the
programmer, get to choose what a Point should look like when it gets printed. In this case, we have decided that the string
representation will include the values of x and y as well as some identifying text. It is required that the __str__ method create
and return a string.
class Point:
""" Point class for representing and manipulating x,y coordinates. """
def __init__(self, initX, initY):
""" Create a new point at the given coordinates. """
self.x = initX
self.y = initY
def getX(self):
return self.x
def getY(self):
return self.y
def distanceFromOrigin(self):
return ((self.x ** 2) + (self.y ** 2)) ** 0.5
def __str__(self):
return "x=" + str(self.x) + ", y=" + str(self.y)
p = Point(7, 6)
print(p)
When we run the program above you can see that the print function now shows the string that we chose.
Now, you ask, don’t we already have an str type converter that can turn our object into a string? Yes we do!
And doesn’t print automatically use this when printing things? Yes again!
But, as we saw earlier, these automatic mechanisms do not do exactly what we want. Python provides many default implementations for
methods that we as programmers will probably want to change.
When a programmer changes the meaning of a special method we say that we override the method. Note also that the str type converter
function uses whatever __str__ method we provide.
class Point:
def __init__(self, initX, initY):
""" Create a new point at the given coordinates. """
self.x = initX
self.y = initY
def getX(self):
return self.x
def getY(self):
return self.y
def distanceFromOrigin(self):
return ((self.x ** 2) + (self.y ** 2)) ** 0.5
def __str__(self):
return "x=" + str(self.x) + ", y=" + str(self.y)
def halfway(self, target):
mx = (self.x + target.x) / 2
my = (self.y + target.y) / 2
return Point(mx, my)
p = Point(3, 4)
q = Point(5, 12)
mid = p.halfway(q)
print(mid)
print(mid.getX())
print(mid.getY())
import math
class Point:
""" Point class for representing and manipulating x,y coordinates. """
def __init__(self, initX, initY):
""" Create a new point at the given coordinates. """
self.x = initX
self.y = initY
def getX(self):
return self.x
def getY(self):
return self.y
def distanceFromOrigin(self):
return ((self.x ** 2) + (self.y ** 2)) ** 0.5
def distanceFromPoint(self, otherP):
dx = (otherP.getX() - self.x)
dy = (otherP.getY() - self.y)
return math.sqrt(dy**2 + dx**2)
p = Point(3, 3)
q = Point(6, 7)
print(p.distanceFromPoint(q))
# Q: Add a distanceFromPoint method that works similar to distanceFromOrigin except that it takes a Point as a parameter and computes
the distance between that point and self.
class Point:
""" Point class for representing and manipulating x,y coordinates. """
def __init__(self, initX, initY):
""" Create a new point at the given coordinates. """
self.x = initX
self.y = initY
def getX(self):
return self.x
def getY(self):
return self.y
def reflect(self):
x = -self.x
return x, self.y
p = Point(3, 5)
print(p.reflect())
class Point:
""" Point class for representing and manipulating x,y coordinates. """
def __init__(self, initX, initY):
""" Create a new point at the given coordinates. """
self.x = initX
self.y = initY
def getX(self):
return self.x
def getY(self):
return self.y
def distanceFromOrigin(self):
return ((self.x ** 2) + (self.y ** 2)) ** 0.5
def slope_from_origin(self):
if self.x == 0:
return None
else:
return self.y / self.x
p = Point(4, 10)
print(p.slope_from_origin())
# Q: Add a method slope_from_origin which returns the slope of the line joining the origin to the point.
class Point:
""" Point class for representing and manipulating x,y coordinates. """
def __init__(self, initX, initY):
""" Create a new point at the given coordinates. """
self.x = initX
self.y = initY
def getX(self):
return self.x
def getY(self):
return self.y
def coeff(self, otherP):
""" finds coefficients of y = ax + c """
dx = (otherP.getX() - self.x)
dy = (otherP.getY() - self.y)
a = dy / dx
c = self.y - a*self.x
return a,c
p = Point(3, 4)
q = Point(6, 13)
print(p.coeff(q))
class Point:
""" Point class for representing and manipulating x,y coordinates. """
def __init__(self, initX, initY):
""" Create a new point at the given coordinates. """
self.x = initX
self.y = initY
def getX(self):
return self.x
def getY(self):
return self.y
def distanceFromOrigin(self):
return ((self.x ** 2) + (self.y ** 2)) ** 0.5
def move(self, dx, dy):
self.x = self.x + dx
self.y = self.y + dy
def __str__(self):
return str(self.x) + "," + str(self.y)
p = Point(7, 6)
print(p)
p.move(5, 10)
print(p)
# Add a method called move that will take two parameters, call them dx and dy. The method will cause the point to move in the x and y
# direction the number of units given. (Hint: you will change the values of the state of the point)
class Fraction:
def __init__(self, top, bottom):
self.num = top # the numerator is on top
self.den = bottom # the denominator is on the bottom
def __str__(self):
return str(self.num) + "/" + str(self.den)
def getNum(self):
return self.num
def getDen(self):
return self.den
myfraction = Fraction(3, 4)
print(myfraction)
print(myfraction.getNum())
print(myfraction.getDen())
# Note that the __str__ method provides a “typical” looking fraction using a slash between the numerator and denominator. We have also
# added a few simple accessor methods, getNum and getDen, that can return the state values for the fraction.
def gcd(m, n):
while m % n != 0:
oldm = m
oldn = n
m = oldn
n = oldm % oldn
return n
print(gcd(12, 30))
def gcd(m, n):
while m % n != 0:
oldm = m
oldn = n
m = oldn
n = oldm % oldn
return n
class Fraction:
def __init__(self, top, bottom):
self.num = top # the numerator is on top
self.den = bottom # the denominator is on the bottom
def __str__(self):
return str(self.num) + "/" + str(self.den)
def simplify(self):
common = gcd(self.num, self.den)
self.num = self.num // common
self.den = self.den // common
myfraction = Fraction(12, 16)
print(myfraction)
myfraction.simplify()
print(myfraction)
There are two important things to note about this implementation.
First, the gcd function is not a method of the class. It does not belong to Fraction. Instead it is a function that is used by
Fraction to assist in a task that needs to be performed. This type of function is often called a helper function.
Second, the simplify method does not return anything. Its job is to modify the object itself. This type of method is known as a
mutator method because it mutates or changes the internal state of the object.
class Fraction:
def __init__(self, top, bottom):
self.num = top # the numerator is on top
self.den = bottom # the denominator is on the bottom
def __str__(self):
return str(self.num) + "/" + str(self.den)
myfraction = Fraction(3, 4)
yourfraction = Fraction(3, 4)
print(myfraction is yourfraction)
ourfraction = myfraction
print(myfraction is ourfraction)
def sameRational(f1, f2):
return f1.getNum()*f2.getDen() == f2.getNum() * f1.getDen()
class Fraction:
def __init__(self, top, bottom):
self.num = top # the numerator is on top
self.den = bottom # the denominator is on the bottom
def __str__(self):
return str(self.num) + "/" + str(self.den)
def getNum(self):
return self.num
def getDen(self):
return self.den
myfraction = Fraction(3, 4)
yourfraction = Fraction(3, 4)
print(myfraction is yourfraction)
print(sameRational(myfraction, yourfraction))
notInLowestTerms = Fraction(15, 20)
print(sameRational(myfraction, notInLowestTerms))
# Python has a powerful feature that allows a designer of a class to decide what an operation like == or < should mean.
# But sometimes the implementors will attach shallow equality semantics, and sometimes deep equality, as shown in this little experiment:
p = Point(4, 2)
s = Point(4, 2)
print("== on Points returns", p == s) # by default, == does a shallow equality test here
a = [2, 3]
b = [2, 3]
print("== on lists returns", a == b) # by default, == does a deep equality test on lists
# This outputs:
# == on Points returns False
# == on lists returns True
# So we conclude that even though the two lists (or tuples, etc.) are distinct objects with different memory addresses, in one case
# the == operator tests for deep equality, while in the case of points it makes a shallow test.
def gcd(m, n):
while m % n != 0:
oldm = m
oldn = n
m = oldn
n = oldm % oldn
return n
class Fraction:
def __init__(self, top, bottom):
self.num = top # the numerator is on top
self.den = bottom # the denominator is on the bottom
def __str__(self):
return str(self.num) + "/" + str(self.den)
def add(self,otherfraction):
newnum = self.num*otherfraction.den + self.den*otherfraction.num
newden = self.den * otherfraction.den
common = gcd(newnum, newden)
return Fraction(newnum // common, newden // common)
f1 = Fraction(1, 2)
f2 = Fraction(1, 4)
f3 = f1.add(f2)
print(f3)
# One final modification to this method will be quite useful. Instead of invoking the add method, we can use the addition operator “+”. This requires that we implement another special method, this time called __add__. The details of the method are the same.
def __add__(self, otherfraction):
newnum = self.num*otherfraction.den + self.den*otherfraction.num
newden = self.den * otherfraction.den
common = gcd(newnum, newden)
return Fraction(newnum // common, newden // common)
# However, now we can perform addition in the same manner that we are used to with other numeric data.
f1 = Fraction(1, 2)
f2 = Fraction(1, 4)
f3 = f1 + f2 # calls the __add__ method of f1
print(f3)
class Point:
""" Point class for representing and manipulating x,y coordinates. """
def __init__(self, initX, initY):
self.x = initX
self.y = initY
def getX(self):
return self.x
def getY(self):
return self.y
def __str__(self):
return "x=" + str(self.x) + ", y=" + str(self.y)
class Rectangle:
"""Rectangle class using Point, width and height"""
def __init__(self, initP, initW, initH):
self.location = initP
self.width = initW
self.height = initH
loc = Point(4, 5)
r = Rectangle(loc, 6, 5)
print(r)
class Point:
""" Point class for representing and manipulating x,y coordinates. """
def __init__(self, initX, initY):
self.x = initX
self.y = initY
def getX(self):
return self.x
def getY(self):
return self.y
def __str__(self):
return "x=" + str(self.x) + ", y=" + str(self.y)
class Rectangle:
"""Rectangle class using Point, width and height"""
def __init__(self, initP, initW, initH):
self.location = initP
self.width = initW
self.height = initH
def getWidth(self):
return self.width
def getHeigth(self):
return self.height
def __str__(self):
return "w=" + str(self.width) + ", h=" + str(self.height)
loc = Point(4, 5)
r = Rectangle(loc, 6, 5)
print(r)
class Point:
""" Point class for representing and manipulating x,y coordinates. """
def __init__(self, initX, initY):
self.x = initX
self.y = initY
def getX(self):
return self.x
def getY(self):
return self.y
def __str__(self):
return "x=" + str(self.x) + ", y=" + str(self.y)
class Rectangle:
"""Rectangle class using Point, width and height"""
def __init__(self, initP, initW, initH):
self.location = initP
self.width = initW
self.height = initH
def area(self):
return self.width * self.height
r = Rectangle(Point(0, 0), 10, 5)
print(r.area(), 50)
class Point:
""" Point class for representing and manipulating x,y coordinates. """
def __init__(self, initX, initY):
self.x = initX
self.y = initY
def getX(self):
return self.x
def getY(self):
return self.y
def __str__(self):
return "x=" + str(self.x) + ", y=" + str(self.y)
class Rectangle:
"""Rectangle class using Point, width and height"""
def __init__(self, initP, initW, initH):
self.location = initP
self.width = initW
self.height = initH
def perimeter(self):
return 2*self.width + 2*self.height
r = Rectangle(Point(0, 0), 10, 5)
print(r.perimeter(), 30)
class Point:
""" Point class for representing and manipulating x,y coordinates. """
def __init__(self, initX, initY):
self.x = initX
self.y = initY
def getX(self):
return self.x
def getY(self):
return self.y
def __str__(self):
return "x=" + str(self.x) + ", y=" + str(self.y)
class Rectangle:
"""Rectangle class using Point, width and height"""
def __init__(self, initP, initW, initH):
self.location = initP
self.width = initW
self.height = initH
def transpose(self):
temp = self.width
self.width = self.height
self.height = temp
return self.width, self.height
r = Rectangle(Point(100, 50), 10, 5)
print(r.transpose())
class Point:
""" Point class for representing and manipulating x,y coordinates. """
def __init__(self, initX, initY):
self.x = initX
self.y = initY
def getX(self):
return self.x
def getY(self):
return self.y
def __str__(self):
return "x=" + str(self.x) + ", y=" + str(self.y)
class Rectangle:
"""Rectangle class using Point, width and height"""
def __init__(self, initP, initW, initH):
self.location = initP
self.width = initW
self.height = initH
def diagonal(self):
d = (self.width**2 + self.height**2) ** 0.5
return d
r = Rectangle(Point(100, 50), 12, 5)
print(r.diagonal())
Python Multiple Inheritance
A class can be derived from more than one base class in Python, similar to C++. This is called multiple
inheritance. In multiple inheritance, the features of all the base classes are inherited into the derived class.
The syntax for multiple inheritance is similar to single inheritance.