13 Classes
13 Classes
3
Baidya Nath Saha
Module
Modules can also be simply divided into built-in modules and
custom modules.
Built-in modules are built into Python, such as sys, os and
other basic modules. Built-in function dir (module_name)
Let you see what data the module defines (include variable
name, module name, function name, etc.).
dir () function will return all currently defined names when no
arguments are present.
4
Baidya Nath Saha
5
Baidya Nath Saha
Package
6
Difference between Class and Module
7
Difference between Class and Module
8
Difference between Class and Module
9
Module and Packages
10
Advantage of Modular Programming
11
Advantage of Modular Programming
12
Baidya Nath Saha
Class
13
Baidya Nath Saha
Defining a Class
Self here refers to the class itself. If you notice the self.name
method, this is a key feature of classes. You must create an
object of the class and then use .function(), calling it to the
created class object.
Any functions defined within a class are considered to be art of
the local scope and cannot be used outside of the class unless in
relation to a class object such as in the following example
where addition is an object of the class Addition and therefore
can use the add() function of the class.
15
Creating class instances
Initializing Objects
17
Baidya Nath Saha
Class Variables
18
Class Variables
class ExampleClass():
Notice how even though greeting exists in
both the class and on a global scope, the
def greeting(self):
two do not interact with one another nor do
greeting = 'hello'
they change the value of each other.
return(greeting)
example = ExampleClass()
Whenever you call the class object, the
value of greeting will always be the value
greeting = 'yo'
given inside the class because it is counted
as a local variable which has a higher
print(greeting)
precedence over the global variable
print(example.greeting())
greeting. Though once executed greeting
print(greeting)
will not have changed value outside of the
class.
19
Accessing a function within a class
20
Functions within and outside of the class
21
Class using outside input
class ExampleClass():
def hello_name(self, name):
print("Hello " + name + "!")
example = ExampleClass()
your_name = input()
example.hello_name(your_name)
22
Create a class with methods
_init_
The __init__ method is known as the constructor and it is called
automatically when an instance of an object is created. In order to
use the values defined in __init__ you must initialize them with
self.parameter in __init__ and then refer to them as such
throughout the class.
class example():
def __init__(self, x, y): def mult (self, w):
self.x = x return (self.y * w) - self.x
self.y = y e = example(3,7)
print(e.sub())
def sub(self):
return self.y - self.x
24
Assign object
25
Read object variable data using accessors
27
Baidya Nath Saha
# Mutator Methods
def increase_height(self, factor):
self.height = self.height * factor
28
Input Object
29
Output object information
This is the reason that built-in functions like id(), print() etc. are
always available to us from any part of the program.
Each module creates its own global namespace.
These different namespaces are isolated. Hence, the same name
that may exist in different modules do not collide.
Modules can have various functions and classes. A local
namespace is created when a function is called, which has all the
names defined in it. Similar, is the case with class. Following
diagram may help to clarify this concept.
https://www.programiz.com/python-programming/namespace
3
Baidya Nath Saha
Namespace in Python
Namespaces are one honking great idea -- let's do more of those!
https://www.programiz.com/python-programming/namespace
4
Design problem
5
Create a class with methods
6
Difference between Abstraction
and Encapsulation
Hide
details Hide details
in design in
level implementation
level
7
Class
8
Class
9
Public, Private and Protected Variables
10
Public, Private and Protected Variables
11
Example: Public Attributes
12
Example: Protected Attributes
class employee:
def __init__(self, name, sal):
self._name=name # protected attribute
self._salary=sal # protected attribute
e1=employee(“Swati",10000)
Python's convention to make an
instance variable protected is to
print(e1._salary)
add a prefix _ (single underscore)
e1._salary=20000
to it. This effectively prevents it
print(e1._salary)
to be accessed, unless it is from
within a sub-class.
13
Example: Protected Attributes
14
Example: Private Attributes
class employee:
def __init__(self, name, sal):
self.__name=name # private attribute
self.__salary=sal # private attribute
e1=employee(“Bill",10000)
Similarly, a double underscore __
prefixed to a variable makes
print(e1.__salary)
it private. It gives a strong
suggestion not to touch it from
AttributeError: 'employee' object
outside the class. Any attempt to
has no attribute '__salary'
do so will result in an Attribute
Error:
15
Private variables
16
Private variables : Name mangling
E1=employee(“Bill",10000)
print(e1._employ__salary
e1._employee__salary=20000
print(e1._employee__salary)
17
Private variables: Name mangling
18
Private variables: Name mangling
Name mangling is helpful for letting subclasses override methods
without breaking intraclass method calls. For example:
class Mapping: class MappingSubclass(Mapping):
def __init__(self, iterable): def update(self, keys, values):
self.items_list = [] # provides new signature for
self.__update(iterable) update()
def update(self, iterable): # but does not break __init__()
for item in iterable: for item in (keys, values): s
self.items_list.append(item) self.items_list.append(item)
__update = update
# private copy of original update() method
19
Private variables: Name mangling
20
Private variables: Name mangling
21
Odds and Ends
22
Odds and Ends
A piece of Python code that expects a particular abstract
data type can often be passed a class that emulates the
methods of that data type instead. For instance, if you have
a function that formats some data from a file object, you can
define a class with methods read() and readline() that get the
data from a string buffer instead, and pass it as an argument.
23
Iterators
By now you have probably noticed that most container
objects can be looped over using a for statement:
24
Iterators
25
Iterators
>>> s = 'abc‘
>>> it = iter (s)
>>> it Having seen the mechanics behind
<iterator object at 0x00A1DB50> the iterator protocol, it is easy to
>>> next(it) add iterator behavior to your
'a'
>>> next(it)
classes. Define an __iter__()
'b' method which returns an object
>>> next(it) with a __next__() method. If the
'c' class defines __next__(), then
>>> next (it) __iter__() can just return self:
Traceback (most recent call last):
File “<stdin>” , line 1, in <module>
next(it)
StopIteration
26
Iterators
class Reverse: >>> rev = Reverse('spam')
"""Iterator for looping over a sequence >>> iter (rev)
backwards."""
<__main__.Reverse object at
def __init__(self, data):
0x00A1DB50>
self.data = data
self.index = len(data) >>> for char in rev:
def __iter__(self): ... Print (char)
return self
...
def __next__(self):
if self.index == 0: m
raise StopIteration a
self.index = self.index - 1 p
return self.data[self.index]
s
27
Generators
Generators are a simple and powerful tool for creating iterators.
They are written like regular functions but use the yield
statement whenever they want to return data. Each time next()
is called on it, the generator resumes where it left off (it
remembers all the data values and which statement was last
executed). An example shows that generators can be trivially
easy to create: >>> for char in reverse('golf'):
... print (char)
def reverse(data):
...
for index in range(len(data)-1, -1, -1): f
yield data[index] l
o
g
28
Generators
Anything that can be done with generators can also be done with
class-based iterators as described in the previous section. What
makes generators so compact is that the __iter__() and __next__()
methods are created automatically.
Another key feature is that the local variables and execution state
are automatically saved between calls. This made the function
easier to write and much more clear than an approach using
instance variables like self.index and self.data.
In addition to automatic method creation and saving program state,
when generators terminate, they automatically raise StopIteration.
In combination, these features make it easy to create iterators with
no more effort than writing a regular function.
29
Generator Expressions
Some simple generators can be coded succinctly as expressions using
a syntax similar to list comprehensions but with parentheses instead
of square brackets. These expressions are designed for situations
where the generator is used right away by an enclosing function.
Generator expressions are more compact but less versatile than full
generator definitions and tend to be more memory friendly than
equivalent list comprehensions.
Examples:
30
Generator Expressions
31
Department of Mathematical and Physical Sciences
Concordia University of Edmonton
Implementing Inheritance
Inheritance
2
Inheritance
class DerivedClassName(BaseClassName):
<statement-1>
...
<statement-N>
3
Inheritance
6
Inheritance
class parent():
def __init__(self, width, length):
self.width = width
self.length = length
def perimeter(self):
per = (2*self.width) + (2*self.length)
return(per)
def area(self):
ar = (self.width*self.length)
return(ar)
8
Inheritance
class child(parent):
def __init__(self, width, length, height):
parent.__init__(self, width, length)
self.height = height
def volume(self):
vol = (self.area()*self.height)
return(vol)
square = child(3,3,3)
print(square.volume())
print(square.area())
print(square.perimeter())
9
Inheritance
Note:
class child(parent):
def __init__(self, width, length, height):
parent.__init__(self, width, length)
self.height = height
10
Exercise 1
Exercise 1
11
_str_
12
_str_
13
_str_
# Notice how no function call is needed to print out the result, just #
a call to the class object.
14
Exercise 2
Exercise 2
Take the code from the first example and change it so that it uses the
__str__ method. You can also employ super() in your code. When
you call the child class you must override the __str__ function in the
child class. Return the same thing as in the parent class and add to it
the properly formatted and calculated weekly pay from the first
exercises.
15
Overriding
16
Overloading
17
Exercise 3
Exercise 3
18
Department of Mathematical and Physical Sciences
Concordia University of Edmonton
Multiple Inheritance
Multiple Inheritance
2
Multiple Inheritance
For most purposes, in the simplest cases, you can think of the
search for attributes inherited from a parent class as depth-first,
left-to-right, not searching twice in the same class where there is an
overlap in the hierarchy. Thus, if an attribute is not found in
DerivedClassName, it is searched for in Base1, then (recursively)
in the base classes of Base1, and if it was not found there, it was
searched for in Base2, and so on.
In fact, it is slightly more complex than that; the method resolution
order changes dynamically to support cooperative calls to super().
This approach is known in some other multiple-inheritance
languages as call-next-method and is more powerful than the super
call found in single-inheritance languages.
3
Multiple Inheritance
5
Diamond Problem
(Deadly Diamond of Death)
6
Diamond Problem
(Deadly Diamond of Death)
“Multiple Inheritance (object-oriented programming) was widely
supposed to be very difficult to implement efficiently. For example,
in a summary of C++ in his book on objective C Brd.Cox actually
claimed that adding Multiple inheritance to C++ was impossible.
Thus, multiple inheritance seemed more of a challenge. Since I had
considered multiple inheritance as early as 1982 and found a
simple and efficient implementation technique in 1984. I couldn't
resist the challenge. I suspect this to be the only case in which
fashion affected the sequence of events.”
- Bjarne Stroustrup
Stroustrup, Bjarne (1999). Multiple Inheritance for C++. Proceedings of the Spring
1987 European Unix Users Group Conference.
7
Diamond Problem
(Deadly Diamond of Death)
8
Case I
This is a simple case where we have class C derived from both A and
B. When method process() is called with object of class C then
process() method in class A is called.
Python constructs the order in which it will look for a method in the
hierarchy of classes. It uses this order, known as MRO, to determine
which method it actually calls.
It is possible to see MRO of a class using mro() method of the class.
9
Case I
class A:
def process(self):
print('A process()')
class B:
pass
class C(A, B): From MRO of class C, we get to know that
pass Python looks for a method first in class C.
obj = C() Then it goes to A and then to B. So, first it
goes to super class given first in the list
obj.process() then second super class, from left to right
print(C.mro()) order. Then finally Object class, which is a
# print MRO for class C super class for all classes.
10
Case II
11
Case III
Now, lets change the hierarchy. We create B and C from A and then D
from B and C. Method process() is present in both A and C.
class A:
def process(self):
print('A process()')
class B(A):
pass obj = D()
class C(A): obj.process()
def process(self):
print('C process()')
class D(B,C):
pass
13
Case IV
14
Case V
15
Case V
Then according to the rule (good head) A should NOT be ahead of B as A is super
class of B. So new MRO must be like this:
C -> B -> A But A is also direct super class of C. So, if a method is in both A and B
classes then which version should class C call? According to new MRO, the version
in B is called first ahead of A and that is not according to inheritance rules (specific
to generic) resulting in Python to throw error.
Understanding MRO is very important for any Python programmer. I strongly
recommend trying more cases until you completely understand how Python
constructs MRO. Do not confuse yourself by taking old way of constructing MRO
used in earlier versions of Python. It is better to consider only Python 3.
http://www.srikanthtechnologies.com/blog/python/mro.aspx
16
Department of Mathematical and Physical Sciences
Concordia University of Edmonton
Polymorphism
Polymorphism
2
Polymorphism
# Driver code
print (add(2, 3))
print(add(2, 3, 4))
3
Polymorphism with class methods
Below code shows how python can use two different class types, in
the same way. We create a for loop that iterates through a tuple of
objects. Then call the methods without being concerned about which
class type each object is. We assume that these methods actually
exist in each class.
4
Polymorphism with class methods
class India():
def capital(self:
print("New Delhi is the capital of India.")
def language(self):
print("Hindi the primary language of India.")
def type(self):
print("India is a developing country.")
class USA():
def capital(self):
print("Washington, D.C. is the capital of USA.")
5
Polymorphism with class methods
def language(self):
print("English is the primary language of USA.")
def type(self):
print("USA is a developed country.")
obj_ind = India()
obj_usa = USA()
for country in (obj_ind, obj_usa):
country.capital()
country.language()
country.type()
6
Polymorphism with Inheritance
7
Polymorphism with Inheritance
class Bird:
def intro(self):
print("There are many types of birds.")
def flight(self):
print("Most of the birds can fly but some
cannot.")
class sparrow(Bird):
def flight(self):
print("Sparrows can fly.")
class ostrich(Bird):
def flight(self):
print("Ostriches cannot fly.")
8
Polymorphism with Inheritance
obj_bird = Bird()
obj_spr = sparrow()
obj_ost = ostrich()
obj_bird.intro()
obj_bird.flight()
obj_spr.intro()
obj_spr.flight()
obj_ost.intro()
obj_ost.flight()
9
Polymorphism with Function and Objects
10
Polymorphism with Function and Objects
class India():
def capital(self):
print("New Delhi is the capital of India.")
def language(self):
print("Hindi the primary language of India.")
def type(self):
print("India is a developing country.")
class USA():
def capital(self):
print("Washington, D.C. is the capital of
USA.")
Code : Implementing Polymorphism with a Function
11
Polymorphism with Function and Objects
def language(self):
print("English is the primary language of USA.")
def type(self):
print("USA is a developed country.")
def func(obj):
obj.capital()
obj.language()
obj.type()
obj_ind = India()
obj_usa = USA()
func(obj_ind)
func(obj_usa)
12
Polymorphism
13
Polymorphism
If you have overridden a method in the child class, then the version
of the method will be called based upon the type of the object used
to call it.
If a child class object is used to call an overridden method then the
child class version of the method is called.
On the other hand, if parent class object is used to call an
overridden method, then the parent class version of the method is
called.
14
Polymorphism
15
Polymorphism
16
Polymorphism
class A:
def explore(self):
print("explore() method from class A")
class B(A):
def explore(self):
super().explore() # calling the parent class explore() method
print("explore() method from class B")
b_obj = B()
b_obj.explore()
17
Object – The Base Class