PythonOOP
PythonOOP
Avinash Kak
Purdue University
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. [What makes it worse is that a large majority of
internet based classes for teaching DL operate in the same mode. After presenting difficult
concepts rather superficially, they ask you to download code from GitHub, assuming that if you
would just execute that code, you might accept the illusion of having understood the material.]
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.
Purdue University 2
Preamble (contd.)
The material that I present is drawn from Chapter 3 of my book Scripting with
Objects [The book title is 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 the Table of Contents of the book that should give you the sense
that there’s a lot more to Object-Oriented Scripting than what is covered in this
lecture.
Purdue University 3
Outline
Outline
Purdue University 5
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 6
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 7
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 10
The Main OO Concepts
Outline
Purdue University 11
The Main OO Concepts
Purdue University 12
The Main OO Concepts
An instance constructed from a class will have specific values for the
attributes.
Purdue University 13
The Main OO Concepts
Purdue University 14
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 15
The Main OO Concepts
Purdue University 16
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 18
PythonOO: Pre-Defined and Programmer-Supplied Attributes
Outline
Purdue University 19
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 20
PythonOO: Pre-Defined and Programmer-Supplied Attributes
Purdue University 21
PythonOO: Pre-Defined and Programmer-Supplied Attributes
Purdue University 22
Function Objects vs. Callables
Outline
Purdue University 23
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 24
Function Objects vs. Callables
You will see objects that may be called with or without the ‘()’
operator and, when they are called with ‘()’, there may or may not
exist any arguments inside the parentheses.
For a class X with method foo, calling just X.foo returns a result that
is different from what is returned by X.foo(). The former returns the
method object itself that X.foo stands for and the latter will cause
execution of the function object associated with the method call.
Purdue University 25
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 26
Function Objects vs. Callables
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
Obviously, now we must call the xobj instance with a parameter. Say
we make the following call in Line (C) on the previous slide:
print( xobj(10) )
What
Purdue do you
University think the output will look like? 27
Defining a Class in Python
Outline
Purdue University 28
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 29
Defining a Class in Python
self.name = a_name
self.age = an_age
#--------- end of class definition --------
#test code:
a_person = Person( "Zaphod", 114 )
print(a_person.name) # Zaphod
print(a_person.age) # 114
Purdue University 30
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 31
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 32
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 33
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 34
Defining a Class in Python
class MyClass :
class_var1
class_var2 = aabbcc ## assuming ’aabbcc’ is a
## variable set previously
...
...
Purdue University 35
Defining a Class in Python
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 37
Defining a Class in Python
All classes are subclassed, either directly or indirectly from the root
class object. The object class defines a set of methods with default
implementations that are inherited by all classes derived from 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 ) )
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 38
How Python Creates an Instance: new() vs. init()
Outline
Purdue University 39
How Python Creates an Instance: new() vs. init()
NOTE: For a more complete understanding of the roles played by the functions new () and int () in the creation of a
new instance from a class definition, see the discussion in Pages 230-240 and Pages 654-656 of my book “Scripting with
Objects”. As described there, the role of the former is to construct the immutable part of a new instance and that of the latter
to construct the mutable part. As explained in Pages 654-656, you can also use new () to exercise control over the production
of new instances — such as when you want only a single instance to be created for a class. An example of when you need this
sort of control over instance production is when you are writing object-oriented code for database drivers. You never want more
than one instance of a database driver since that can lead to all sorts of problems when a user interacts with the database.
Purdue University 41
How Python Creates an Instance: new() vs. init()
Purdue University 42
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 because init () did not define any instance variables.
def __new__( cls ): # The param ’cls’ set to the name of the class
print("__new__ invoked")
return object.__new__( cls )
print( xobj.__dict__) ) # {}
Purdue University 44
How Python Creates an Instance: new() vs. init()
Purdue University 45
Defining Methods
Outline
Purdue University 46
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 47
Defining Methods
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 48
Defining Methods
In the script shown on the next slide, the important thing to note is
that is that the assignment to foo gives X an attribute that is a
function object. As shown, this object can then serve as an instance
method.
Purdue University 49
Defining Methods
def getn(self):
return self.n
xobj = X(10)
print( xobj.getn() ) # 10
Purdue University 50
Defining Methods
class X:
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 51
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 52
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 53
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 54
Defining Methods
A Python class and a Python instance object are so open that they
can be modified even after the objects are brought into existence.
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 57
Defining Methods
@staticmethod
def foo():
print ‘‘foo called’’
Purdue University 58
Creating a Class Hierarchy: Method Definitions
Outline
Purdue University 59
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 60
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 61
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) 62
Creating a Class Hierarchy: Method Definitions
Purdue University 63
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 64
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 65
Creating a Class Hierarchy: Superclass–Subclass Constructors
Outline
Purdue University 66
Creating a Class Hierarchy: Superclass–Subclass Constructors
Purdue University 67
Creating a Class Hierarchy: Superclass–Subclass Constructors
Purdue University 68
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 69
Creating a Class Hierarchy: Superclass–Subclass Constructors
Purdue University 70
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 71
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 72
Multiple-Inheritance Class Hierarchies
Outline
Purdue University 73
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 75
Multiple-Inheritance Class Hierarchies
Purdue University 77
Multiple-Inheritance Class Hierarchies
Purdue University 79
Multiple-Inheritance Class Hierarchies
Purdue University 80
Multiple-Inheritance Class Hierarchies
Outline
Purdue University 82
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 83
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 85