Skip to content

OBJECT ORIENTED PROGRAMMING

Irvine Sunday edited this page Jan 6, 2023 · 10 revisions

Programs designed around functions are called procedure oriented programms
while those designed around classes are called object oriented.
It's a programming paradigm that uses objects and classes in programming.
The main concept of OOP is to bind data and the functions that work with that data together as a single unit so that no other part of the code can access this data.

Basic concepts of OOP in Python:

  • Class: A class is a template for creating objects. It contains methods and attributes
    that define the behavior of the objects created from the class.
  • Object: An object is an instance of a class. It's a real world entity that has attributes
    and behaviors. Objects can store data using ordinary variables that belong to the object called 'fields or attributes
  • Attribute: A variable defined inside a class. It is used to store informaion about the object.
  • Method: A method is a function that is defined inside a class. It is used to perform some action or calculation on the object.
  • Inheritance: A way to create a new class that is a modified version of an existing class. This new class is called the derived class and the existing class is the base class
  • Polymorphism: It is the ability of a class to take on multiple forms. It's achieved in Python through inheritance and method overloading.
  • Encapsulation: The idea of building data and methods that operate on that data within a single unit(object). This is done to protect the data from outside access and modification.
  • Data Abstraction : The process of exposing only the essential features of an object but huding its implementation details. It's a way of simplifying complex systems.

These terminology help to differenciate between Independent functions and variables and those that belong to a class or object.

Class creation

To create a class in Python, you use the class keyword followed by the name of the class and a colon. The class definition should be indented, and the methods and attributes of the class should be defined inside the class definition.(Body of the class)

class Dog:
  def __init__(self, name, breed):
    self.name = name
    self.breed = breed
    
  def bark(self):
    print("Woof!")

The class defines a Dog class with two attributes: name and breed and a method called bark.
To create an object from a class, you use the class name followed by parentheses and any necessary arguments. For example:

dog1 = Dog("Fido", "Labrador")
dog2 = Dog("Buddy", "Poodle")

This creates two Dog objects, dog1 and dog2 with attributes name and breed set to "Fido" and "Labrador" for dog1, and "Buddy" and "Poodle" for dog2.

Types of attributes

In OOP, an attribute is avariable defined inside a class. It is used to store information about the object. There are two types of attributes.

1. Instance Attributes

variables that are unique to each instance(object) of a class. alsocalled object variables.
They are defined inside the constructor method (__init__) and are used to store information that is specific to each object. For example, if you have a Person class, each person object might have a unique name and age, which would be stored as instance attributes.

class Person:
  def __init__(self, name, age):
    self.name = name  # instance attribute
    self.age = age  # instance attribute
    
p1 = Person("Alice", 30)
p2 = Person("Bob", 35)

In this example, name and age are instance attributes of the Person class. The name attribute of p1 is "Alice" and the age attribute is 30, while the name attribute of p2 is "Bob" and the age attribute is 35.

2. Class Attributes

also called class variables.
They are variables that are shared by all instances of a class.
They are defined outside the constructor method and are the same for all objects.
There is only one copy of the class variable and when any object makes changes to a class variable, that change will be seen by all the other instances. An object variable with the same name as a class variable will vide the class variable.

Example:

class Car:
  make = "Toyota"  # class attribute
  model = "Corolla"  # class attribute
  
  def __init__(self, year):
    self.year = year  # instance attribute
    
c1 = Car(2010)
c2 = Car(2015)

In this example, make and model are class attributes of the Car class, and year is an instance attribute. The make and model attributes are shared by all Car objects, while the year attribute is unique to each object.

Public Instance variables

In Python, instance variables are considered public by default, which means they can be accessed and modified from outside the class.

class Person:
 def __init__(self, name, age):
   self.name = name  # public instance variable
   self.age = age  # public instance variable

person = Person("John", 30)
print(person.name)  # prints "John"

They don't have any special prefixes or suffixes. These instance variables can be accessed or modified from outside the Person class using the dot notation.

Private Instance Variables

Instance variables are variables that are associated with an instace of a class. In python you can make an instance variable private by adding a double underscore prefix to its name. This is the convention used to indicate that the instance variable should not be directly accessed or modified from outside the class.
Example:

class Person:
  def __init__(self, name, age):
    self.__name = name  # private instance variable
    self.__age = age  # private instance variable

person = Person("John", 30)
print(person.__name)  # this will raise an AttributeError

These instance variables can only be accessed or modified from within the Person class using setter and getter methods.
It's worth noting that the double underscore prefix does not actually make the instance variables completely private in Python. It only renames the instance variables to include the class name as a prefix, which makes it more difficult to access them from outside the class. However, the instance variables can still be accessed using the _classname__variablename notation.

print(person._Person__name)  # prints "John"

object._classname__variablename notation

Although it's possible to access private instance variables using the _classname__variablename notation, it's generally considered bad practice to do so. Private instance variables are meant to be an implementation detail of the class and should not be accessed or modified directly from outside the class.

Private class variable

class variables are variables that are associated with a class and are shared among all instances of the class. In Python, you can make a class variable private by adding a double underscore prefix to its name. This is a convention used to indicate that the class variable should not be directly accessed or modified from outside the class.
Example:

class Person:
  # private class variable
  __total_count = 0

  def __init__(self, name, age):
    self.name = name
    self.age = age
    Person.__total_count += 1

person1 = Person("John", 30)
person2 = Person("Jane", 25)
print(Person.__total_count)  # this will raise an AttributeError

Public class variables

In Python, class variables are considered public by default, which means they can be accessed and modified from outside the class.

The Self

Class methods have one specific difference from ordinary functions. They have an extra first argument that has to be added to the beginning of the parameter list. But you do not give a value for this parameter when you call the method. Python will provide it.
It is a special variable that refers to the current instance of a class. It is used to access the attributes and methods of the current object from within the class. by convention it is called self but you could call it anything. Example:

class Dog:
  def __init__(self, name, breed):
    self.name = name
    self.breed = breed
    
  def bark(self):
    print("Woof!")

The __init__ method takes three arguments: self, name and breed but when its called during the creation of an object only name and breed are provided.

dog1 = Dog("Fido", "Labrador")

similarly, the bark function takes one parameter self but when calling the function, no arguments are provided.

dog1.bark()  # prints "Woof!"

This is because when the method is called, its automatically converted to:

Dog.bark(dog1)

and

Dog.__init__(dog1, name, breed)

The self argument is not specified when calling the method, but it is automatically passed to the method when it is called.

Method Types

In object-oriented programming (OOP), a method is a function that is defined inside a class. It is used to perform some action or calculation on the object. There are several types of methods in OOP, including instance methods, class methods, and static methods.

1. Instance methods

These are functions that operate on a specific instance of a class.
They are defined inside the class definition and and take self as an argument which refers to the current instace of the object.

2. Class methods.

These are functions that operate on the class itself rather than on a specific instance of the class. They are defined using the @classmethod decorator and they take cls as the first argument which refers to the class itself.

3. Static methods

These are functions that are associated with a class, but they do not operate on the class or on the instance of the class. They are defined using the @staticmethod decorator and they do not take any special arguments.

Example:

class Dog:
  # class attribute
  species = "Canis familiaris"  

  def __init__(self, name, breed):
    # Instance attribute
    self.name = name
    self.breed = breed

  # instance method 
  def say_hi(self):  
    print("Hello, my name is {} and my breed is {}".format(self.name, self.breed))

  @classmethod
  def get_species(cls):  # class method
    return cls.species

  @staticmethod
  def bark():  # static method
    print("Woof!")

N/B: You must refer to the variables and methods of the same object using the self only. This is called attribute reference.

Public methods

In Python, methods are considered public by default, which means they can be called from outside the class.

Private methods

In Python, you can make a method private by adding a double underscore prefix to its name. This is a convention used to indicate that the method should not be directly called from outside the class.
Private methods are meant to be an implementation detail of the class and you should use public methods to access and modify private methods.
Example:

class Person:
  def __init__(self, name, age):
    self.name = name
    self.age = age

  # private method
  def __increment_age(self):
    self.age += 1

  # public method
  def birthday(self):
    self.__increment_age()

person = Person("John", 30)
person.__increment_age()  # this will raise an AttributeError
person.birthday()
print(person.age)  # prints 31

In the example above, the __increment_age method is private and can only be called from within the Person class. The birthday method is public and can be called from outside the Person class. When the birthday method is called, it calls the private __increment_age method to increment the person's age by one.
By using public methods to access and modify private methods, you can encapsulate the implementation details of the class and provide a more intuitive and user-friendly interface to the users of the class.

Magic methods

These are special methods in Python that are used to implement operator overloading.
They are called "magic methods" because they are not visible to the user, but they are called begind the scenes to perform certain actions.
Magic methods are defined with a double underscore prefix and a double underscore suffix, such as __init__ or __add__. They are used to define the behavior of certain operators, such as the + operator or the [] operator, when they are applied to objects of a class.

Examples:

  1. __init__: This is the constructor method, which is called when an object is created(instantiated) from a class. It is used to iniialize the attributes of the object.

  2. __str__: This method is called when the str() function is applied on an object of a class. This should return the string representation of the object.

  3. __len__: This method is called when the len() function is applied to an object of the class. It should return the length of the object.

  4. __add__: This method is called when the + operaor is applied to two objects of a class. It should return the result of the addition.

  5. __getitem__: This method is called when the [] operator is applied to an object of a class. It should return the element at the specified index.

  6. __repr__: This method is called when the repr() function is applied to an object. It should return a string representation of Python code needed to rebuild the object ising eval().

  7. __setitem__: This method is called when an object is indexed and assigned a value such as object[key] = value. It should set the value at he specified index.

  8. __delitem__: This method is called when an object is indexed and deleted such as del object[key]. It should delete the value at the specified index.

  9. __contains__: This method is called when the in keyword is used to check if an object is contained in another object. It should return True if the value is contained in the object otherwise False.

  10. __iter__: This method is called when an object is iterated over, such as in a for loop. It should return an iterator object.

  11. __del__: This method is used to clean up resources when an object is deleted. It is called automatically when an object is deleted and is used to release resources that the object was using.

class Vector:
  def __init__(self, x, y):
    self.x = x
    self.y = y
    
  def __add__(self, other):
    return Vector(self.x + other.x, self.y + other.y)
 
  def __str__(self):
    return f"x = {self.x} and y = {self.y}"
    
  def __repr__(self):
    return "Vector({}, {})".format(self.x self.y)
    
v1 = Vector(1, 2)
v2 = Vector(3, 4)
v3 = v1 + v2  # calls __add__ method
print(v3)  # calls __str__ method
# x = 4 and y = 6
repr(v1) # calls __repr__ method
# Vector(1, 2)
v6 = eval(repr(Vector(2, 4))) # eval recreates a new object from the string produced by __repr__
print(str(v6))
# x = 2 and y = 4

Setters and Getters

A setter is a method that updates the value of an instance variable.
A getter is a method that retrieves the value of an instance variable.
setters and getters are used to give you more control over how instance variables are accessed and modified.
Example:

class Person:
  def __init__(self, name, age):
    self.__name = name  # private instance variable
    self.__age = age  # private instance variable

  # getter method
  def get_name(self):
    return self.__name

  # setter method
  def set_name(self, name):
    self.__name = name

  # getter method
  def get_age(self):
    return self.__age

  # setter method
  def set_age(self, age):
    self.__age = age

In Python, you can also use decorators to define setters and getters. Here is an example of how to use decorators to define setters and getters:

class Person:
  def __init__(self, name, age):
    self.__name = name  # private instance variable
    self.__age = age  # private instance variable

  # getter decorator
  @property
  def name(self):
    return self.__name

  # setter decorator
  @name.setter
  def name(self, name):
    self.__name = name

  # getter decorator
  @property
  def age(self):
    return self.__age

  # setter decorator
  @age.setter
  def age(self, age):
    self.__age = age

# create an instance of the Person class
person = Person("John", 30)

# retrieve the name using the getter decorator
name = person.name
print(name)  # prints "John"

# update the name using the setter decorator
person.name = "Jane"

# retrieve the updated name using the getter decorator
name = person.name
print(name)  # prints "Jane"

Clone this wiki locally