The document provides an introduction to object-oriented programming concepts in Python using PyTorch examples. It discusses key object-oriented programming concepts like classes, instances, attributes, encapsulation, inheritance and polymorphism. It also summarizes some example PyTorch code to demonstrate concepts like classes, methods, inheritance and polymorphism in an object-oriented deep learning context.
2. 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
3. Outline
1 Some Examples of PyTorch Syntax 4
2 The Main OO Concepts 10
3 PythonOO: Pre-Defined and Programmer-Supplied Attributes 18
4 Function Objects vs. Callables 22
5 Defining a Class in Python 27
6 How Python Creates an Instance: new() vs. init() 38
7 Defining Methods 45
8 Creating a Class Hierarchy: Method Definitions 58
9 Creating a Class Hierarchy: Superclass–Subclass Constructors 65
10 Multiple-Inheritance Class Hierarchies 72
11 Making a Class Instance Iterable 81
Purdue University 3
4. Some Examples of PyTorch Syntax
Outline
1 Some Examples of PyTorch Syntax 4
2 The Main OO Concepts 10
3 PythonOO: Pre-Defined and Programmer-Supplied Attributes 18
4 Function Objects vs. Callables 22
5 Defining a Class in Python 27
6 How Python Creates an Instance: new() vs. init() 38
7 Defining Methods 45
8 Creating a Class Hierarchy: Method Definitions 58
9 Creating a Class Hierarchy: Superclass–Subclass Constructors 65
10 Multiple-Inheritance Class Hierarchies 72
11 Making a Class Instance Iterable 81
Purdue University 4
5. Some Examples of PyTorch Syntax
Some Examples of PyTorch Syntax
If you are not already well-schooled in the syntax of object-oriented
Python, you might find the following examples somewhat befuddling:
import torchvision.transforms as tvt
xform = tvt.Compose( [tvt.Grayscale(num_output_channels = 1), tvt.Resize((64,64)) ] )
out_image = xform( input_image_pil )
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
6. Some Examples of PyTorch Syntax
Some Examples of PyTorch Syntax (contd.)
Now consider the following example:
class EncoderRNN( torch.nn.Module ):
def __init__(self, input_size, hidden_size):
super(EncoderRNN, self).__init__()
=== the rest of the definition ===
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
7. Some Examples of PyTorch Syntax
Some Examples of PyTorch Syntax (contd.)
For another example, the class TwoLayerNet shown below (from a
PyTorch tutorial) is a 2-layer network whose layers are defined in lines (A)
and (B). And how the network is connected is declared in forward() in
lines (C) and (D). We push data through the network by calling model(x)
in line (E). But we never call forward(). How does one understand that?
class TwoLayerNet(torch.nn.Module):
def __init__(self, D_in, H, D_out):
torch.nn.Module.__init__( self )
self.linear1 = torch.nn.Linear(D_in, H) ## (A)
self.linear2 = torch.nn.Linear(H, D_out) ## (B)
def forward(self, x):
h_relu = self.linear1(x).clamp(min=0) # using clamp() for nn.ReLU ## (C)
y_pred = self.linear2(h_relu) ## (D)
return y_pred
N, D_in, H, D_out = 64, 1000, 100, 10
x = torch.randn(N, D_in) # N is batch size
y = torch.randn(N, D_out)
model = TwoLayerNet(D_in, H, D_out)
criterion = torch.nn.MSELoss(reduction=’sum’)
optimizer = torch.optim.SGD(model.parameters(), lr=1e-4)
for t in range(10000):
y_pred = model(x) ## (E)
loss = criterion(y_pred, y)
optimizer.zero_grad()
loss.backward()
optimizer.step()
Purdue University 7
8. Some Examples of PyTorch Syntax
Some Examples of PyTorch Syntax (contd.)
For another example that may confuse a beginning Python programmer,
consider the following syntax for constructing a data loader in a PyTorch
script:
training_data = torchvision.datasets.CIFAR10(
root=self.dataroot, train=True, download=True, transform=transform_train)
train_data_loader = torch.utils.data.DataLoader(
training_data, batch_size=self.batch_size, shuffle=True, num_workers=2)
Subsequently, you may see the following sorts of calls:
dataiter = iter(train_data_loader)
images, labels = dataiter.next()
or calls like
for data in train_data_loader:
inputs,labels = data
...
outputs = model(inputs)
...
(continued on next slide)
Purdue University 8
9. Some Examples of PyTorch Syntax
Some Examples of PyTorch Syntax (contd.)
(continued from previous slide)
For a novice Python programmer, a construct like
for x in something:
...
to make sense, ”something” is likely to be one of the typical storage
containers, like a list, tuple, set, etc. But train data loader does not look
like any of those storage containers. So what’s going on here?
Purdue University 9
10. The Main OO Concepts
Outline
1 Some Examples of PyTorch Syntax 4
2 The Main OO Concepts 10
3 PythonOO: Pre-Defined and Programmer-Supplied Attributes 18
4 Function Objects vs. Callables 22
5 Defining a Class in Python 27
6 How Python Creates an Instance: new() vs. init() 38
7 Defining Methods 45
8 Creating a Class Hierarchy: Method Definitions 58
9 Creating a Class Hierarchy: Superclass–Subclass Constructors 65
10 Multiple-Inheritance Class Hierarchies 72
11 Making a Class Instance Iterable 81
Purdue University 10
11. The Main OO Concepts
The Main OO Concepts
The following fundamental notions of object-oriented programming in
general apply to object-oriented Python also:
Class, Instances, and Attributes
Encapsulation
Inheritance
Polymorphism
Purdue University 11
12. The Main OO Concepts
What are Classes and Instances?
At a high level of conceptualization, a class can be thought of as a
category. We may think of “Cat” as a class.
A specific cat would then be an instance of this class.
For the purpose of writing code, a class is a data structure with
attributes.
An instance constructed from a class will have specific values for the
attributes.
To endow instances with behaviors, a class can be provided with
methods.
Purdue University 12
13. The Main OO Concepts
Attributes: Methods, Instance Variables, and Class Variables
A method is a function you invoke on an instance of the class or the
class itself.
A method that is invoked on an instance is sometimes called an
instance method.
You can also invoke a method directly on a class, in which case it is
called a class method or a static method.
Attributes that take data values on a per-instance basis are frequently
referred to as instance variables.
Attributes that take on values on a per-class basis are called class
attributes or static attributes or class variables.
Purdue University 13
14. The Main OO Concepts
Encapsulation
Hiding or controlling access to the implementation-related attributes
and the methods of a class is called encapsulation.
With appropriate data encapsulation, a class will present a
well-defined public interface for its clients, the users of the class.
A client should only access those data attributes and invoke those
methods that are in the public interface.
Purdue University 14
15. The Main OO Concepts
Inheritance and Polymorphism
Inheritance in object-oriented code allows a subclass to inherit some
or all of the attributes and methods of its superclass(es).
Polymorphism basically means that a given category of objects can
exhibit multiple identities at the same time, in the sense that a Cat
instance is not only of type Cat, but also of type FourLegged and
Animal, all at the same time.
Purdue University 15
16. The Main OO Concepts
Polymorphism (contd.)
As an example of polymorphism, suppose we declare a list
animals = [’kitty’, ’fido’, ’tabby’, ’quacker’, ’spot’]
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()
polymorphism would cause the correct implementation code for
calculateIQ() to be invoked automatically for each of the animals. As
you would expect, how you test the IQ of each animal type would be
specific to that type. [Just imagine how frustrating it would be for the duck named
“quacker” in the list to be asked to do dog tricks — that would amount to animal cruelty
and could get you into trouble.]
Purdue University 16
17. The Main OO Concepts
Regarding the Previous Example on Polymorphism
In many object-oriented languages, a method such as calculateIQ()
would need to be declared for the root class Animal for the control
loop shown on the previous slide to work properly.
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.
Polymorphism in a nutshell allows us to manipulate instances
belonging to the different classes of a hierarchy through a common
interface defined for the root class.
Purdue University 17
18. PythonOO: Pre-Defined and Programmer-Supplied Attributes
Outline
1 Some Examples of PyTorch Syntax 4
2 The Main OO Concepts 10
3 PythonOO: Pre-Defined and Programmer-Supplied Attributes 18
4 Function Objects vs. Callables 22
5 Defining a Class in Python 27
6 How Python Creates an Instance: new() vs. init() 38
7 Defining Methods 45
8 Creating a Class Hierarchy: Method Definitions 58
9 Creating a Class Hierarchy: Superclass–Subclass Constructors 65
10 Multiple-Inheritance Class Hierarchies 72
11 Making a Class Instance Iterable 81
Purdue University 18
19. PythonOO: Pre-Defined and Programmer-Supplied Attributes
Pre-Defined vs. Programmer-Supplied Attributes
for a Class in PythonOO
While the previous section talked about the basic concepts of OO
programming in general, I’ll now focus on how OO works in Python.
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.
The pre-defined attributes of a class are not to be confused with the
programmer-supplied attributes such as the class and instance
variables and the programmer-supplied methods.
Purdue University 19
20. PythonOO: Pre-Defined and Programmer-Supplied Attributes
Pre-Defined vs. Programmer-Supplied Attributes
for an Instance Object
By the same token, an instance constructed from a class is an object
with certain pre-defined attributes that again are not to be confused
with the programmer-supplied instance and class variables associated
with the instance and the programmer-supplied methods that can be
invoked on the instance.
As you will see in the later examples, the pre-defined attributes, both
variables and methods, employ a special naming convention: the
names begin and end with two underscores.
Purdue University 20
21. PythonOO: Pre-Defined and Programmer-Supplied Attributes
Formal Definition of a Method for PythonOO
To define it formally, a method is a function that can be invoked on
an object using the object-oriented call syntax that for Python is of
the form obj.method(), where obj may either be an instance of a class
or the class itself.
Therefore, the pre-defined functions that can be invoked on either the
class itself or on a class instance using the object-oriented syntax are
also methods.
PythonOO makes a distinction between two kinds of methods:
function objects and callables. While all function objects are callables,
not all callables are function objects.
Purdue University 21
22. Function Objects vs. Callables
Outline
1 Some Examples of PyTorch Syntax 4
2 The Main OO Concepts 10
3 PythonOO: Pre-Defined and Programmer-Supplied Attributes 18
4 Function Objects vs. Callables 22
5 Defining a Class in Python 27
6 How Python Creates an Instance: new() vs. init() 38
7 Defining Methods 45
8 Creating a Class Hierarchy: Method Definitions 58
9 Creating a Class Hierarchy: Superclass–Subclass Constructors 65
10 Multiple-Inheritance Class Hierarchies 72
11 Making a Class Instance Iterable 81
Purdue University 22
23. Function Objects vs. Callables
Function Objects vs. Callables
A function object can only be created with a def statement.
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.
For example, a class name can be called directly to yield an instance
of a class. Therefore, a class name is a callable.
An instance object can also be called directly; what that yields
depends on whether or not the underlying class provides a definition
for the system-supplied call () method.
As you might have guessed already, the call method is a
system-supplied pre-defined attribute for every class and you must
override it in your own code in order to make the instances callable.
Purdue University 23
24. Function Objects vs. Callables
In Python, ’()’ is an Operator — the Function Call
Operator
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
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 24
25. Function Objects vs. Callables
An Example of a Class with Callable Instances
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) ) ## (A)
print(xobj.get_num(2)) # 1 ## (B)
print(xobj()) # [7, 9, 1, 3, 5] ## (C)
xobj = X( random.sample(range(1,10), 5) ) ## (D)
print(xobj.get_num(2)) # 9 ## (E)
print(xobj()) # [8, 7, 9, 3, 4] ## (F)
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
26. Function Objects vs. Callables
The Same Example But With No Definition for call
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
27. Defining a Class in Python
Outline
1 Some Examples of PyTorch Syntax 4
2 The Main OO Concepts 10
3 PythonOO: Pre-Defined and Programmer-Supplied Attributes 18
4 Function Objects vs. Callables 22
5 Defining a Class in Python 27
6 How Python Creates an Instance: new() vs. init() 38
7 Defining Methods 45
8 Creating a Class Hierarchy: Method Definitions 58
9 Creating a Class Hierarchy: Superclass–Subclass Constructors 65
10 Multiple-Inheritance Class Hierarchies 72
11 Making a Class Instance Iterable 81
Purdue University 27
28. Defining a Class in Python
Defining a Class in Python
I’ll present the full definition of a Python class in stages.
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.
First, here is the simplest possible definition of a class in Python:
class SimpleClass:
pass
An instance of this class may be constructed by invoking its pre-
defined default constructor:
x = SimpleClass()
Purdue University 28
29. Defining a Class in Python
Defining a Class in Python (contd.)
Here is a class with a user-supplied init (). This method is
automatically invoked to initialize the state of the instance returned
by a call to Person():
#------------- class Person --------------
class Person:
def __init__(self, a_name, an_age ):
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 29
30. Defining a Class in Python
Pre-Defined Attributes for a Class
Being an object in its own right, every Python class comes equipped
with the following pre-defined attributes:
name : string name of the class
doc : documentation string for the class
bases : tuple of parent classes of the class
dict : dictionary whose keys are the names of the class
variables and the methods of the class and whose values
are the corresponding bindings
module : module in which the class is defined
Purdue University 30
31. Defining a Class in Python
Pre-Defined Attributes for an Instance
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
It is important to realize that the namespace as represented by the
dictionary dict for a class object is not the same as the
namespace as represented by the dictionary dict for an instance
object constructed from the class.
Purdue University 31
32. Defining a Class in Python
dict vs. dir()
As an alternative to invoking dict on a class name, one can also
use the built-in global dir(), as in
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).
If we had called “print( dir(Person) )” for the Person class
defined on Slide 29, the system would have returned:
[’__class__’, ’__delattr__’, ’__dict__’, ’__dir__’, ’__doc__’, ’__eq__’, ’__format__’, ’__ge__’,
’__getattribute__’, ’__gt__’, ’__hash__’, ’__init__’, ’__init_subclass__’, ’__le__’, ’__lt__’,
’__module__’, ’__ne__’, ’__new__’, ’__reduce__’, ’__reduce_ex__’, ’__repr__’, ’__setattr__’,
’__sizeof__’, ’__str__’, ’__subclasshook__’, ’__weakref__’]
Purdue University 32
33. Defining a Class in Python
Illustrating the Values for System-Supplied Attributes
#------ class Person --------
class Person:
"A very simple class"
def __init__(self,nam,yy ):
self.name = nam
self.age = yy
#-- end of class definition --
#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
34. Defining a Class in Python
Class Definition: More General Syntax
class MyClass :
‘optional documentation string’
class_var1
class_var2 = aabbcc ## assuming ’aabbcc’ is a
## variable set previously
def __init__( self, var3 = default3 ):
‘optional documentation string’
attribute3 = var3
rest_of_construction_init_suite
def some_method( self, some_parameters ):
‘optional documentation string’
method_code
...
...
Purdue University 34
35. Defining a Class in Python
Class Definition: More General Syntax (contd.)
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.
A class variable can be given a value in a class definition, as shown for
class var2.
In general, the header of init () may look like:
def __init__(self, var1, var2, var3 = default3):
body_of_init
This constructor initializer could be for a class with three instance
variables, with the last default initialized as shown. The first
parameter, typically named self, is set implicitly to the instance under
construction.
Purdue University 35
36. Defining a Class in Python
Class Definition: More General Syntax (cond.)
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
37. Defining a Class in Python
The Root Class object
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 ) )
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
38. How Python Creates an Instance: new() vs. init()
Outline
1 Some Examples of PyTorch Syntax 4
2 The Main OO Concepts 10
3 PythonOO: Pre-Defined and Programmer-Supplied Attributes 18
4 Function Objects vs. Callables 22
5 Defining a Class in Python 27
6 How Python Creates an Instance: new() vs. init() 38
7 Defining Methods 45
8 Creating a Class Hierarchy: Method Definitions 58
9 Creating a Class Hierarchy: Superclass–Subclass Constructors 65
10 Multiple-Inheritance Class Hierarchies 72
11 Making a Class Instance Iterable 81
Purdue University 38
39. How Python Creates an Instance: new() vs. init()
How Python Creates an Instance from a Class
Python uses the following two-step procedure for constructing an instance
from a class:
STEP 1:
The call to the constructor creates what may be referred to as a
generic instance from the class definition.
The generic instance’s memory allocation is customized with the code
in the method new () of the class. This method may either be
defined directly for the class or the class may inherit it from one of its
parent classes.
Purdue University 39
40. How Python Creates an Instance: new() vs. init()
Creating an Instance from a Class (contd.)
The method new () is implicitly considered by Python to be a
static method. Its first parameter is meant to be set equal to the
name of the class whose instance is desired and it must return the
instance created.
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:
Then the instance method init () of the class is invoked to initialize
the instance returned by new ().
Purdue University 40
41. How Python Creates an Instance: new() vs. init()
Example Showing new () and init () Working Together
for Instance Creation
The script shown on Slide 43 defines a class X and provides it with a
static method new () and an instance method init ().
We do not need any special declaration for new () to be recognized
as static because this method is special-cased by Python.
Note the contents of the namespace dictionary dict created for
class X as printed out by X. dict . This dictionary shows the names
created specifically for class X. On the other hand, dir(X) also shows
the names inherited by X.
Purdue University 41
42. How Python Creates an Instance: new() vs. init()
Instance Construction Example (contd.)
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.
As stated earlier, when dir() is called on a class, it returns a list of all
the attributes that can be invoked on a class and on the instances
made from that class. The returned list also includes the attributes
inherited from the class’s parents.
When called on a instance, as in dir( xobj ), the returned list is the
same as above plus any instance variables defined for the class.
Purdue University 42
43. How Python Creates an Instance: new() vs. init()
Instance Construction Example (contd.)
#------------------ class X --------------------
class X (object): # X derived from root class object
def __new__( cls ): # the param ’cls’ set to the name of the class
print("__new__ invoked")
return object.__new__( cls )
def __init__( self ):
print("__init__ invoked")
#------------------- Test Code -----------------
xobj = X() # __new__ invoked
# __init__ invoked
print( X.__dict__ ) #{’__module__’: ’__main__’, ’__new__’: <static method ..>,
# .......}
print( dir(X) ) #[’__class__’,’__delattr__’, ’__getattribute__’, ’__hash__’,
# ’__init__’, ’__module__’, ’__new__’, ...........]
print( dir( xobj ) ) #[’__class__’, ’__delattr__’, ’__getattribute__’, ’__hash__’,
# ’__init__’, ’__module__’, ’__new__’, ..........]
Purdue University 43
44. How Python Creates an Instance: new() vs. init()
Instance Construction Example (contd.)
The only difference between the class definition on the previous slide and
this slide is in the first line of the code shown: Now we do NOT make
explicit that the class X is derived from the root class object. This is just
to point out that, implicitly, every class is a subclass of the root class
object.
#------------------ class X --------------------
class X: # X is still derived from the root class object
def __new__( cls ): # The param ’cls’ set to the name of the class
print("__new__ invoked")
return object.__new__( cls )
def __init__( self ):
print("__init__ invoked")
#------------------- Test Code -----------------
xobj = X() # __new__ invoked
# __init__ invoked
print( X.__dict__ ) # {’__module__’: ’__main__’, ’__new__’: <static method ..>,
# ....... }
print( dir(X) ) # [’__class__’,’__delattr__’, ’__getattribute__’, ’__hash__’,
# ’__init__’, ’__module__’, ’__new__’, ........]
print( dir( xobj ) ) # [’__class__’, ’__delattr__’, ’__getattribute__’, ’__hash__’,
# ’__init__’, ’__module__’, ’__new__’,.......]
Purdue University 44
45. Defining Methods
Outline
1 Some Examples of PyTorch Syntax 4
2 The Main OO Concepts 10
3 PythonOO: Pre-Defined and Programmer-Supplied Attributes 18
4 Function Objects vs. Callables 22
5 Defining a Class in Python 27
6 How Python Creates an Instance: new() vs. init() 38
7 Defining Methods 45
8 Creating a Class Hierarchy: Method Definitions 58
9 Creating a Class Hierarchy: Superclass–Subclass Constructors 65
10 Multiple-Inheritance Class Hierarchies 72
11 Making a Class Instance Iterable 81
Purdue University 45
46. Defining Methods
The Syntax for Defining a Method
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
47. Defining Methods
Defining a Method (contd.)
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
48. Defining Methods
A Method Can be Defined Outside a Class
It is not necessary for the body of a method to be enclosed by a class.
A function object created outside a class can be assigned to a name
inside the class. The name will acquire the function object as its
binding. Subsequently, that name can be used in a method call as if
the method had been defined inside the class.
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 48
49. Defining Methods
Method Defined Outside a Class (contd.)
## A function object defined outside class X:
def bar(self,arg1,arg2, arg3=1000):
self.n = arg1 + arg2 + arg3
#-------------- class X ----------------
class X:
foo = bar ## the function obj bound to bar
## is assigned to foo
def __init__(self, nn):
self.n = nn
def getn(self):
return self.n
#------- End of Class Definition --------
xobj = X(10)
print( xobj.getn() ) # 10
xobj.foo( 20, 30 ) ## foo acts like other instance methods
print( xobj.getn() ) # 1050
Purdue University 49
50. Defining Methods
Only One Method for a Given Name Rule
When the Python compiler digests a method definition, it creates a
function binding for the name of the method.
For example, for the following code fragment
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
51. Defining Methods
Only One Method Per Name Rule (contd.)
So if you examine the attribute X. dict after the class is compiled,
you will see the following sort of entry in the namespace dictionary for
X:
‘foo’ : <function foo at 0x805a5e4>
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
52. Defining Methods
Method Names can be Usurped by Data Attribute
Names
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.
What that means is that if a class definition contains a class variable
of a given name after a method attribute of the same name has been
defined, the binding stored for the name in the namespace dictionary
will correspond to the definition that came later.
Purdue University 52
53. Defining Methods
Destruction of Instance Objects
Python comes with an automatic garbage collector. Each object
created is kept track of through reference counting. Each time an
object is assigned to a variable, its reference count goes up by one,
signifying the fact that there is one more variable holding a reference
to the object.
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.
Python provides us with del () for cleaning up beyond what is
done by automatic garbage collection.
Purdue University 53
54. Defining Methods
Encapsulation Issues for Classes
Encapsulation is one of the cornerstones of OO. How does it work in
Python?
As opposed to OO in C++ and Java, all of the attributes defined for
a class are available to all in Python.
So the language depends on programmer cooperation if software
requirements, such as those imposed by code maintenance and code
extension considerations, dictate that the class and instance variables
be accessed only through get and set 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
55. Defining Methods
Defining Class Variables
A class definition usually includes two different kinds of variables:
those that exist on a per-instance basis and those that exist on a
per-class basis. The latter are commonly referred to as being static.
As mentioned earlier, such attributes are also referred to as class
variables.
A variable becomes static (also described as becoming a class
variable) if it is declared outside of any method in a class definition.
Shown on the next slide is a class with a class variable
next serial num
Purdue University 55
56. Defining Methods
Defining Class Variables (contd.)
#-------------- class Robot ----------------
class Robot:
next_serial_num = 1 ## a class variable
def __init__(self, an_owner):
self.owner = an_owner ## an instance variable
self.idNum = self.get_next_idNum() ## another instance variable
def get_next_idNum( self ):
new_idNum = Robot.next_serial_num ## class variable invoked on class name itself
Robot.next_serial_num += 1
return new_idNum
def get_owner(self):
return self.owner
def get_idNum(self):
return self.idNum
#--------- End of Class Definition ---------
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
57. Defining Methods
Class Methods (also known as Static Methods)
In the old days, a static method used to be created by supplying a
function object to staticmethod() as its argument. For example, to
make a method called foo() static, we’d do the following
def foo():
print(‘‘foo called’’)
foo = staticmethod( foo )
The function object returned by staticmethod() is static. In the
above example, when foo is subsequently called directly on the class
using the function call operator ‘()’, it is the callable object bound to
foo in the last statement above that gets executed.
The modern approach to achieving the same effect is through the use
of the staticmethod decorator, as in
@staticmethod
def foo():
print ‘‘foo called’’
Purdue University 57
58. Creating a Class Hierarchy: Method Definitions
Outline
1 Some Examples of PyTorch Syntax 4
2 The Main OO Concepts 10
3 PythonOO: Pre-Defined and Programmer-Supplied Attributes 18
4 Function Objects vs. Callables 22
5 Defining a Class in Python 27
6 How Python Creates an Instance: new() vs. init() 38
7 Defining Methods 45
8 Creating a Class Hierarchy: Method Definitions 58
9 Creating a Class Hierarchy: Superclass–Subclass Constructors 65
10 Multiple-Inheritance Class Hierarchies 72
11 Making a Class Instance Iterable 81
Purdue University 58
59. Creating a Class Hierarchy: Method Definitions
Extending a Class — Using super() in Method
Definitions
Method extension for the case of single-inheritance is illustrated in
the Employee-Manager class hierarchy shown on Slide 61. Note how
the derived-class promote() calls the base-class promote(), and how
the derived-class myprint() calls the base-class myprint().
As you will see later, extending methods in multiple inheritance
hierarchies always requires calling super(). To illustrate, suppose we
wish for a method foo() in a derived class Z to call on foo() in Z’s
superclasses to do part of the work:
class Z( A, B, C, D):
def foo( self ):
....do something....
super( Z, self).foo()
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
60. Creating a Class Hierarchy: Method Definitions
Method Definitions in a Derived Class Calling a Superclass Definition
Directly
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
61. Creating a Class Hierarchy: Method Definitions
Calling a Superclass Method Definition Directly
#-------------- base class Employee -----------------
class Employee:
def __init__(self, nam, pos):
self.name = nam
self.position = pos
promotion_table = { ## a class variable
’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=" ")
#------------- derived class Manager --------------
class Manager( Employee ):
def __init__(self, nam, pos, dept):
Employee.__init__(self,nam,pos)
self.dept = dept
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 )
(continued on next slide)
Purdue University 61
63. Creating a Class Hierarchy: Method Definitions
A Derived Class Method Calling super() for the Parent Class’s Method
Shown on the next slide is the same class single-inheritance class
hierarchy you saw on the previous slides and the methods of the
derived class still want a part of the job to be done by the methods of
the parent class.
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
64. Creating a Class Hierarchy: Method Definitions
A Derived Class Method Calling super() (contd.)
#-------------- base class Employee -----------------
class Employee:
def __init__(self, nam, pos):
self.name = nam
self.position = pos
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=" ")
#------------- derived class Manager --------------
class Manager( Employee ):
def __init__(self, nam, pos, dept):
Employee.__init__(self,nam,pos)
self.dept = dept
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
65. Creating a Class Hierarchy: Superclass–Subclass Constructors
Outline
1 Some Examples of PyTorch Syntax 4
2 The Main OO Concepts 10
3 PythonOO: Pre-Defined and Programmer-Supplied Attributes 18
4 Function Objects vs. Callables 22
5 Defining a Class in Python 27
6 How Python Creates an Instance: new() vs. init() 38
7 Defining Methods 45
8 Creating a Class Hierarchy: Method Definitions 58
9 Creating a Class Hierarchy: Superclass–Subclass Constructors 65
10 Multiple-Inheritance Class Hierarchies 72
11 Making a Class Instance Iterable 81
Purdue University 65
66. Creating a Class Hierarchy: Superclass–Subclass Constructors
How Does a Derived-Class Constructor Call the Base-Class
Constructor
So far we have talked about how a derived-class method can invoke a
base-class method by either calling it directly or through super().
Now I’ll focus on the relationship between the constructor in a derived
class vis-a-vis the constructor in the base class.
As you have seen already in the Employee-Manager class hierarchy: The
first thing that the constructor of a derived class must do is to call on
the constructor of the base class for the initializations that are in the
latter’s init ().
In the code shown on the next slide, this call is made in line (A). As
you can see, the derived class is calling init () directly on the name
of the base class.
Purdue University 66
68. Creating a Class Hierarchy: Superclass–Subclass Constructors
Derived-Class Constructor Calling Base-Class Constructor Through
super()
Regarding the relationship between a derived-class constructor and
the base-class constructor, in the examples you have seen so far, the
derived-class constructor called the base-class constructor directly.
As shown on the next slide, another way for a derived class
constructor to call on the constructor of the base class is using the
built-in super() as shown on the next slide. Note in Line (A) the
following syntax for the call to super():
super(Manager, self).__init__(nam,pos)
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
70. Creating a Class Hierarchy: Superclass–Subclass Constructors
New Style Syntax for Calling super() in Derived-Class Constructor
However, Python3 allows a derived class’s constructor to call super()
without using the derived class’s name as its argument.
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
72. Multiple-Inheritance Class Hierarchies
Outline
1 Some Examples of PyTorch Syntax 4
2 The Main OO Concepts 10
3 PythonOO: Pre-Defined and Programmer-Supplied Attributes 18
4 Function Objects vs. Callables 22
5 Defining a Class in Python 27
6 How Python Creates an Instance: new() vs. init() 38
7 Defining Methods 45
8 Creating a Class Hierarchy: Method Definitions 58
9 Creating a Class Hierarchy: Superclass–Subclass Constructors 65
10 Multiple-Inheritance Class Hierarchies 72
11 Making a Class Instance Iterable 81
Purdue University 72
73. Multiple-Inheritance Class Hierarchies
An Example of Diamond Inheritance
Python allows a class to be derived from multiple base classes. In
general, the header of such a derived class would look like
class derived_class( base1, base2, base3, .... ):
....body of the derived class....
The example shown on the next slide contains a diamond hierarchy
for illustrating multiple inheritance. The class D inherits from two
parents, B and C. Both those classes inherit from A, thus forming an
example of diamond inheritance.
Such hierarchies lead to following questions: Suppose we invoke a
method on a derived-class instance and the method is not defined
directly in the derived class, in what order will the base classes be
searched for an implementation of the method? The order in which
the class and its bases are searched for the implementation code is
commonly referred to as the Method Resolution Order (MRO) in
Python.
Purdue University 73
74. Multiple-Inheritance Class Hierarchies
An Example of Diamond Inheritance (contd.)
#-------------- Classes of A, B, C, D of the hierarchy ---------------
class A (object):
def __init__(self):
print("called A’s init")
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__()
#--------------------------- Test Code -------------------------------
dobj = D()
# called D’s init
# called B’s init
# called C’s init
# called A’s init
#
print(D.__mro__)
# (<class ’__main__.D’>, <class ’__main__.B’>, <class ’__main__.C’>, <class ’__main__.A’>, <t
Purdue University 74
75. Multiple-Inheritance Class Hierarchies
On the Importance of Using super() in init ()
In order to illustrate the importance of using super(), I will present
GOOD-practice and BAD-practice scenarios when defining a
multiple-inheritance hierarchy.
The example shown on the previous slide represented a
GOOD-practice scenario in which we called the parent class’s init ()
in the definition of super () for all the derived classes.
To see why what the programmer of the ABC-hierarchy did on the
previous slide amounted to a good practice: Let’s say that a client of
the original ABC hierarchy decides to extend that hierarchy because
they want to endow the original classes B and C with additional
behavior that is defined in the class Mixin defined on the next slide.
The ”mixed-in” versions of the original classes B and C are called P
and Q, respectively.
Purdue University 75
76. Multiple-Inheritance Class Hierarchies
Importance of Using super() in init () (contd.)
#----------------- Classes of A, B, C of the original hierarchy ----------------
class A (object):
def __init__(self):
print("called A’s init")
class B( A ):
def __init__(self):
print("called B’s init")
super(B,self).__init__()
class C( A ):
def __init__(self):
print("called C’s init")
super(C,self).__init__()
#----- A client wants to mixin additional behaviors with the classes B and C ---
class Mixin(A):
def __init__(self):
print("called Mixin’s init")
super(Mixin, self).__init__()
class P(B, Mixin):
def __init__(self):
print("called P’s init")
super(P, self).__init__()
class Q(C, Mixin):
def __init__(self):
print("called Q’s init")
super(Q, self).__init__()
#--------------------------- Test Code -------------------------------
print("nP instance being constructed:n")
Pobj = P()
# called P’s init
# called B’s init
# called Mixin’s init
# called A’s init
print("nQ instance being constructed:n")
Qobj = Q()
# called Q’s init
# called C’s init
# called Mixin’s init
# called A’s init
print(P.__mro__)
# (<class ’__main__.P’>, <class ’__main__.B’>, <class ’__main__.Mixin’>, <class ’__main__.A’>, <class ’object’>)
print(Q.__mro__)
# (<class ’__main__.Q’>, <class ’__main__.C’>, <class ’__main__.Mixin’>, <class ’__main__.A’>, <class ’object’>)
Purdue University 76
77. Multiple-Inheritance Class Hierarchies
New-Style Version of the Good-Practice Example
of Using super in init ()
The example shown on the next slide is basically the same as what
was shown in the previous slide: A GOOD-practice scenario in which
the original programmer of the ABC hierarchy has used the new-style
super() to call the parent class’s init () in the init () for all the
derived classes.
The only difference from the previous example is that the calls like
super(B,self).__init__()
in the init () of the derived classes have been replaced by the
more compact
super().__init__()
As a result, when a client of the ABC-hierarchy decides to extend the
original hierarchy in order to create mixin versions of those classes as
shown in the part of the code that begins with the class definition for
the Mixin class, there is no problem.
Purdue University 77
78. Multiple-Inheritance Class Hierarchies
New-Style Version of the Good Practice Example (contd.)
#----------------- Classes A, B, C of the orignal hierarchy ----------------
class A (object):
def __init__(self):
print("called A’s init")
class B( A ):
def __init__(self):
print("called B’s init")
super().__init__() ## new-style syntax for super()
class C( A ):
def __init__(self):
print("called C’s init")
super().__init__() ## new-style syntax for super()
#------- The Mixin class and the mixin versions of the B and C classes -----
class Mixin(A):
def __init__(self):
print("called Mixin’s init")
super().__init__() ## new-style syntax for super()
class P(B, Mixin):
def __init__(self):
print("called P’s init")
super().__init__() ## new-style syntax for super()
class Q(C, Mixin):
def __init__(self):
print("called Q’s init")
super().__init__() ## new-style syntax for super()
#--------------------------- Test Code -------------------------------
print("nP instance being constructed:n")
Pobj = P()
# called P’s init
# called B’s init
# called Mixin’s init
# called A’s init
print("nQ instance being constructed:n")
Qobj = Q()
# called Q’s init
# called C’s init
# called Mixin’s init
# called A’s init
print(P.__mro__)
# (<class ’__main__.P’>, <class ’__main__.B’>, <class ’__main__.Mixin’>, <class ’__main__.A’>, <class ’object’>)
print(Q.__mro__)
# (<class ’__main__.Q’>, <class ’__main__.C’>, <class ’__main__.Mixin’>, <class ’__main__.A’>, <class ’object’>)
Purdue University 78
79. Multiple-Inheritance Class Hierarchies
Consequences of NOT Using super()
What’s shown on the next slide represents a BAD-practice scenario in
which the original programmer has explicitly called the parent class’s
init () in the definition of init () for the derived class B.
Subsequently, a client of the ABCD hierarchy decides to extend the
ABC hierarchy because he/she wants to endow the original classes B
and C with additional behavior that is defined in the class Mixin as
defined below. The ”mixed-in” versions of the original classes B and
C are called P and Q, respectively.
As shown in the ”test” section, while Q’s constructor calls Mixin’s
constructor, as it should, P’s constructor fails to do so.
Purdue University 79
80. Multiple-Inheritance Class Hierarchies
Consequences of NOT Using super()(contd.)
#----------------- Classes of A, B, C of the hierarchy ----------------
class A (object):
def __init__(self):
print("called A’s init")
class B( A ):
def __init__(self):
print("called B’s init")
A.__init__(self) ## base class explicit
class C( A ):
def __init__(self):
print("called C’s init")
super(C,self).__init__()
class D(B,C):
def __init__(self):
print("called D’s init")
super(D,self).__init__()
#---------------------- User classes Mixin, P, Q -----------------------
class Mixin(A):
def __init__(self):
print("called Mixin’s init")
super(Mixin, self).__init__()
class P(B, Mixin):
def __init__(self):
print("called P’s init")
super(P, self).__init__()
class Q(C, Mixin):
def __init__(self):
print("called Q’s init")
super(Q, self).__init__()
#--------------------------- Test Code -------------------------------
print("nP instance being constructed:n")
Pobj = P() ## this constructor call fails to call the Mixin constructor
# called P’s init
# called B’s init
# called A’s init
print("nQ instance being constructed:n")
Qobj = Q()
# called Q’s init
# called C’s init
# called Mixin’s init
# called A’s init
print(P.__mro__)
# (<class ’__main__.P’>, <class ’__main__.B’>, <class ’__main__.Mixin’>, <class ’__main__.A’>, <class ’object’>)
print(Q.__mro__)
# (<class ’__main__.Q’>, <class ’__main__.C’>, <class ’__main__.Mixin’>, <class ’__main__.A’>, <class ’object’>)
Purdue University 80
81. Making a Class Instance Iterable
Outline
1 Some Examples of PyTorch Syntax 4
2 The Main OO Concepts 10
3 PythonOO: Pre-Defined and Programmer-Supplied Attributes 18
4 Function Objects vs. Callables 22
5 Defining a Class in Python 27
6 How Python Creates an Instance: new() vs. init() 38
7 Defining Methods 45
8 Creating a Class Hierarchy: Method Definitions 58
9 Creating a Class Hierarchy: Superclass–Subclass Constructors 65
10 Multiple-Inheritance Class Hierarchies 72
11 Making a Class Instance Iterable 81
Purdue University 81
82. Making a Class Instance Iterable
Iterable vs. Iterator
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
83. Making a Class Instance Iterable
An Example of an Iterable Class
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
def __iter__(self):
return Xiterator(self)
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 ---------------------
(continued on next slide)
Purdue University 83
84. Making a Class Instance Iterable
An Example of an Iterable Class (contd.)
(continued from previous slide)
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
## Trying the iterator for a new instance of X:
xobj2 = X( random.sample(range(1,10), 5) )
print(xobj2()) # [8, 7, 9, 3, 4] because xobj2 is callable
it2 = iter(xobj2) # iter() returns a new instance of the iterator
print(it2.next()) # 8
Purdue University 84