Objects
Objects are the building blocks of the enormous programming paradigm called object oriented programming (OOP for short). Classes are python's primary object-oriented programming tool, so we will look at objects on the basis of python's class
. Classes are conceived to create and manage new objects and support inheritance - a key ingredient in the OOP universe and a mechanism of reusing code.
Historical Note
The idea of objects first appeared in the SIMULA progamming language when its designers Ole-Johan Dahl and Kristen Nygaard decided that the concept of classes, objects, inheritance and dynamic binding were neat ways of designing discrete event simulation programs.
It is good to note that using OOP is entirely optional and most programs can be written without it. You can do a lot just by the use of functions and variables. The primary advantage of using OOP is that it is an extremely useful way of organizing code, especially for very large projects. Because using classes requires some prior planning, it is commonly said that they are of more interest to people working in strategic mode than to people in tactical mode.
Classes
In object-oriented programming, a class is an extensible program-code-template for creating objects, providing initial values for state (member variables) and implementations of behavior (member functions, methods). The user defined objects are created using the class
keyword. The class is a blueprint that defines a nature of a future object. From classes we construct instances. An instance is a specific object created from a particular class.
Following is an example of a simple python class which defines the people in brilliant community:
1 2 3 4 5 6 7 8 9 10 |
|
The above is a class
statement that defines a new class, just like def
statements define a new function. The class's name is brilliant
.
Instance is a specific object created from a particular class. To create instances of a class, you call the class using class name and pass in whatever arguments its __init__
method accepts.
1 |
|
Here we create a new instance of the brilliant
class. Or in other words, we instantiate the brilliant
class
1 |
|
The above defines a method for the brilliant
class. Methods is used for functions that belong to a class.
The name __init__()
is used for the "constructor function" for the class. While a class is a blue print for a new data type, you still need to create values of this data type in order to have something that you can store in variables or pass to functions.
When called, the constructor creates the new object, runs the code in the constructor, and returns the new object. This is what the user =brilliant ('Mursalin', 17, 4)
line is. No matter what the class name is, the constructor is always named__init__
.
Another common term is an attribute. Attributes are characteristics of an object. The method called __init__()
is used to initialize the attributes of an object. Just like Methods are functions defined in a class, attributes are variables defined in a class.
Each method in a class definition begins with a reference to the instance object. It is by convention named 'self'.
In Python, the first parameter for methods self
. The self
parameter is used to create member variables.
The body of the constructor function is:
1 2 3 |
|
This seems a bit repetitive, but what this code does is create member variables for the object being created by the constructor. Member variables will begin with self
. to show that they are member variables belonging to the object, and not just regular local variables in the method.
Data
Data refers to attributes that are simply variables of the class we are defining. They are also referred to as class variables. They can be used like any other variable in that they are set when the class is created and can be updated by either methods within the class or elsewhere in the main part of the program. Java programmers refer to these kind of variables as static
.
They can also be made to be a unique attribute of the class. These types of class data are referred to as instance variables. This means that they are associated with the class ad they are referred to as non-static variables by Java programmers.
Example of using a static class data attribute
1 2 3 4 5 6 7>>> class bar(object): ... foo = 2 >>> print bar.foo 2 >>> bar.foo = bar.foo + 1 >>> print bar.foo 101
Whereas a class is a data structure definition type, an instance is a declaration of a variable of that type. Instances are essentially classes brought to life. A class
declaration basically describes a framework for what should exist. If a single general construction plan for any house is a class, an instance would be an actual specific type of house with its own attributes but still following the general structure. Even though many other OOP languages provide a new keyword in order to create an instance class, python simply requires calling the class with parameters.
Example of creating an instance of a class
1 2 3 4 5 6 7 8 9class House(object): color = None size = None bluehouse = House() bluehouse.color = 'blue' redhouse = House() redhouse.color = 'red' print bluehouse.color #Prints 'blue' print redhouse.color # Prints 'red'
Methods
A method is the equivalent of a function in the Object Oriented world. If you have a procedural programming background, that is, you've already played around with functions in your programs, you know that a method should then produce some output when invoked. A method could also accept parameters and manipulate these and then produce an output based on these parameters, just like functions do. However, why not just refer to them as 'functions'? The use of the word 'method' does not limit itself as being some kind of fancy terminology, as methods are not used "just" to perform straightforward computations, but are also classified according to their purpose in the class design.
Interface Methods
Interface methods are the ones which have a purpose of providing an interface of an object with the external environment (be it other objects' methods, data input from an user, data from another object, anything that's not inside that very same object). One of the principles of OO design is encapsulation: build that object to be a capsule containing all of its data and methods inside itself. However, as you can guess, an object that is isolated from everything is just plain useless, it should be a part of a bigger system. That's when interface methods come in: they provide the minimum necessary interface for that object to get external input and provide output so it can indeed be a part of a bigger system while also being a smaller system on itself.
For example, it is a very good OO practice to define a getter and a setter method for an attribute, most notably to security and code integrity reasons. Getters and setters are methods which provide internal attributes access to external agents in an indirect way (you'll see an example of the usefulness of this approach in the below section, "Object variable types and scope"). Let's see how they are implemented:
Getter & Setter Interface Methods in Java:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40// in case you don't know why the keywork 'public' is being used here, its purpose will come clear in the in the below section, "Object variable types and scope" public class Square { // 'double' is the data type of the attribute, must be declared. private double sideLength; // 'double' is the data type returned by the method, must be declared. // Getter: public double getSideLength() { return this.sideLength; } // 'void' is used to indicate that this method does not return any value. The parameter data type must be declared. // Setter: public void setSideLength(double sideLength) { this.sideLength = sideLength; } public Square(double sideLength) { this.sideLength = sideLength; } public static void Main(String[] args) { // the 'data type' of a variable which holds an object is that object's class. Square square = new Square(10.0); System.out.println(square.getSideLength()); // prints 10.0 to the screen square.setSideLength(3.0); System.out.println(square.getSideLength()); // prints 3.0 to the screen } }
Remember that the Main() method is a Java standard; its content is the code that will be executed when a Java program is run.
Getter & Setter Interface Methods in Python:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20class Square(object): def __init__(self, sideLength): self.sideLength = sideLength # Getter: def getSideLength(self): return self.sideLength # Setter: def setSideLength(self,sideLength): self.sideLength = sideLength square = Square(10.0) print square.getSideLength() # prints 10.0 to the screen square.setSideLength(3.0) print square.getSideLength() # prints 3.0 to the screen
Both of these code snippets do EXACTLY the same thing: define a Square class which will have instances with a sideLength attribute accessible through the getter method 'getSideLength' which will provide that attribute's value and through the setter method 'setSideLength' which will provide a way to alter that attribute's value. Then, an instance of the 'Square' class is created with an initial value for sideLength equaling 10.0 and this instance gets assigned to the 'square' variable. The value of the sideLength variable is then printed in the screen, then the value of sideLength is updated and finally the updated value of the sideLength variable is printed to the screen.
If you are totally new to Object-Oriented programming, probably the 'Square()' Java method and the init() Python method meanings are oblivious to you. That's because they are another type of method, a constructor.
Constructor Methods
A constructor method is used to instantiate a class, which is what effectivelly creates an object (remember that a class is a form to an object and an object is an instance of a class). They are usually either named after the class (such as in Java and C#) or named after a keyword (such as in Python and Ruby). The parameters of a constructor method, when present, are usually the initial values to be assigned to some or all of the attributes of that object. You've seen an example of a constructor method which assigns attributes in the box above. Let's see an example of a constructor which does not take any parameters and instead initializes the object attributes to some default values:
Constructor Methods in Python:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19class Square(object): # Constructor: def __init__(self): self.sideLength = 5.0 def getSideLength(self): return self.sideLength def setSideLength(self,sideLength): self.sideLength = sideLength square = Square() // invokes the constructor method print square.getSideLength() # prints 5.0 to the screen square.setSideLength(3.0) print square.getSideLength() # prints 3.0 to the screen
The program's structure is basically the same, except that the constructor method now takes no parameters and returns the default value when the getter is called for an attribute which did not have its value individually set. As you can see, a default value for an attribute does not mean that this attribute is static (this Java keywork will be explained in bigger depth soon) to the object, only that you've kinda decided that "I guess I'll define this attribute later but I want my instance totally working now". Also note that not only the constructor implementation needs to obey some language-specific conventions, but the constructor invoking as well. In Java, the constructor method is invoked by adding the keywork 'new' before calling the constructor with (or without) its parameters. In Python, the constructor is invoked by passing parameters to the class (this will actually trigger the init method).
Implementation Methods
The implementation method is the one that most resembles a standard procedural function. Like the name suggests, that's a method in which you'll actually implement functionality to the object. This is usually done by manipulating the object's attributes and provide some output (or alter some internal attribute in a specific way without providing any immediate output, the sky's the limit). Let's implement the 'outputArea' functionality in out Square object:
Implementation Methods in Python:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21class Square(object): # Constructor: def __init__(self): self.sideLength = 5.0 def getSideLength(self): return self.sideLength def setSideLength(self,sideLength): self.sideLength = sideLength # Implemented functionality: def outputArea(): return self.getSideLength() * self.getSideLength() square = Square() square.setSideLength(3.0) print square.outputArea() # prints 9.0 to the screen
So, what's happening here? The output of the 'outputArea()' method displays the area of a square with a side length equal to that Square's sideLength attribute. The basic guideline for a method internal to an object is to operate inside its data to provide actual functionality. Any method that has a more general functionality not restricted to a single class (or class hierarchy) is usually defined inside a module (think of a module as a collection of methods designed to provide functionality that can be applied in a lot of different classes). If the Square's sideLength attribute was 4.0 instead of 3.0, the output would be 16.0 instead of 9.0 . The method is also not restricted to just performing some closed operation on its attributes, we could manipulate the output using both the object's attributes and some arbitrary parameter, like so:
New implementation of outputArea method in Python:
1 2 3 4def outputAreaTimesN(n): return (self.getSideLength() * self.getSideLength()) * n print square.outputAreaTimesN(2) # prints 18.0 to the screen
Object variable types and scope
In object oriented programming, methods and variables have various scope. Scope means that the method or variable may or may not be directly accessable to other objects or classes. Classes that do not have instances may be accessable to the system.
Class Scope:Class variables and class methods are associated with a class. An instance of the class (object) is not required to use these variables or metnods. Class method cannot access instance variables or methods, only class variables and methods.
Instance Scope:Instance variables and instance methods are associated with a specific object. They can access class variables and methods.
Private Scope:Private variables and private methods are only accessible to the object they are contained in. So if something goes wrong with that, there is usually only one source file to look at. If you have a million lines of code in your project, but your classes are kept small, this can reduce your bug tracking effort by a large factor.
Public Scope:Public variables and public methods are accessible outside the object they are contained in, which for practical considerations means "potentially anywhere". If something goes wrong with a public field, the culprit can be anywhere, and so in order to track down the bug, you may have to look at quite a lot of code
Protected Scope:Protected variables and protected methods are accessible by the class they are in and inheriting classes (sub classes).
Encapsulation:The process of providing a public interface to interact with the object while hiding other information inside the object. Encapsulation means that the internal representation of an object is generally hidden from view outside of the object's definition.The main way that encapsulation helps reduce rippling effects of change is by keeping as many of the implementation details private to the class. By limiting the interface only to those members needed to use the class, many changes can be made to the implementation without affecting any code that uses the class.
Inheritance
Different kinds of objects often have a certain amount in common with each other. Brilliant staff members and users, for example, all share the same characteristics of (name, age, level). Yet each also defines additional features that make them different: for staff
we may want to add salary, area of expertise, and so on.
Object-oriented programming allows classes to inherit commonly used state and behavior from other classes. In this example, staff members now becomes the super-class of users.
1 2 3 4 5 |
|
What this is effectively saying, "A staff
is the same as a user
, with some additional methods and member variables
Important benefits of inheritance are code reuse and reduction of complexity of a program. The derived classes (descendants) override or extend the functionality of base classes (ancestors).
We should be careful when using inheritance, because it can be abused. We must be certain that any conceivable change or update you make to the user
class would also be something you would want the staff
class and every other subclass of user
to also have.
Inheritance describes how the attributes of base cases are inherited by the derived class. A subclass inherits attributes of any of its base classes weather they be data attributes or methods.
Below is an example. Parent
is a simple class with no attributes. Child
is a class that derives from Parent
and is thus a sub-class.
1 2 3 4 5class Parent(object): #Parent class pass class Child(Parent): #Child class pass
1 2 3 4 5 |
|
Let us create a method within Parent
that will override in its child class:
1 2 3 4 5 6 |
|
Now let us create the child class
1 2 3 4 5 6 |
|
Although Child
inherit's Parent
's fun
method, it is overriden because Child
defines its own fun
method. One reason for overriding methods is to add special and unique functionality to your sub-class.
If you want to call the base class method you overrode in your subclass, you can invoke an unbound base class method, explicitly providing the instance of the subclass :
1 2 |
|
Modules
In order to avoid some tedious work it is often necessary to work with modules. A module is a distinct thing that may have one or two dozen closely-related classes. The trick is that a module is something you'll import, and you need that import to be perfectly sensible to people who will read, maintain and extend your software.
A module is imported using the import
statement.
1 |
|
In Python every file that ends with .py is a module
1 2 3 4 5 6 7 8 |
|
Know we save this file as basic.py
1 2 3 4 5 6 7 |
|
We save this file us more.py.
1 2 3 4 5 6 7 8 9 10 |
|
Which will output
1 2 3 4 5 6 |
|
In the above example the modules basic.py and more.py contained only one class each, but that need not be. Think of the import as the way to organize your code in concepts or chunks. Exactly how many classes are in each import doesn't matter. What matters is the overall organization that you're portraying with your import statements.
Problems
Consider the following class, implemented in Python:
class A(object):
def __init__(self, a=None, b=None):
self.a = a
self.b = b
This class can be used to create lists, as demonstrated in this example:
#the list 1, 2, 3
one_two_three = A(1, A(2, A(3)))
one_two_three.a #returns 1
Jenny wrote the below function to create a list of 1000 elements and return a sublist of the last 163 elements. To get the correct answer, she must call it with inputs n and m: get_sublist_last_163(n, m).
n and m are non-negative integers. What is the value of n + m?
def get_sublist_last_163(N, M):
# create a list of 1000 elements
big_list = A(1000)
i = 999
while (i>0):
big_list = A(i, big_list)
i = i - 1
# get a sublist of the last 163 items
sublist_last_163 = big_list
# call "b" N times
i = 0
while (i < N):
sublist_last_163 = sublist_last_163.b
i = i + 1
# call "a" M times
i = 0
while (i < M):
sublist_last_163 = sublist_last_163.a
i = i + 1
return sublist_last_163