Object-oriented programming (OOP) is a programming paradigm that uses objects to design applications and computer programs. In OOP, an object is an instance of a class that contains data and behavior. The primary goal of OOP is to increase the flexibility and maintainability of code.
The four major concepts of OOP are:
- Encapsulation: Encapsulation is the concept of keeping the data and the methods that operate on the data in a single unit, known as a class. Encapsulation allows for abstraction, which means that the complexity of the implementation is hidden from the user.
- Inheritance: Inheritance is the concept of creating a new class from an existing class. The new class inherits the properties and methods of the existing class, which is known as the parent or superclass.
- Polymorphism: Polymorphism is the concept of using a single interface to represent multiple types of data. This allows for flexibility and reusability of code.
- Abstraction: Abstraction is the concept of separating the interface of an object from its implementation. This allows for the user to interact with the object through its interface, without needing to know the details of its implementation.
In Python, OOP is implemented through classes and objects. A class is a blueprint for creating objects, and an object is an instance of a class. The properties and methods of a class are defined in the class definition, and are shared by all objects of that class.
The following is an example of a simple class definition in Python:
class Car:
def __init__(self, make, model, year):
self.make = make
self.model = model
self.year = year
def get_make(self):
return self.make
def get_model(self):
return self.model
def get_year(self):
return self.year
This class represents a car, and has three properties (make, model, and year) and three methods (get_make(), get_model(), and get_year()). The __init__() method is a special method that is called when an object of the class is created, and is used to initialize the object’s properties.
Classes and objects, defining a class, and creating an instance of a class
In object-oriented programming (OOP), a class is a blueprint for creating objects that encapsulate data and behavior. An object is an instance of a class, and it can have its own data and behavior.
A class consists of data members and methods. Data members are variables that hold data, and methods are functions that perform operations on the data. When an object is created, the data members are initialized to their default values, and the methods can be called to perform operations on the data.
To define a class in Python, you use the class keyword followed by the name of the class, as shown below:
class MyClass:
# Class definition goes here
To create an instance of a class, you call the class constructor by using the name of the class, followed by parentheses. For example:
my_object = MyClass()
This creates a new instance of the MyClass class and assigns it to the variable my_object.
Once you have created an object, you can access its data members and methods by using the dot notation, as shown below:
my_object.data_member = 42
my_object.method()
This sets the data_member variable to 42 and calls the method() method on the my_object instance.
By using classes and objects, you can organize your code into modular, reusable components that are easier to maintain and extend over time.
Class attributes and instance attributes
In Python, attributes are variables that store data. In the context of OOP, attributes can be defined at both the class level and the instance level.
Class attributes are attributes that are shared by all instances of a class. They are defined inside the class but outside any methods. Class attributes are accessed using the class name. For example:
class Dog:
species = "mammal"
In this example, species is a class attribute that is shared by all instances of the Dog class. It can be accessed using the class name Dog.
Instance attributes are attributes that are specific to an instance of a class. They are defined inside the class’s methods using the self keyword. Instance attributes are accessed using the instance name. For example:
class Dog:
def __init__(self, name, age):
self.name = name
self.age = age
In this example, name and age are instance attributes that are specific to each instance of the Dog class. They are accessed using the instance name, such as my_dog.name.
It’s important to note that instance attributes override class attributes with the same name. For example:
class Dog:
species = "mammal"
def __init__(self, name, age):
self.name = name
self.age = age
self.species = "canine"
In this example, species is both a class attribute and an instance attribute. When an instance of the Dog class is created, the species attribute is set to “canine” for that instance, while the species attribute for all other instances of the Dog class remains “mammal”.
Class methods and instance methods
In object-oriented programming, classes can have both class methods and instance methods.
A class method is a method that belongs to the class and not to any specific instance of the class. It can be called on the class itself and can access class-level data. Class methods are defined using the @classmethod decorator.
An instance method, on the other hand, is a method that belongs to a specific instance of the class. It can access instance-level data and is defined without any decorator.
Here is an example that demonstrates the difference between class methods and instance methods:
class MyClass:
class_variable = "I am a class variable"
def __init__(self, instance_variable):
self.instance_variable = instance_variable
def instance_method(self):
print("I am an instance method and I can access the instance variable: ", self.instance_variable)
@classmethod
def class_method(cls):
print("I am a class method and I can access the class variable: ", cls.class_variable)
# Creating an instance of MyClass
obj = MyClass("I am an instance variable")
# Calling the instance method
obj.instance_method() # Output: I am an instance method and I can access the instance variable: I am an instance variable
# Calling the class method
MyClass.class_method() # Output: I am a class method and I can access the class variable: I am a class variable
In the example above, the class MyClass has a class variable class_variable and an instance variable instance_variable. The instance_method() is an instance method that can access the instance variable, while the class_method() is a class method that can access the class variable.
Note that we use self to refer to the current instance of the class in an instance method and cls to refer to the class in a class method.
Inheritance and polymorphism
Inheritance is one of the fundamental concepts in object-oriented programming (OOP). It is the mechanism that allows you to create a new class from an existing class. The new class, called the subclass or derived class, inherits the attributes and methods of the existing class, called the superclass or base class. In Python, you can create a subclass by specifying the name of the superclass in parentheses after the name of the subclass.
Here’s an example:
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
def introduce(self):
print(f"My name is {self.name} and I am {self.age} years old.")
class Student(Person):
def __init__(self, name, age, student_id):
super().__init__(name, age)
self.student_id = student_id
def study(self):
print("I am studying.")
student = Student("John", 21, "1234")
student.introduce() # My name is John and I am 21 years old.
student.study() # I am studying.
In this example, we have a Person class with an __init__ method and an introduce method. We then create a Student class that inherits from the Person class. The Student class has its own __init__ method that takes an additional argument, student_id, and a study method. To call the superclass’s __init__ method from the subclass, we use the super() function.
When we create an instance of the Student class, we can call the introduce method from the Person class as well as the study method from the Student class.
Polymorphism is the ability of an object to take on many forms. In Python, polymorphism is implemented using method overriding and method overloading.
Method overriding occurs when a child class has a method that has the same name and function signature as a method in its parent class. The child class’s method overrides the parent class’s method, giving the child class its own implementation of the method.
Method overloading is not directly supported in Python, but it can be simulated by using default argument values. In this case, a single method can have multiple signatures, depending on the arguments passed to it.
Here’s an example of method overriding in Python:
rubyCopy codeclass Animal:
def speak(self):
print("The animal speaks.")
class Dog(Animal):
def speak(self):
print("The dog barks.")
a = Animal()
d = Dog()
a.speak() # prints "The animal speaks."
d.speak() # prints "The dog barks."
In this example, the Animal class has a speak method that simply prints “The animal speaks.” The Dog class inherits from the Animal class and overrides the speak method with its own implementation that prints “The dog barks.” When we create instances of these classes and call their speak methods, we see that the correct implementation of the speak method is called for each class.
Here’s an example of method overloading in Python:
pythonCopy codeclass Math:
def add(self, x, y=0, z=0):
return x + y + z
m = Math()
print(m.add(1)) # prints 1
print(m.add(1, 2)) # prints 3
print(m.add(1, 2, 3)) # prints 6
In this example, the Math class has an add method that takes three arguments. However, the second and third arguments have default values of 0, so they can be omitted. When we call the add method with one, two, or three arguments, we get the expected results, even though we’re technically calling the same method with different signatures.
Exercise:
Write a program to create a simple bank account class with methods to withdraw, deposit and check balances.