Object Oriented Programming

Object Oriented Programming (OOP) is a programming paradigm that uses objects and their interactions to design applications and computer programs. (Wikipedia)

There are some basic programming concepts in OOP:

  • Abstraction
  • Polymorphism
  • Encapsulation
  • Inheritance

What's an Object?

  • An object is created by instantiating a class
  • A class is created using Python's class statement (usually)
  • In general, any number of objects can be created from a single class ...
  • ... unless the class is given some special magic to make it a "Singleton"

Oh, great... Lots of Jargon... Some OOP Terminology

  • $\bf Class: $ A user-defined prototype for an object that defines a set of attributes that characterize any object of the class. The attributes are data members (class variables and instance variables) and methods, accessed via dot notation.

  • $\bf Class\ variable: $ A variable that is shared by all instances of a class. Class variables are defined within a class but outside any of the class's methods. Class variables are not used as frequently as instance variables are.

  • $\bf Data\ member: $ A class variable or instance variable that holds data associated with a class and its objects.

  • $\bf Function\ overloading: $ The assignment of more than one behavior to a particular function. The operation performed varies by the types of objects or arguments involved.

  • $\bf Instance\ variable: $ A variable that is defined inside a method and belongs only to the current instance of a class.

  • $\bf Inheritance: $ The transfer of the characteristics of a class to other classes that are derived from it.

  • $\bf Instance: $ An individual object of a certain class. An object obj that belongs to a class Circle, for example, is an instance of the class Circle.

  • $\bf Instantiation: $ The creation of an instance of a class.

  • $\bf Method : $ A special kind of function that is defined in a class definition.

  • $\bf Object: $ A unique instance of a data structure that's defined by its class. An object comprises both data members (class variables and instance variables) and methods.

  • $\bf Operator\ overloading: $ The assignment of more than one function to a particular operator.

Lets look at some objects...


In [ ]:
import sys

def function(): pass

print type(1)
print type("")
print type([])
print type({})
print type(())
print type(object)
print type(function)
print type(sys)

Python programs can have different styles

  • Simple, short scripts can just be straight-line code -- Do this one thing! BAM!
  • A somewhat longer task might have a recurring sub-task: a function
  • If more functions appear, they might be grouped into one file (a module)
  • Classes come in when higher levels of structure are needed ...

Ok ... so what's a Class?

  • ### A "Class" is a code structure that contains ...

    1. Data in the form of Fields, a.k.a. "attributes", and
    2. Procedures, a.k.a. "methods"
  • ### One way to think of these is:

    1. An object's attributes represent its state
    2. Its methods represent its behavior

Everything in Python is an object. Objects are the basic building blocks of a Python OOP program.

When should I write my own classes?

  • When you have some functions that are passing one or more parameters around
  • When you have some functions that need to access the same "state" information
  • When you are developing a Graphical User Interface (GUI)
  • When you need to represent and process some highly structured data, e.g.:
    • a document or web page
    • data about a real-world "thing" (either man-made or natural)

The class keyword

The previous objects were mostly built-in objects of the Python programming language. 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. For example, Phydoux might be an instance of a Dog class.


In [ ]:
# first.py

class First:
   pass

fr = First()

print type(fr)
print type(First)

This is our first class. The body of the class is left empty for now. It is a convention to give Classes a name that starts with a capital letter.

Here we see that fr is an instance object and First is a class object.

Inside a class, we can define attributes and methods. An attribute is a characteristic of an object. This can be for example the wavelength of a sensor. A method defines operations that we can perform with our objects. A method might define a turning the sensor on and collecting an image. Technically, attributes are variables and methods are functions defined inside a class.

Attributes

Attributes are characteristics of an object. A special method called init() is used to initialize the attributes of an object.


In [ ]:
class Dog:
   def __init__(self, name):
      self.name = name

phydoux = Dog('Phydoux')
lucky = Dog('Lucky')

print phydoux.name
print lucky.name

In this code example, we have a Dog class. The special method init() is called automatically right after the object has been created.

  • def init(self, name):

Each method in a class definition begins with a reference to the instance object . It is by convention named self. There is nothing special about the self name. We could name it this, for example. The name is the argument. The value is passed during the class instantiation.

  • self.name = name

Here we pass an attribute to an instance object.

  • phydoux = Dog('Phydoux')
  • lucky = Dog('Lucky')

Here we instantiate two objects: dogs Phydoux and Lucky. The number of arguments must correspond to the __init__() method of the class definition. The 'Phydoux' and 'Lucky' strings become the name parameter of the __init__() method.

The attributes can be assigned dynamically, not just during initialization. This shows the next example.


In [ ]:
class Dynamic:
   pass

d = Dynamic()
d.name = "Dynamic"
print d.name

We define and create an empty Dynamic class. The line of code:

  • d.name = "Dynamic"

creates a new name attribute, which is very different that other languages where you have to declaire the variables up front.

Methods

Methods are functions defined inside the body of a class. They are used to perform operations with the attributes of our objects. Methods are essential in encapsulation concept of the OOP paradigm. For example, we might have a connect() method in our AccessDatabase class. We need not to be informed how exactly the method connect connects to the database. We only know that it is used to connect to a database. This is essential in dividing responsibilities in programming, especially in large applications.


In [ ]:
class Circle:
   pi = 3.141592

   def __init__(self, radius=1):
      self.radius = radius 

   def area(self):
      return self.radius * self.radius * Circle.pi

   def setRadius(self, radius):
      self.radius = radius

   def getRadius(self):
      return self.radius

c = Circle()

c.setRadius(5)
print c.getRadius()
print c.area()

In the code example, we have a Circle class. We define three new methods.

  • area()
  • setRadius()
  • getRadius()

The area() method returns the area of a circle. The getRadius() method returns the current radius.

Special Methods

Classes in Python programming language can implement certain operations with special method names. These methods are not called directly, but by a specific language syntax. This is similar to what is known as operator overloading in C++ or Ruby.


In [ ]:
class Book:
   def __init__(self, title, author, pages):
      print "A book is created"
      self.title = title
      self.author = author
      self.pages = pages

   def __str__(self):
      return "Title:%s , author:%s, pages:%s " % \
              (self.title, self.author, self.pages)

   def __len__(self):
      return self.pages

   def __del__(self):
      print "A book is destroyed"

book = Book("Inside Steve's Brain", "Leander Kahney", 304)

print book
print len(book)
del book

In our code example, we have a book class. Here we introduce four special methods. The __init__(), __str__(), __len__() and the __del__() methods. You can imagine how similar special methods can be used to implement __add__(), __sub__(), etc.

Inheritance

The inheritance is a way to form new classes using classes that have already been defined. The newly formed classes are called derived classes, the classes that we derive from are called base classes. 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).


In [ ]:
class Animal:
   def __init__(self):
      print "Animal created"

   def whoAmI(self):
      print "Animal"

   def eat(self):
      print "Eating"


class Dog(Animal):
   def __init__(self):
      Animal.__init__(self)
      print "Dog created"

   def whoAmI(self):
      print "Dog"

   def bark(self):
      print "Woof!"

d = Dog()
d.whoAmI()
d.eat()
d.bark()

In this example, we have two classes: Animal and Dog. The Animal is the base class, the Dog is the derived class. The derived class inherits the functionality of the base class. It is shown by the eat() method. The derived class modifies existing behaviour of the base class, shown by the whoAmI() method. Finally, the derived class extends the functionality of the base class, by defining a new bark() method.

We put the ancestor classes in round brackets after the name of the descendant class. If the derived class provides its own __init__() method, it must explicitly call the base class __init__() method.

Polymorphism

The polymorphism is the process of using an operator or function in different ways for different data input. In practical terms, polymorphism means that if class B inherits from class A, it doesn’t have to inherit everything about class A; it can do some of the things that class A does differently.


In [ ]:
class Animal:
   def __init__(self, name=''):
      self.name = name

   def talk(self):
      pass

class Cat(Animal):
   def talk(self):
      print "Meow!"

class Dog(Animal):
   def talk(self):
      print "Woof!"

a = Animal()
a.talk()

c = Cat("Missy")
c.talk()

d = Dog("Rocky")
d.talk()

Here we have two species: a dog and a cat. Both are animals. The Dog class and the Cat class inherit the Animal class. They have a talk() method, which gives different output for them.

Encapsolation

In computer programming languages, encapsulation is used to refer to one of three related but distinct ideas:

  • A language mechanism for restricting direct access to some of the object's components.
  • A language construct that facilitates the bundling of data with the methods (or other functions) operating on that data.
  • A programming convention for respecting the intended scope of the information

Some programming language researchers and academics use the first meaning alone or in combination with the second as a distinguishing feature of object-oriented programming, while other programming languages which provide lexical closures view encapsulation as a feature of the language orthogonal to object orientation.

The second definition is motivated by the fact that in many OOP languages hiding of components is not automatic or can be overridden; thus, information hiding is defined as a separate notion by those who prefer the second definition.

Python doesn’t have any mechanisms that would effectively restrict you from accessing a variable or calling a member method. All of this is a matter of culture and convention.

Data Encapsolation

Name Notation Behaviour
name Public Can be accessed from inside and outside
_name Protected Like a public member, but they shouldn't be directly accessed from outside.
__name Private Can't be seen and accessed from outside

If an identifier is only preceded by one underscore character, it is a protected member. Protected members can be accessed like public members from outside of class. It can be seen in the following example:


In [ ]:
class Encapsulation(object):
    def __init__(self, a, b, c):
        self.public = a
        self._protected = b
        self.__private = c

The following interactive sessions shows the behaviour of public, protected and private members:


In [ ]:
x = Encapsulation(11,13,17)

In [ ]:
x.public

In [ ]:
x._protected

In [ ]:
x._protected = 23

In [ ]:
x._protected

In [ ]:
x.__private

Note, that if you try accessing __private from outside the class, you’ll get an error. But you can still stumble upon something like this:


In [ ]:
>>> x._Encapsulation__private

As mentioned above this is considered bad practice, and if caught you will be subject to being held up to small children as an example of how NOT to behave.

Breakout Exercise

Add a method for fleas. Does your dog or cat have fleas? Add a polymorphic method to the Animal example above for play -- Dog's fetch, and Cat's pounce.


In [ ]: