These postings document my responses to and learnings from the Python for Everybody specialisation on Coursera (links below) which I highly recommend to anyone wanting to learn Python programming in a well-structured and fun way. Earlier posts in the series are (bookmarked here).
Data types, objects and methods:
Python has two data structures which we met before – lists and dictionaries – both of which are known in programming terms as objects. Other bits of data we’ve been using – string, integer, float, Boolean – are also objects. Objects are often described as being code + data. They consist of values (the data contained in the object, sometimes also including attributes, the equivalent of a field in a database or column-header in an Excel spreadsheet), together with methods (the code denoting the functions or procedures that can be applied to the object). Different methods may be applied to different object types, for example append is a method applicable to lists, but not to dictionaries.
Any number of objects may exist within a program. They can interact and co-operate with one another (and we can transfer data from one object to another, etc.) yet at the same time also remain partitioned from other objects and data structures, allowing us greater flexibility in how we design a program.
Key terms:
- Class: an abstraction or template that denotes a particular type (e.g. list, string). The class can been viewed together with its associated methods and attributes, since these are functions/attributes of all members of that particular class. [Natural language equivalent = NOUN]
- Method or Message: a defined behaviour, procedure or function of the class; what an instance of a class can do (or have done to it). [Natural language equivalent = VERB]
- Attribute or Field: a piece of data in the class (this might be the data value e.g. value in a string, or its metadata e.g. length of the string). [natural language equivalent ~= ADJECTIVE]
- Object or Instance: a specific occurrence of a class occurring during a program, called by the variable we set up for that instance/object
- State: The value assigned to an instance of a class
Creating a new class:
This seems pretty complex but just walking through step by step, without being overwhelmed by any terminology, it’s an easy thing to set up a new class and attach a simple function or method to that class (e.g. funx()).
# define a new class
class clsx :
____x = 0
____# define method/function on class
____def funx(self) :
________self.x = self.x + 1
________print 'Value of x:', self.x
# create an instance of that class
varname = clsx()
# call a method on that instance of the class
varname.funx()
varname.funx()
First we create a new class using the reserved word class followed by our chosen class name. Under this – in a 1st-level indented block – we set any required parameters (e.g. set starting value x = 0) and define a new function as method to the newly-created class (e.g. define function named funx()). Under this – in a 2nd-level indented block – we put the relevant code that’s to be executed for this particular function or method of the class. Here we use the global alias ‘self’ to denote any and all future instances of the class upon which this function may later be called.
We can now create an object ‘varname’ as an instance of the newly-created class by assigning to it the newly-created class type (e.g. clsx()). We can think of this class instance as an object consisting of both data+code: within varname is ‘stored’ some data (x = 0) and also the code denoting the method to be applied to it (i.e. the block of code defining the function funx()).
Now we can call the method on the object or instance of the class using the varname.funx(). Inside Python, what gets executed when we call the class method is: {class}.{method}({object}) or, in this example, clsx.funx(varname).
So what happens when we want to find out what kind of methods we can call on our newly-created class? We can use type() to confirm the type of an object (returns: <type ‘instance’>) and dir() to list the methods associated with it (returns: [‘__doc__’, ‘__module__’, ‘funx’, ‘x’]. Python will list both our defined method(s) and also it’s own internal methods (denoted by __xxx__).
print 'Type:', type(varname)
print 'Methods:', dir(varname)
Setting up (initialising) and destroying instance variables:
An advanced method which can be built into our class is to initialise/construct or (less-commonly) destruct a variable (i.e. an instance of the class) that we want in our program. The relevant functions are __init__() and __del__(), and can be executed within a class definition as follows:
class clsx :
x = 0
# CONSTRUCTOR
def __init__(self) :
____# any code here needs to be indented
# define method/function on class
def funx(self) :
self.x = self.x + 1
print 'Value of x:', self.x
# DESTRUCTOR
def __del__(self) :
____# any code here needs to be indented
Creating multiple instances of a class:
Construct is very useful as it allows us to set up multiple objects/instances of the class (each with an identifying parameter, such as type, order, or whatever). We can then call the same method of the different instances of the class. Here we use __init__(self, {parameter}) which now has two parameters: one parameter ‘self’ which is the global alias as before, the other being the parameter you wish to use to identify the multiple instances (in this example ‘nam’):
class clsx :
____# creating two instance variables, x and name
____x = 0
____name = ''
____# CONSTRUCT, with additional parameter nam
____def __init__(self, nam) :
________self.name = nam
________print self.name, 'HAS BEEN CONSTRUCTED'
____def funx(self) :
________self.x = self.x + 1
________print self.name, 'Count:', self.x
# create first instance & call method
fst = clsx('First')
fst.funx()
fst.funx()
# create second instance & call method
# on both second and first instances
scnd = clsx('Second')
scnd.funx()
fst.funx()
fst.funx()
When we create any given instance of the class, we can include the required parameter value for ‘nam’. Each value of ‘nam’ in turn will then be assigned to self.name as part of assigning each class instance. So class instance ‘fst’ has value ‘first’ for parameter ‘nam’ which gets put into variable ‘name’ in this first instance of the class. Likewise, class instance ‘scnd’ has value ‘second’ for parameter ‘nam’ which gets put into variable ‘name’ in this second instance.
Class inheritance or extension:
Another useful aspect of defining our own class is that we can effectively clone an existing class, taking all its methods and capabilities with it to the new class we wish to set up – but also still giving us the ability to add more methods/capabilities to the new class as well. This is often referred to as class extension. We can think of the cloned class as being a child of the master or parent class; the child class will often be referred to as a sub-class of the parent.
To clone an existing class into a new sub-class, we use the expression class {sub-class}({class}), for example class clsx1(clsx). Here class clsx1 includes all the variables, attributes and methods we created in class clsx, but we can also add some new variable(s) and method(s) as well. When we assign a variable to clsx we can only apply those methods we defined for class clsx (i.e. funx()). But for class clsx1, we can assign a variable and then call either/both methods from either/both classes (i.e. funx() and/or funx1()). Here’s what that would look like:
# define parent class
class clsx :
____x = 0
____name = ''
____def __init__(self, nam) :
________self.name = nam
________print self.name, 'HAS BEEN CONSTRUCTED'
____def funx(self) :
________self.x = self.x + 1
________print self.name, 'X is now:', self.x
# define child class
class clsx1(clsx) :
____count = 0
____def funx1(self) :
________self.count = self.count + 10
________print self.name, '10+ Count is now:', self.count
# create first instance (of parent class) & call method
fst = clsx('First')
fst.funx()
fst.funx()
# create second instance (of child class) & call methods
# from both classes on second instance
scnd = clsx1('Second')
scnd.funx()
scnd.funx1()
scnd.funx1()
Wen we call a method on the instance of the parent class ‘clsx’, funx() will execute, giving results: “x is now: 1”; “x is now: 2”. But when we call a method on the instance of the child class ‘clsx1’, we now have the option to call either method funx() or funx1(). Executing as in the example will give: “x is now: 1”; “count is now: 10”; “count is now: 20”.
These are just really simple examples – and based on the examples used by Dr. Chuck in his course (links below). But they do illustrate the power of the object-oriented approach we can employ in Python. My mind returns (briefly) to the Aged Debtors Summary report which intuitively I can see could be far more easily written using the ability to define new classes with their own variables and methods, coupled with the ability to clone a parent class and develop it into a new sub-class, with each layer adding a new level of complexity and analysis over the layer/class that went before it.
Read more on Object Oriented Programming at:
- https://en.wikipedia.org/wiki/Object-oriented_programming
- https://en.wikibooks.org/wiki/A-level_Computing/AQA/Paper_1/Fundamentals_of_programming/Object-oriented_programming
Read more like this:
This post follows on from earlier Coding 101 posts and records my responses and learnings from the highly-recommended Python programming book and Coursera specialisation by Charles Severance (see References below).
References:
Book: Programming for Informatics – Exploring Information by Charles Severance
Course: Using Databases with Python by Univ. of Michigan. Part of the Python for Everybody specialisation.