Python is an object oriented programming language. Almost everything in Python is an object, with its attributes and methods.
Class creates a user-defined data structure, which holds its own data members (attributes) and member functions (methods), which can be accessed and used by creating an instance of that class.
Classes are like a blueprint for objects outlining possible behaviors and states that every object of a certain type could have.
Classes are defined via the keyword class
, like this:
# Define an empty class Employee
class Employee:
pass
Objects/Instances
An Object is an instance of a Class. A class is like a blueprint while an instance is a copy of the class with actual values. It’s not an idea anymore, it’s an actual employee, like a person named "Andrew" who is 30 years old.
You can have many employees to create many different instances, but without the class as a guide, you would be lost, not knowing what information is required.
After a class is defined, we can create objects of the class type like this:
# Define an empty class Employee
class Employee:
pass
# Create an Employee object
employee = Employee()
Attributes/Properties
A class attribute is a Python variable that belongs to a class and is shared between all the objects of this class. Let's add some attributes to our Employee
class:
class Employee:
name = 'Andrew'
age = 30
emp1 = Employee()
emp2 = Employee()
print(emp1.name) # prints 'Andrew'
emp2.age = 40
print(emp2.age) # prints 40
We added two properties: the string name
and the int age
. Then, we created two different Employee objects emp1
and emp2
.
The attributes of each object can be accessed and modified using the dot operator (.
) like we did above with emp1.name
and emp2.age
.
The problem here is that emp1
and emp2
start with the same attribute values, name: 'Andrew'
and age: 30
.
In real life, we want to set the attributes specific to each object when creating it. For example, an employee named Andrew of 30 years old and another one named Mary of 25 years old. Here the constructor comes to rescue.
Constructors
A constructor is a special type of method (function) which is used to initialize the instance members of the class.
In Python the __init__()
method is called the constructor and is always called when an object is created.
The constructor takes its first argument as a reference to the instance being constructed known as self
and the rest of the arguments are provided by the programmer.
Let's add a constructor to our Employee class:
class Employee:
def __init__(self, name, age):
self.name = name
self.age = age
emp1 = Employee('Andrew', 30) # emp1.name = 'Andrew', emp1.age = 30
emp2 = Employee('Mary', 25) # emp2.name = 'Mary', emp2.age = 25
The "self"
Class methods must have an extra first parameter in the method definition. We do not give a value for this parameter when we call the method, Python provides it.
This extra parameter, known as self
, is a refference to the object we called the method on. We use self
to access and manipulate the attributes of that object, like we did above with self.name = name
.
Basically, when we call a method of this object as myobject.method(arg1, arg2)
, this is automatically converted by Python into MyClass.method(myobject, arg1, arg2)
– this is all the special self is about.
Member functions
Member functions are functions that belong to the class:
class Employee:
def __init__(self, name, age):
self.name = name
self.age = age
def printName(self):
print(self.name)
emp1 = Employee('Andrew', 30)
emp1.printName() # prints 'Andrew'
emp2 = Employee('Mary', 25)
emp2.printName() # prints 'Mary'
As you can see, we access functions just like we access attributes: by creating an object of the class and using the dot syntax (.).
The function call uses the attributes of the object it was called on. That's why the first printName()
prints 'Andrew' and the second prints 'Mary'.
Calling member functions from other member functions
One more important thing to know is that when we call a member function from an other member function, we should use the self
keyword:
class Employee:
def __init__(self, name, age):
self.name = name
self.age = age
def printName(self):
print(self.name)
def printEmployee(self):
self.printName() # here
print(self.age)
emp = Employee('Andrew', 30)
emp.printEmployee() # prints 'Andrew', 30
Here, inside printEmployee(self)
we call printName
function like this: self.printName()
. We always do it like this in these scenarios.
Assignment
Follow the Coding Tutorial and let's write some classes.
Hint
Look at the examples above if you get stuck.
In this lesson, we will explore the concept of classes in Python, a fundamental aspect of object-oriented programming (OOP). Understanding classes is crucial for creating complex and efficient programs. Classes allow us to model real-world entities and their interactions, making our code more modular, reusable, and easier to maintain.
Classes are particularly useful in scenarios where we need to represent objects with similar attributes and behaviors, such as employees in a company, products in an inventory, or users in a social media application.
Before diving into the details, let's understand some fundamental concepts:
Let's start with a simple example to illustrate these concepts:
# Define a simple class
class Dog:
species = "Canis familiaris" # Class attribute
def __init__(self, name, age):
self.name = name # Instance attribute
self.age = age # Instance attribute
def bark(self):
print(f"{self.name} says woof!")
# Create an instance of the Dog class
my_dog = Dog("Buddy", 3)
print(my_dog.name) # Output: Buddy
print(my_dog.species) # Output: Canis familiaris
my_dog.bark() # Output: Buddy says woof!
Now, let's delve deeper into the key concepts and techniques involved in working with classes in Python:
We define a class using the class
keyword followed by the class name and a colon. Inside the class, we define attributes and methods:
class Employee:
pass # An empty class
We create an object (instance) of a class by calling the class name followed by parentheses:
employee = Employee()
Attributes are variables that belong to a class. They can be class attributes (shared by all instances) or instance attributes (unique to each instance):
class Employee:
company = "TechCorp" # Class attribute
def __init__(self, name, age):
self.name = name # Instance attribute
self.age = age # Instance attribute
Methods are functions that belong to a class. They define the behaviors of an object:
class Employee:
def __init__(self, name, age):
self.name = name
self.age = age
def print_details(self):
print(f"Name: {self.name}, Age: {self.age}")
Let's explore some examples and use cases to understand how classes can be applied in various contexts:
class Book:
def __init__(self, title, author, year):
self.title = title
self.author = author
self.year = year
def get_info(self):
return f"{self.title} by {self.author}, published in {self.year}"
# Create instances of the Book class
book1 = Book("1984", "George Orwell", 1949)
book2 = Book("To Kill a Mockingbird", "Harper Lee", 1960)
print(book1.get_info()) # Output: 1984 by George Orwell, published in 1949
print(book2.get_info()) # Output: To Kill a Mockingbird by Harper Lee, published in 1960
class Student:
def __init__(self, name, student_id, grades):
self.name = name
self.student_id = student_id
self.grades = grades
def calculate_gpa(self):
return sum(self.grades) / len(self.grades)
# Create instances of the Student class
student1 = Student("Alice", "S001", [90, 85, 88])
student2 = Student("Bob", "S002", [78, 82, 80])
print(student1.calculate_gpa()) # Output: 87.66666666666667
print(student2.calculate_gpa()) # Output: 80.0
When working with classes, it's important to be aware of common pitfalls and follow best practices:
self
in method definitions and calls.Once you are comfortable with the basics, you can explore advanced techniques such as inheritance, polymorphism, and encapsulation:
Inheritance allows a class to inherit attributes and methods from another class:
class Manager(Employee):
def __init__(self, name, age, department):
super().__init__(name, age)
self.department = department
def print_details(self):
super().print_details()
print(f"Department: {self.department}")
manager = Manager("Alice", 35, "HR")
manager.print_details()
Polymorphism allows methods to be used interchangeably between different classes:
class Dog:
def speak(self):
return "Woof!"
class Cat:
def speak(self):
return "Meow!"
def make_sound(animal):
print(animal.speak())
dog = Dog()
cat = Cat()
make_sound(dog) # Output: Woof!
make_sound(cat) # Output: Meow!
Let's implement a comprehensive example that demonstrates the use of classes, attributes, methods, and advanced techniques:
class Vehicle:
def __init__(self, make, model, year):
self.make = make
self.model = model
self.year = year
def get_info(self):
return f"{self.year} {self.make} {self.model}"
class Car(Vehicle):
def __init__(self, make, model, year, doors):
super().__init__(make, model, year)
self.doors = doors
def get_info(self):
return f"{super().get_info()} with {self.doors} doors"
class Motorcycle(Vehicle):
def __init__(self, make, model, year, type):
super().__init__(make, model, year)
self.type = type
def get_info(self):
return f"{super().get_info()} which is a {self.type} motorcycle"
# Create instances of Car and Motorcycle
car = Car("Toyota", "Camry", 2020, 4)
motorcycle = Motorcycle("Harley-Davidson", "Street 750", 2019, "cruiser")
print(car.get_info()) # Output: 2020 Toyota Camry with 4 doors
print(motorcycle.get_info()) # Output: 2019 Harley-Davidson Street 750 which is a cruiser motorcycle
Debugging and testing are crucial for ensuring the correctness of your code. Here are some tips:
self
references.Writing tests helps you verify that your code works as expected. You can use the unittest
module in Python:
import unittest
class TestEmployee(unittest.TestCase):
def test_employee_creation(self):
emp = Employee("John", 28)
self.assertEqual(emp.name, "John")
self.assertEqual(emp.age, 28)
def test_employee_method(self):
emp = Employee("John", 28)
self.assertEqual(emp.printName(), "John")
if __name__ == "__main__":
unittest.main()
When approaching problems related to classes, consider the following strategies:
In this lesson, we covered the basics of classes in Python, including defining classes, creating objects, working with attributes and methods, and exploring advanced techniques like inheritance and polymorphism. Mastering these concepts is essential for writing efficient and maintainable code in Python.
We encourage you to practice by creating your own classes and experimenting with different scenarios. The more you practice, the more comfortable you will become with object-oriented programming in Python.
For further reading and practice, consider the following resources: