Understanding Encapsulation, Inheritance, and Polymorphism in Python Classes
Class Encapsulation
Encapsulation refers to the bundling of data and methods within a class, hiding internal implementation details from external access. External code can only interact with the object through defined interfaces.
Example:
class Individual:
def __init__(self, name, years):
self.name = name
self.years = years
person = Individual(name="Xiaohong", years=27)
if person.years >= 18:
print(f"{person.name} is an adult")
else:
print(f"{person.name} is underage")
To determine if someone is an adult, one needs to access years. However, age is often considered private information. To prevent unauthorized access, prefixing the attribute with double underscores makes it private. This approach ensures that sensitive data remains hidden.
Here's how encapsulation can be implemented:
class Individual:
def __init__(self, name, years):
self.name = name
self.__years = years
def is_adult(self):
return self.__years >= 18
person = Individual(name="Xiaohong", years=27)
person.is_adult()
Class Inheritance
Inheritance allows a new class to acquire properties and methods from an existing class. The original class is called the base (or parent) class, and the derived class is known as the child class.
The syntax for inheritance is:
class ChildClass(ParentClass):
Single Inheritance
Consider the following example where a People class defines a speak method, and a Student class inherits from People.
# Base class definition
class People:
def __init__(self, name, age, weight):
self.name = name
self.age = age
def speak(self):
print(f"{self.name} says: I am {self.age} years old.")
# Derived class
class Student(People):
def __init__(self, name, age, weight, level):
People.__init__(self, name, age, weight)
self.level = level
Since inheritance is used, instances of Student will have all attributes and methods of People, including the speak method.
>>> student = Student(name="Xiaoming", age=10, weight=50, level="Grade 3")
>>> student.speak()
Xiaoming says: I am 10 years old.
Method overriding occurs when the derived class redefines a method from the parent class:
class Student(People):
def __init__(self, name, age, weight, level):
People.__init__(self, name, age, weight)
self.level = level
def speak(self):
print(f"{self.name} says: I am {self.age} years old and studying in {self.level}")
Now calling speak will invoke the overridden version:
>>> student = Student(name="Xiaoming", age=10, weight=50, level="Grade 3")
>>> student.speak()
Xiaoming says: I am 10 years old and studying in Grade 3
Multiple Inheritance
Python supports multiple inheritance, allowing a class to inherit from more than one parent class:
class ChildClass(ParentClass1, ParentClass2, ParentClass3...):
Below is a set of classes demonstrating multiple inheritance:
class D: pass
class C(D): pass
class B(C):
def display(self):
print("I am B")
class G: pass
class F(G): pass
class E(F):
def display(self):
print("I am E")
class A(B, E): pass
When creating an instance of class A, and calling its display method:
>>> a = A()
>>> a.display()
i am B
Because clas A does not define display, it searches its parents. It finds the method first in class B and executes it. Therefore, the order of parent classes matters in multiple inheritance.
What happens if class B doesn't define display, but class D does?
class D:
def display(self):
print("I am D")
class C(D): pass
class B(C): pass
class G: pass
class F(G): pass
class E(F):
def display(self):
print("I am E")
class A(B, E): pass
Executing this code produces:
>>> a = A()
>>> a.display()
i am D
This behvaior follows the left-to-right, depth-first search rule for resolving method calls in multiple inheritance.
Class Polymorphism
Polymorphism describes how different objects can respond differently to the same method call.
class Human:
def talk(self):
pass
class American(Human):
def talk(self):
print("Hello, boys")
class Chinese(Human):
def talk(self):
print("你好,老铁")
p1 = American()
p2 = Chinese()
Both American and Chinese inherit from Human but implement talk differently. An execute_talk function demonstrates polymorphism:
def execute_talk(human):
human.talk()
execute_talk(p1)
execute_talk(p2)
Regardless of whether the argument is an American or Chinese instance, as long as it has the talk method, it works correctly.