Object-Oriented Python - An Introduction: Avi Kak and Charles Bouman
Object-Oriented Python - An Introduction: Avi Kak and Charles Bouman
Object-Oriented Python - An Introduction: Avi Kak and Charles Bouman
Purdue University
Purdue University 1
The Reason for This Material at the Outset
A large majority of people who play with deep learning algorithms operate in the
zombie mode — meaning that they simply run canned programs downloaded
from the internet with the expectation that a combination of the downloaded
software and their own dataset would lead to results that would somehow pave
their way to fame and fortune. This, unfortunately, is no way for a student to
prepare himself or herself for the future.
The goal of our deep learning class is to help you become more genuine in how
you utilize your deep learning skills.
I’ll therefore be starting my part of this class with a focus on object-oriented
(OO) Python since that’s what many of today’s software tools for deep learning
are based on.
Regarding this lecture, in what follows, I’ll start with the main concepts of OO
programming in general and then devote the rest of the material to Python OO.
The material that I present is drawn from Chapter 3 of my book Scripting with
Objects [It’s a clickable link]. The book provides an in-depth understanding of how
object-oriented scripting works in Perl and Python. Here is a link for its Table of
Contents.
Purdue University 2
Outline
Outline
Purdue University 4
Some Examples of PyTorch Syntax
The statement in the third line appears to indicate that we are using xform
as a function which is being returned by the statement in the second line.
Does that mean functions in Python return functions?
To fully understand what’s going on here you have to know what’s meant
by an object being callable.
Python makes a distinction between function objects and callables. While
all function objects are callables, not all callables are function objects.
Purdue University 5
Some Examples of PyTorch Syntax
The keyword class in the first line means we are defining a new class named
EncoderRNN as a subclass of torch.nn.Module and the method init () is
there to initialize an instance object constructed from this class.
But why are we making the call super(EncoderRNN, self). init () and
supplying the name of the subclass again to this method? To understand
this syntax, you have to know how you can ask a method to get part of
the work done by a method defined for one of its superclasses. How that
works is different for a single-inheritance class hierarchy and for a
multiple-inheritance class hierarchy.
Purdue University 6
Some Examples of PyTorch Syntax
train_data_loader = torch.utils.data.DataLoader(
training_data, batch_size=self.batch_size, shuffle=True, num_workers=2)
or calls like
for data in train_data_loader:
inputs,labels = data
...
outputs = model(inputs)
...
Purdue University 9
The Main OO Concepts
Outline
Purdue University 10
The Main OO Concepts
Purdue University 11
The Main OO Concepts
An instance constructed from a class will have specific values for the
attributes.
Purdue University 12
The Main OO Concepts
Purdue University 13
The Main OO Concepts
Encapsulation
A client should only access those data attributes and invoke those
methods that are in the public interface.
Purdue University 14
The Main OO Concepts
Purdue University 15
The Main OO Concepts
Polymorphism (contd.)
of cats, dogs, and a duck. These would be instances made from three
different classes in the same Animal hierarchy. If we were to invoke a
method calculateIQ() on this list of animals in the following fashion
for item in animals:
item.calculateIQ()
All of the public methods and attributes defined for the root class
would constitute the public interface of the class hierarchy and each
class in the hierarchy would be free to provide its own implementation
for the methods declared in the root class.
Purdue University 17
PythonOO: Pre-Defined and Programmer-Supplied Attributes
Outline
Purdue University 18
PythonOO: Pre-Defined and Programmer-Supplied Attributes
First note that in Python the word attribute is used to describe any
property, variable or method, that can be invoked with the dot
operator on either a class or an instance constructed from a class.
Among all the things that can be invoked with the dot operator on
either a class or an instance object, note that a class in Python comes
with certain pre-defined attributes.
Purdue University 19
PythonOO: Pre-Defined and Programmer-Supplied Attributes
Purdue University 20
PythonOO: Pre-Defined and Programmer-Supplied Attributes
Purdue University 21
Function Objects vs. Callables
Outline
Purdue University 22
Function Objects vs. Callables
On the other hand, a callable is any object that can be called like a
function, but that has NOT been defined with a def statement.
Purdue University 23
Function Objects vs. Callables
Purdue University 24
Function Objects vs. Callables
If you execute this code, you will see the output shown in the
commented-out portions of the code lines.
In Lines (C) and (F), note how we are calling the function call operator
’()’ on the instance constructed from the class X.
The results shown are for the seed 0 set at the beginning of the script.
Purdue University 25
Function Objects vs. Callables
import random
random.seed(0)
#---------------------------- class X ---------------------------
class X:
def __init__( self, arr ) :
self.arr = arr
def get_num(self, i):
return self.arr[i]
# def __call__(self):
# return self.arr
#------------------------ end of class definition ----------------
xobj = X( random.sample(range(1,10), 5) )
print(xobj.get_num(2)) # 1
print(xobj()) # Traceback (most recent call last)
# File "X.py", line 15, in <module>
# print(xobj())
# TypeError: ’X’ object is not callable
Purdue University 26
Defining a Class in Python
Outline
Purdue University 27
Defining a Class in Python
I’ll start with a very simple example of a class to make the reader
familiar with the pre-defined init () method whose role is to
initialize the instance returned by a call to the constructor.
Purdue University 28
Defining a Class in Python
#test code:
a_person = Person( "Zaphod", 114 )
print(a_person.name) # Zaphod
print(a_person.age) # 114
Purdue University 29
Defining a Class in Python
Being an object in its own right, every Python class comes equipped
with the following pre-defined attributes:
Purdue University 30
Defining a Class in Python
Since every class instance is also an object in its own right, it also
comes equipped with certain pre-defined attributes. We will be
particularly interested in the following two:
class : string name of the class from which the instance was
constructed
dict : dictionary whose keys are the names of the instance
variables
Purdue University 31
Defining a Class in Python
dir( MyClass )
that returns a list of all the attribute names, for variables and for
methods, for the class (both directly defined for the class and
inherited from a class’s superclasses).
Purdue University 32
Defining a Class in Python
#test code:
a_person = Person("Zaphod",114)
print(a_person.name) # Zaphod
print(a_person.age) # 114
# class attributes:
print(Person.__name__) # Person
print(Person.__doc__) # A very simple class
print(Person.__module__) # main
print(Person.__bases__) # ()
print(Person.__dict__) # {‘__module__’ : ‘__main__’, ‘__doc__’ : ‘A very simp..’,
# ‘__init__’:<function __init..’,
# instance attributes:
print(a_person.__class__) # __main__.Person
print(a_person.__dict__ ) # {‘age’:114, ‘name’:’Zaphod’}
Purdue University 33
Defining a Class in Python
class MyClass :
‘optional documentation string’
class_var1
class_var2 = aabbcc ## assuming ’aabbcc’ is a
## variable set previously
...
...
Purdue University 34
Defining a Class in Python
Regarding the syntax shown on the previous slide, note the class
variables class var1 and class var2. Such variables exist on a
per-class basis, meaning that they are static.
If you do not provide a class with its own init (), the system will
provide the class with a default init (). You override the default
definition by providing your own implementation for init ().
The syntax for a user-defined method for a class is the same as for
stand-alone Python functions, except for the special significance
accorded the first parameter, typically named self. It is meant to be
bound to a reference to the instance on which the method is invoked.
Purdue University 36
Defining a Class in Python
All classes are subclassed, either directly or indirectly from the root
class object.
The list of attributes defined for the object class can be seen by
printing out the list returned by the built-in dir() function:
print( dir( object ) )
This call returns
[‘__class__’,‘__delattr__’,‘__doc__’,‘__getattribute__’,‘__hash__’,’__init__’,’__new__’,__reduce__’,
‘__reduce_ex__’,’__repr__’,’__setattr__’,’__str__’]
We can also examine the attribute list available for the object class by
printing out the contents of its dict attribute by
print( object.__dict__ )
This will print out both the attribute names and their bindings.
Purdue University 37
How Python Creates an Instance: new() vs. init()
Outline
Purdue University 38
How Python Creates an Instance: new() vs. init()
STEP 1:
Purdue University 39
How Python Creates an Instance: new() vs. init()
If a class does not provide its own definition for new (), a search is
conducted for this method in the parent classes of the class.
STEP 2:
Purdue University 40
How Python Creates an Instance: new() vs. init()
Purdue University 41
How Python Creates an Instance: new() vs. init()
In the script on the next slide, also note that the namespace
dictionary xobj. dict created at runtime for the instance xobj is
empty — for obvious reasons.
Purdue University 42
How Python Creates an Instance: new() vs. init()
Purdue University 43
How Python Creates an Instance: new() vs. init()
Purdue University 44
Defining Methods
Outline
Purdue University 45
Defining Methods
A method defined for a class must have special syntax that reserves
the first parameter for the object on which the method is invoked.
This parameter is typically named self for instance methods, but
could be any legal Python identifier.
In the script shown on the next slide, when we invoke the constructor
using the syntax
xobj = X( 10 )
the parameter self in the call to init () is set implicitly to the
instance under construction and the parameter nn to the value 10.
A method may call any other method of a class, but such a call must
always use class-qualified syntax, as shown by the definition of bar()
on the next slide.
Purdue University 46
Defining Methods
In the definition shown below, one would think that a function like baz()
in the script below could be called using the syntax X.baz(), but that does
not work. (We will see later how to define a class method in Python).
#---------- class X ------------
class X:
def __init__(self, nn): # the param ’self’ is bound to the instance
self.n = nn
def getn(self): # instance method
return self.n
def foo(self,arg1,arg2,arg3=1000): # instance method
self.n = arg1 + arg2 + arg3
def bar( self ): # instance method
self.foo( 7, 8, 9 )
def baz(): # this is NOT how you define a class method
pass
#--- End of Class Definition ----
xobj = X(10)
print( xobj.getn() ) # 10
xobj.foo(20,30)
print( xobj.getn() ) # 1050
xobj.bar()
print( xobj.getn() ) # 24
# X.baz() # ERROR
Purdue University 47
Defining Methods
Purdue University 48
Defining Methods
def getn(self):
return self.n
xobj = X(10)
print( xobj.getn() ) # 10
Purdue University 49
Defining Methods
class X:
def foo(self, arg1, arg2):
implemention_of_foo
rest_of_class_X
the compiler will introduce the name foo as a key in the namespace
dictionary for class X. The value entered for this key will be the
function object corresponding to the body of the method definition.
Purdue University 50
Defining Methods
Since all the method names are stored as keys in the namespace
dictionary and since the dictionary keys must be unique, this implies
that there can exist only one function object for a given method
name.
If after seeing the code snippet shown on the previous slide, the
compiler saw another definition for a method named for the same
class, then regardless of the parameter structure of the function, the
new function object will replace the old for the value entry for the
method name. This is unlike what happens in C++ and Java where
function overloading plays an important role.
Purdue University 51
Defining Methods
We just talked about how there can only be one method of a given
name in a class — regardless of the number of arguments taken by
the method definitions.
As a more general case of the same property, a class can have only
one attribute of a given name.
Purdue University 52
Defining Methods
And each time a variable whose referent object either goes out of
scope or is changed, the reference count associated with the object is
decreased by one. When the reference count associated with an
object goes to zero, it becomes a candidate for garbage collection.
Purdue University 53
Defining Methods
A Python class and a Python instance object are so open that they
can be modified after the objects are brought into existence.
Purdue University 54
Defining Methods
Purdue University 55
Defining Methods
def get_owner(self):
return self.owner
def get_idNum(self):
return self.idNum
robot1 = Robot("Zaphod")
print( robot1.get_idNum() ) # 1
robot2 = Robot("Trillian")
print( robot2.get_idNum() ) # 2
robot3 = Robot("Betelgeuse")
print( robot3.get_idNum() ) # 3
Purdue University 56
Defining Methods
@staticmethod
def foo():
print ‘‘foo called’’
Purdue University 57
Creating a Class Hierarchy: Method Definitions
Outline
Purdue University 58
Creating a Class Hierarchy: Method Definitions
However, as you will see in the next couple of slides, using super() in
the manner shown above may not be necessary in single-inheritance
hierarchies.
Purdue University 59
Creating a Class Hierarchy: Method Definitions
The next two slides show a single inheritance hierarchy, with Employee
as the base class and the Manager as the derived class.
We want both the promote() and the myprint() methods of the derived
class to call on the method of the same names in the base class to do
a part of the job.
As shown in lines (A) and (B), we accomplish this by calling the
methods promote() and the myprint() directly on the class name Employee.
Purdue University 60
Creating a Class Hierarchy: Method Definitions
def myprint(self):
print( self.name, "%s" % self.position, end=" ")
def promote(self): ## this promote() calls parent’s promote() for part of the work
if self.position == ’executive’:
print( "not possible" )
return
Employee.promote( self ) ## (A)
def myprint(self): ## this print() calls parent’s print() for part of the work
Employee.myprint(self) ## (B)
print( self.dept )
Purdue University (continued on next slide) 61
Creating a Class Hierarchy: Method Definitions
Purdue University 62
Creating a Class Hierarchy: Method Definitions
A Derived Class Method Calling super() for the Parent Class’s Method
But now the methods of the derived class invoke the built-in super()
for calling on the methods of the parent class.
Purdue University 63
Creating a Class Hierarchy: Method Definitions
promotion_table = {
’shop_floor’ : ’staff’,
’staff’ : ’manager’,
’manager’ : ’executuve’
}
def promote(self):
self.position = Employee.promotion_table[self.position]
def myprint(self):
print( self.name, "%s" % self.position, end=" ")
def promote(self):
if self.position == ’executive’:
print( "not possible" )
return
super(Manager, self).promote() ## (A)
def myprint(self):
super(Manager, self).myprint() ## (B)
print( self.dept )
Purdue University 64
Creating a Class Hierarchy: Superclass–Subclass Constructors
Outline
Purdue University 65
Creating a Class Hierarchy: Superclass–Subclass Constructors
Purdue University 66
Creating a Class Hierarchy: Superclass–Subclass Constructors
Purdue University 67
Creating a Class Hierarchy: Superclass–Subclass Constructors
Such calls to super() typically mention the name of the derived class
in which super() is called. It is a holdover from the old days.
Purdue University 68
Creating a Class Hierarchy: Superclass–Subclass Constructors
Purdue University 69
Creating a Class Hierarchy: Superclass–Subclass Constructors
Shown on the next slide is the same hierarchy that you saw on the
previous slide, but with the following new-style syntax for the call to
super() in line (A):
super().__init__(nam, pos)
Purdue University 70
Creating a Class Hierarchy: Superclass–Subclass Constructors
def promote(self):
if self.position == ’executive’:
print( "not possible" )
return
super(Manager, self).promote()
def myprint(self):
super(Manager, self).myprint()
print( self.dept )
#------------------ Test Code --------------------
emp = Employee("Orpheus", "staff")
emp.myprint() # Orpheus staff
print("\n")
emp.promote()
print( emp.position ) # manager
emp.myprint() # Orpheus manager
print()
man = Manager("Zaphod", "manager", "sales")
man.myprint() # Zaphod manager sales
man.promote()
print("\n")
print( man.position ) # executive
print(isinstance(man, Employee)) # True
print(isinstance(emp, Manager)) # False
print(isinstance(man, object)) # True
print(isinstance(emp, object)) # True
Purdue University 71
Multiple-Inheritance Class Hierarchies
Outline
Purdue University 72
Multiple-Inheritance Class Hierarchies
class B( A ):
def __init__(self):
print("called B’s init")
super().__init__()
class C( A ):
def __init__(self):
print("called C’s init")
super().__init__()
class D(B,C):
def __init__(self):
print("called D’s init")
super().__init__()
print(D.__mro__)
# (<class ’__main__.D’>, <class ’__main__.B’>, <class ’__main__.C’>, <class ’__main__.A’>, <t
Purdue University 74
Multiple-Inheritance Class Hierarchies
Purdue University 76
Multiple-Inheritance Class Hierarchies
Purdue University 78
Multiple-Inheritance Class Hierarchies
Purdue University 79
Multiple-Inheritance Class Hierarchies
Outline
Purdue University 81
Making a Class Instance Iterable
A class instance is iterable if you can loop over the data stored in the
instance. The data may be stored in the different attributes and in
ways not directly accessible by, say, array like indexing.
A class must provide for an iterator in order for its instances to be
iterable and this iterable must be returned by the definition of iter
for the class.
Commonly, the iterator defined for a class will itself be a class that
provides implementation for a method named next in Python 3 and
next in Python 2.
Purdue University 82
Making a Class Instance Iterable
class Xiterator:
def __init__(self, xobj):
self.items = xobj.arr
self.index = -1
def __iter__(self):
return self
def __next__(self):
self.index += 1
if self.index < len(self.items):
return self.items[self.index]
else:
raise StopIteration
next = __next__
#------------------------ end of class definition ---------------------
xobj = X( random.sample(range(1,10), 5) )
print(xobj.get_num(2)) # 1
print(xobj()) # [7, 9, 1, 3, 5] because xobj is callable
for item in xobj:
print(item, end=" ") # 7 9 1 3 5 because xobj is iterable
## Using the built-in iter() to construct a new instance of the iterator over the same instance of X:
it = iter(xobj) # iter() is a built-in function that calls __iter__()
print(it.next()) # 7
print(it.next()) # 9
Purdue University 84