Procedural programming focuses on the step-by-step workflow to implement a feature, requiring developers to handle every execution stage manually. Object-oriented programming shifts focus to reusable entities that can complete the target task, eliminating the need to implement every underlying logic manually.
Common real-world comparisons:
-
Laundry Procedural (hand wash): Take off dirty clothes, prepare a basin, pour water, add detergent, soak for 30 mins, scrub clothes, wring out, drain water, rinse repeatedly, wring again, hang to dry. OOP (machine wash): Take off dirty clothes, load into washing machine, start wash cycle, take out clean clothes to hang.
-
Purchase a laptop Procedural: Confirm requirement, research hardware parameters, compare multiple models, check promotion events, negotiate price with seller, place order, receive package, test device, confirm receipt. OOP: Confirm requirement, ask assistant to handle purchase, receive laptop.
-
Meal preparation Procedural: Feel hungry, buy groceries, wash and sort ingredients, cut vegetables, preheat stove, cook, serve food, eat, wash dishes. OOP: Feel hungry, order takeout, eat.
Classes and Objects
Two core concepts form the foundation of object-oriented programming: classes and objects. Objects are concrete runtime entities, while classes are abstract templates that group objects with identical attributes and behaviors.
Classes
Classes in object-oriented languages are designed to model real-world entities, consisting of two core components:
- Attributes: Descriptive features of an entity that define what the entity is.
- Behaviors: Callable capabilities of an entity that define what the entity can do. A class is an abstract template, for example a mobile phone design blueprint, while an object is a concrete instance produced from the template, i.e. a working physical mobile phone. One class can generate any number of unique object instances.
Objects
Objects are tangible, concrete instances of classes that exist in runtime memory. Every object is created based on its parent class template.
Class Composition
A standard Python class includes three parts:
- Class identifier (class name)
- Attribute set (data variables)
- Method set (callable behaviors)
Define a Class
The basic syntax for defining a class in Python is as follows:
class ClassName:
# method and attribute definitions
Example: Define a Champion class for game characters
# New-style class definition (recommended for Python 3)
class Champion(object):
def introduce(self):
print("Every champion has their own unique origin story.")
Notes:
- Python 3 supports both classic (old-style) and new-style classes, new-style classes inherit from
objectexplicitly and are the recommended practice. objectis the root parent class for all built-in and custom classes in Python.- Class names follow the UpperCamelCase naming convention.
- Instance methods require
selfas the first parameter, which points to the current object instance when called. You can renameselfto any valid variable name, but this convention is universally adopted.
Create Object Instances
You can generate multiple independent objects from a defined class, using the following syntax:
object_instance1 = ClassName()
object_instance2 = ClassName()
Example: Create a champion instance for Tryndamere
class Champion(object):
def introduce(self):
print("self points to the current object instance:", self)
print("Each instance is a unique champion entity.")
tryndamere = Champion()
tryndamere.introduce()
# Print the memory address of the instance, matches the self output above
print(tryndamere)
# Print decimal format of the memory address
print(id(tryndamere))
The self parameter always points to the object that calls the current instance method, as demonstrated in the following example:
class WashingMachine:
def run_wash(self):
print("Starting laundry cycle")
print("Current machine instance:", self)
haier_model1 = WashingMachine()
print("Haier machine 1 address:", haier_model1)
haier_model1.run_wash()
haier_model2 = WashingMachine()
print("Haier machine 2 address:", haier_model2)
The output confirms that self and the object instance share the same memory address.
Add and Access Object Attributes
Attributes are the characteristic values of an object, which can be set and retrieved both inside and outside the class definition.
class Champion(object):
def move(self):
print("Moving to target location")
def attack(self):
print("Launching standard attack")
tryndamere = Champion()
# Add custom attributes to the instance
tryndamere.champ_name = "Tryndamere"
tryndamere.health = 2800
tryndamere.attack_damage = 480
tryndamere.armor = 220
# Access attribute values
print(f"Champion {tryndamere.champ_name} Health: {tryndamere.health}")
print(f"Champion {tryndamere.champ_name} Attack Damage: {tryndamere.attack_damage}")
print(f"Champion {tryndamere.champ_name} Armor: {tryndamere.armor}")
# Call instance methods
tryndamere.move()
tryndamere.attack()
Magic Methods
Methods with names wrapped in double underscores __xx__ are called magic methods in Python, which have pre-defined special functions.
__init__() Initialization Method
The __init__() method is used to initialize instance attributes when an object is created, and is called automatically without manual invocation.
class Champion(object):
def __init__(self):
# Initialize default attribute values during instantiation
self.champ_name = "Tryndamere"
self.health = 2800
self.attack_damage = 480
self.armor = 220
def move(self):
print(f"{self.champ_name} is moving to target location")
def attack(self):
print("Launching standard attack")
tryndamere = Champion()
tryndamere.move()
tryndamere.attack()
Notes:
__init__()runs automatically immediately after an object is created.- The
selfparameter is passed automatically by the Python interpreter, no manual input is required.
Parameterized __init__() Method
You can add custom parameters to __init__() to set different attribute values for different instances:
class Champion(object):
def __init__(self, name, skill, health, ad, armor):
self.champ_name = name
self.skill = skill
self.health = health
self.attack_damage = ad
self.armor = armor
def move(self):
print(f"{self.champ_name} is moving to target location")
def cast_skill(self):
print(f"Casting {self.skill}")
def show_stats(self):
print(f"Champion {self.champ_name} | Health: {self.health} | AD: {self.attack_damage} | Armor: {self.armor}")
tryndamere = Champion("Tryndamere", "Spinning Slash", 2800, 480, 220)
garen = Champion("Garen", "Demacian Justice", 4500, 280, 420)
tryndamere.show_stats()
garen.show_stats()
# Instance attributes are stored independently for each object
print(id(tryndamere.champ_name))
print(id(garen.champ_name))
# Instance methods are shared across all instances of the same class to save memory
print(id(tryndamere.move))
print(id(garen.move))
Notes:
- All instance attributes and methods inside the class are accessed via
self. - All attributes and methods outside the class are accessed via the object instance name.
- Each instance has independent storage for its attributes, while methods are shared across all instances. The interpreter uses
selfto identify which instance called the method.
__str__() Method
By default, printing an object outputs its memory address. If you define a __str__() method in the class, printing the object will return the string value specified in the __str__() return statement.
class Champion(object):
def __init__(self, name, skill, health, ad, armor):
self.champ_name = name
self.skill = skill
self.health = health
self.attack_damage = ad
self.armor = armor
def move(self):
print(f"{self.champ_name} is moving to target location")
def cast_skill(self):
print(f"Casting {self.skill}")
def __str__(self):
return f"<Champion: {self.champ_name}> | Health: {self.health} | AD: {self.attack_damage} | Armor: {self.armor}"
tryndamere = Champion("Tryndamere", "Spinning Slash", 2800, 480, 220)
garen = Champion("Garen", "Demacian Justice", 4500, 280, 420)
print(tryndamere)
print(garen)
# Print class docstring
print(Champion.__doc__)
The __str__() method must return a string value, which is typically used as a human-readable description of the object instance.
__del__() Destructor Method
The __del__() method is called automatically when an object instance is deleted and its memory is reclaimed.
class Champion(object):
def __init__(self, name):
print("__init__() method executed")
self.champ_name = name
def __del__(self):
print("__del__() method executed")
print(f"{self.champ_name} has been removed from the game")
tryndamere = Champion("Tryndamere")
print(f"Deleting instance with address {id(tryndamere)}")
del tryndamere
print("-"*20)
garen = Champion("Garen")
garen_ref1 = garen
garen_ref2 = garen
print(f"Deleting garen reference, address {id(garen)}")
del garen
print(f"Deleting garen_ref1 reference, address {id(garen_ref1)}")
del garen_ref1
print(f"Deleting garen_ref2 reference, address {id(garen_ref2)}")
del garen_ref2
Notes:
- Each time a variable references an object, the object's reference count increases by 1.
- Each
delcall on a reference reduces the reference count by 1. The object is only deleted and memory reclaimed when the reference count reaches 0.
Practical Example 1: Roasted Sweet Potato Simulator
Requirements:
- Roast status changes with cooking time:
- 0-2 minutes: Raw
- 2-4 minutes: Half-cooked
- 4-7 minutes: Fully cooked
- Over 7 minutes: Burnt
- Users can add custom seasonings during cooking.
Implementation Code:
class RoastedSweetPotato:
def __init__(self):
self.cook_duration = 0
self.status = "Raw"
self.seasonings = []
def cook(self, time):
self.cook_duration += time
if 0 <= self.cook_duration < 2:
self.status = "Raw"
elif 2 <= self.cook_duration <4:
self.status = "Half-cooked"
elif 4 <= self.cook_duration <7:
self.status = "Fully cooked"
else:
self.status = "Burnt"
def add_seasoning(self, seasoning):
self.seasonings.append(seasoning)
def __str__(self):
return f"Sweet potato cooked for {self.cook_duration} minutes, status: {self.status}, added seasonings: {self.seasonings}"
potato = RoastedSweetPotato()
print(potato)
potato.cook(2)
potato.add_seasoning("Soy sauce")
print(potato)
potato.cook(2)
potato.add_seasoning("Chili powder")
print(potato)
potato.cook(2)
print(potato)
potato.cook(2)
print(potato)
Practical Example 2: Furniture Moving Simulator
Requirements:
Place furniture items into a house if the furniture area is smaller than the remaining free area of the house.
Implementation Code:
class Furniture:
def __init__(self, item_name, area_occupied):
self.name = item_name
self.area = area_occupied
class House:
def __init__(self, location, total_area):
self.location = location
self.total_area = total_area
self.free_area = total_area
self.furniture_list = []
def __str__(self):
return f"House at {self.location}, total area: {self.total_area}㎡, remaining free area: {self.free_area}㎡, furniture: {self.furniture_list}"
def place_furniture(self, furniture_item):
if self.free_area >= furniture_item.area:
self.furniture_list.append(furniture_item.name)
self.free_area -= furniture_item.area
print(f"Successfully placed {furniture_item.name}")
else:
print(f"Not enough space to place {furniture_item.name}, required {furniture_item.area}㎡, remaining {self.free_area}㎡")
king_bed = Furniture("King size bed", 8)
sofa = Furniture("L-shaped sofa", 12)
apartment = House("Shanghai Pudong", 120)
print(apartment)
apartment.place_furniture(king_bed)
print(apartment)
apartment.place_furniture(sofa)
print(apartment)
basketball_court = Furniture("Full basketball court", 200)
apartment.place_furniture(basketball_court)
print(apartment)
Inheritance
Inheritance in Python refers to the relationship where a child class inherits all public attributes and methods from its parent class by default. All classes in Python inherit from the root object class by default.
# Parent class
class Base:
def __init__(self):
self.count = 10
def show_count(self):
print(self.count)
# Child class inherits from Base
class Derived(Base):
pass
instance = Derived()
instance.show_count() # Output 10
Single Inheritance
A child class inherits from exactly one parent class.
# Master class (parent)
class PancakeMaster:
def __init__(self):
self.recipe = "Traditional pancake recipe"
def make_pancake(self):
print(f"Making pancake using {self.recipe}")
# Apprentice class (child) inherits from PancakeMaster
class Apprentice(PancakeMaster):
pass
da_qiu = Apprentice()
print(da_qiu.recipe)
da_qiu.make_pancake()
Even if the child class does not define an __init__ method or instance methods, it automatically inherits all accessible members from the parent class.
Multiple Inheritance
A child class can inherit from multiple parent classes at the same time.
class PancakeMaster:
def __init__(self):
self.recipe = "Traditional pancake recipe"
def make_pancake(self):
print(f"Making pancake using {self.recipe}")
class TrainingSchool:
def __init__(self):
self.recipe = "Modern commercial pancake recipe"
def make_pancake(self):
print(f"Making pancake using {self.recipe}")
# Child class inherits from both TrainingSchool and PancakeMaster
class Apprentice(TrainingSchool, PancakeMaster):
pass
da_qiu = Apprentice()
print(da_qiu.recipe)
da_qiu.make_pancake()
Note: If multiple parent classes have attributes or methods with the same name, the child class uses the member from the first parent class listed in the inheritance order by default, following the order specified in the class's __mro__ attribute.
Child Class Override Parent Class Members
A child class can define attributes and methods with the same name as parent class members to override them.
class PancakeMaster:
def __init__(self):
self.recipe = "Traditional pancake recipe"
def make_pancake(self):
print(f"Making pancake using {self.recipe}")
class TrainingSchool:
def __init__(self):
self.recipe = "Modern commercial pancake recipe"
def make_pancake(self):
print(f"Making pancake using {self.recipe}")
class Apprentice(TrainingSchool, PancakeMaster):
def __init__(self):
self.recipe = "Original secret pancake recipe"
def make_pancake(self):
print(f"Making pancake using {self.recipe}")
da_qiu = Apprentice()
print(da_qiu.recipe)
da_qiu.make_pancake()
print(Apprentice.__mro__)
When a child class has a member with the same name as a parent class member, the child class's implementation takes priority.
Call Parent Class Members from Child Class
You can explicitly call parent class attributes and methods even if they are overridden by the child class.
class PancakeMaster:
def __init__(self):
self.recipe = "Traditional pancake recipe"
def make_pancake(self):
print(f"Making pancake using {self.recipe}")
class TrainingSchool:
def __init__(self):
self.recipe = "Modern commercial pancake recipe"
def make_pancake(self):
print(f"Making pancake using {self.recipe}")
class Apprentice(TrainingSchool, PancakeMaster):
def __init__(self):
self.recipe = "Original secret pancake recipe"
def make_pancake(self):
self.__init__()
print(f"Making pancake using {self.recipe}")
def make_traditional_pancake(self):
PancakeMaster.__init__(self)
PancakeMaster.make_pancake(self)
def make_commercial_pancake(self):
TrainingSchool.__init__(self)
TrainingSchool.make_pancake(self)
da_qiu = Apprentice()
da_qiu.make_pancake()
da_qiu.make_traditional_pancake()
da_qiu.make_commercial_pancake()
da_qiu.make_pancake()
The self parameter always points to the child class instance, even when calling parent class methods.
Multi-level Inheritance
Inheritance relationships can be passed across multiple levels of classes.
class PancakeMaster:
def __init__(self):
self.recipe = "Traditional pancake recipe"
def make_pancake(self):
print(f"Making pancake using {self.recipe}")
class TrainingSchool(PancakeMaster):
def __init__(self):
self.recipe = "Modern commercial pancake recipe"
def make_pancake(self):
print(f"Making pancake using {self.recipe}")
class Apprentice(TrainingSchool):
def __init__(self):
self.recipe = "Original secret pancake recipe"
def make_pancake(self):
self.__init__()
print(f"Making pancake using {self.recipe}")
def make_traditional_pancake(self):
PancakeMaster.__init__(self)
PancakeMaster.make_pancake(self)
def make_commercial_pancake(self):
TrainingSchool.__init__(self)
TrainingSchool.make_pancake(self)
# Grandchild class inherits from Apprentice
class JuniorApprentice(Apprentice):
pass
xiao_qiu = JuniorApprentice()
xiao_qiu.make_pancake()
xiao_qiu.make_commercial_pancake()
xiao_qiu.make_traditional_pancake()
super() Method
The super() method provides a simplified way to call parent class members, automatically following the __mro__ inheritance order.
class PancakeMaster:
def __init__(self):
self.recipe = "Traditional pancake recipe"
def make_pancake(self):
print(f"Making pancake using {self.recipe}")
class TrainingSchool(PancakeMaster):
def __init__(self):
self.recipe = "Modern commercial pancake recipe"
def make_pancake(self):
print(f"Making pancake using {self.recipe}")
super().__init__()
super().make_pancake()
class Apprentice(TrainingSchool):
def __init__(self):
self.recipe = "Original secret pancake recipe"
def make_pancake(self):
self.__init__()
print(f"Making pancake using {self.recipe}")
def make_legacy_pancake(self):
super().__init__()
super().make_pancake()
da_qiu = Apprentice()
da_qiu.make_legacy_pancake()
super() is most suitable for single inheritance scenarios, as it automatically handles parent class lookup and avoids hardcoding parent class names.
Polymorphism
Polymorphism refers to the ability to call the same method on different subclass objects and get different execution results, relying on inheritance and method overriding. Implementation steps:
- Define a parent class with a public base method
- Define child classes that override the parent class method
- Pass different subclass instances to the caller to get different results
class WorkingDog:
def perform_task(self):
print("Executing assigned task")
class PatrolDog(WorkingDog):
def perform_task(self):
print("Pursuing suspect")
class NarcoticsDog(WorkingDog):
def perform_task(self):
print("Searching for illegal drugs")
class Officer:
def assign_task(self, dog):
dog.perform_task()
patrol_dog = PatrolDog()
narcotics_dog = NarcoticsDog()
officer = Officer()
officer.assign_task(patrol_dog)
officer.assign_task(narcotics_dog)
Polymorphism enables highly flexible and reusable code that can adapt to changing requirements with minimal modification.
Attribute and Method Access Control
Private Attributes and Methods
You can set private access permissions for attributes and methods by adding two leading underscores __ to their names, preventing them from being accessed directly outside the class or inherited by child classes.
class PancakeMaster:
def __init__(self):
self.recipe = "Traditional pancake recipe"
def make_pancake(self):
print(f"Making pancake using {self.recipe}")
class TrainingSchool:
def __init__(self):
self.recipe = "Modern commercial pancake recipe"
def make_pancake(self):
print(f"Making pancake using {self.recipe}")
class Apprentice(TrainingSchool, PancakeMaster):
def __init__(self):
self.recipe = "Original secret pancake recipe"
# Private attribute, only accessible inside the class
self.__savings = 2000000
# Private method, only accessible inside the class
def __show_private_info(self):
print(f"Recipe: {self.recipe}, Savings: {self.__savings}")
def make_pancake(self):
self.__init__()
print(f"Making pancake using {self.recipe}")
class JuniorApprentice(Apprentice):
pass
da_qiu = Apprentice()
# Direct access to private members will throw an error
# print(da_qiu.__savings)
# da_qiu.__show_private_info()
xiao_qiu = JuniorApprentice()
# Child classes cannot inherit private members from parent classes
# print(xiao_qiu.__savings)
Modify Private Attribute Values
Private attributes can be modified via public getter and setter methods defined inside the class.
class Apprentice(TrainingSchool, PancakeMaster):
def __init__(self):
self.recipe = "Original secret pancake recipe"
self.__savings = 2000000
# Getter method for private attribute
def get_savings(self):
return self.__savings
# Setter method for private attribute
def set_savings(self, amount):
self.__savings = amount
def make_pancake(self):
self.__init__()
print(f"Making pancake using {self.recipe}")
da_qiu = Apprentice()
da_qiu.set_savings(3500000)
print(da_qiu.get_savings())
This approach allows controlled access to private data, ensuring data integrity and validation if needed.
Class Attributes vs Instance Attributes
Class attributes belong to the class itself, shared by all instances of the class, and only store one copy in memory. Instance attributes belong to individual object instances, with independent storage for each instance.
class Person:
# Class attribute
nation = "China"
# Private class attribute
__planet = "Earth"
p1 = Person()
# Access public class attribute via instance or class
print(p1.nation)
print(Person.nation)
# Private class attributes cannot be accessed directly outside the class
# print(p1.__planet)
# print(Person.__planet)
Instance attributes are defined inside the __init__ method:
class Person:
nation = "China"
def __init__(self):
# Instance attributes
self.name = "Xiao Wang"
self.age = 22
p1 = Person()
p1.age = 24
print(p1.nation)
print(p1.name)
print(p1.age)
print(Person.nation)
# Instance attributes cannot be accessed via the class
# print(Person.name)
# print(Person.age)
Modifying class attributes via the class affects all instances, while modifying a class attribute via an instance creates a new instance attribute that masks the class attribute for that specific instance:
class Person:
nation = "China"
print(Person.nation)
p1 = Person()
print(p1.nation)
p1.nation = "Japan"
print(p1.nation) # Outputs instance attribute value
print(Person.nation) # Class attribute remains unchanged
del p1.nation # Delete the instance attribute
print(p1.nation) # Outputs class attribute value again
Class Methods and Static Methods
Class Methods
Class methods are marked with the @classmethod decorator, take cls (the class itself) as the first parameter, and can be accessed via both the class and instance objects.
class Person:
nation = "China"
@classmethod
def get_nation(cls):
return cls.nation
@classmethod
def set_nation(cls, new_nation):
cls.nation = new_nation
p1 = Person()
print(p1.get_nation())
print(Person.get_nation())
Person.set_nation("Canada")
print(p1.get_nation())
print(Person.get_nation())
Class methods are typically used to access or modify class attributes.
Static Methods
Static methods are marked with the @staticmethod decorator, do not require any mandatory default parameters, and can be accessed via both the class and instance objects.
class Person:
nation = "China"
@staticmethod
def get_nation():
return Person.nation
p1 = Person()
print(p1.get_nation())
print(Person.get_nation())
Static methods are used for utility functions that do not need to access instance or class attributes.
Practical Example: Counter-Strike Simple Simulation
Requirements:
- Guns have different models and damage values, can hold bullets, and shoot enemies.
- Players have health points, can equip guns, shoot enemies, and take damage.
- When a player's health drops to 0, they are marked as dead.
Implementation Code:
class Firearm:
def __init__(self, model, damage_per_shot):
self.model = model
self.damage = damage_per_shot
self.ammo = 0
def __str__(self):
return f"Model: {self.model}, Damage: {self.damage}, Ammo remaining: {self.ammo}"
def reload(self, bullet_count):
self.ammo += bullet_count
def fire(self, target):
if self.ammo <= 0:
print("Out of ammo, cannot fire")
return
self.ammo -= 1
target.take_damage(self)
class GamePlayer:
def __init__(self, role, player_name):
self.role = role
self.name = player_name
self.health = 100
self.weapon = None
self.is_dead = False
def __str__(self):
return f"Role: {self.role}, Name: {self.name}, Health: {self.health}, Status: {'Dead' if self.is_dead else 'Alive'}, Weapon: {self.weapon}"
def shoot_enemy(self, target):
if not self.weapon:
print("No weapon equipped, cannot attack")
return
if self.weapon.ammo <= 0:
print("Out of ammo, reloading 5 bullets")
self.weapon.reload(5)
return
self.weapon.fire(target)
def take_damage(self, enemy_weapon):
self.health -= enemy_weapon.damage
if self.health < 0:
self.health = 0
print(f"Player {self.name} took {enemy_weapon.damage} damage, remaining health: {self.health}")
if self.health == 0:
self.is_dead = True
print(f"Player {self.name} has been eliminated")
# Main game logic
ak47 = Firearm("AK-47", 35)
policeman = GamePlayer("Police", "Xiao Ming")
terrorist = GamePlayer("Terrorist", "Cobra")
policeman.weapon = ak47
while not terrorist.is_dead:
policeman.shoot_enemy(terrorist)
Object-Oriented Practical Project: Student Management System
System Requirements
Implement a student management system using object-oriented programming, with features including add, delete, modify, query, display all students, save data to file, and load data from file. The system consists of three code files:
main.py: System entry pointstudent.py: Student entity class definitionstudent_manager.py: Core management logic class
main.py Code:
from student_manager import StudentManager
if __name__ == "__main__":
system = StudentManager()
system.run()
student.py Code:
class Student:
def __init__(self, stu_id, stu_name, stu_score):
self.id = stu_id
self.name = stu_name
self.score = stu_score
def __str__(self):
return f"{self.id},{self.name},{self.score}"
student_manager.py Code:
from student import Student
class StudentManager:
def __init__(self):
self.student_storage = {}
def run(self):
self.load_data("students.txt")
while True:
self.display_menu()
choice = int(input("Please enter your operation choice: "))
if choice == 1:
self.add_student()
elif choice == 2:
self.show_all_students()
elif choice == 3:
self.search_student()
elif choice ==4:
self.update_student()
elif choice ==5:
self.delete_student()
elif choice ==0:
self.save_data("students.txt")
print("Exiting system, goodbye!")
break
else:
print("Invalid choice, please try again!\n")
@staticmethod
def display_menu():
print("*"*35)
print("Welcome to Student Management System V1.0")
print("1. Add new student")
print("2. Show all students")
print("3. Search student by ID")
print("4. Update student information")
print("5. Delete student")
print("0. Exit system")
print("*"*35)
def add_student(self):
stu_id = input("Enter student ID: ")
if stu_id in self.student_storage:
print(f"Student with ID {stu_id} already exists!\n")
return
stu_name = input("Enter student name: ")
stu_score = input("Enter student score: ")
new_student = Student(stu_id, stu_name, stu_score)
self.student_storage[stu_id] = new_student
print(f"Student {stu_id} added successfully!\n")
def show_all_students(self):
if not self.student_storage:
print("No student records found in the system!\n")
return
print(f"{'ID':<12}{'Name':<15}{'Score':<8}")
print("-"*35)
for stu in self.student_storage.values():
print(f"{stu.id:<12}{stu.name:<15}{stu.score:<8}")
print("-"*35 + "\n")
def search_student(self):
stu_id = input("Enter student ID to search: ")
if stu_id not in self.student_storage:
print(f"Student with ID {stu_id} not found!\n")
return
stu = self.student_storage[stu_id]
print(f"{'ID':<12}{'Name':<15}{'Score':<8}")
print("-"*35)
print(f"{stu.id:<12}{stu.name:<15}{stu.score:<8}")
print("-"*35 + "\n")
def update_student(self):
stu_id = input("Enter student ID to update: ")
if stu_id not in self.student_storage:
print(f"Student with ID {stu_id} not found!\n")
return
new_name = input("Enter new name: ")
new_score = input("Enter new score: ")
stu = self.student_storage[stu_id]
stu.name = new_name
stu.score = new_score
print(f"Student {stu_id} updated successfully!\n")
def delete_student(self):
stu_id = input("Enter student ID to delete: ")
if stu_id not in self.student_storage:
print(f"Student with ID {stu_id} not found!\n")
return
del self.student_storage[stu_id]
print(f"Student {stu_id} deleted successfully!\n")
def save_data(self, file_path):
with open(file_path, "w", encoding="utf-8") as f:
for stu in self.student_storage.values():
f.write(str(stu) + "\n")
def load_data(self, file_path):
try:
with open(file_path, "r", encoding="utf-8") as f:
for line in f:
line = line.strip()
if not line:
continue
stu_id, stu_name, stu_score = line.split(",")
self.student_storage[stu_id] = Student(stu_id, stu_name, stu_score)
except FileNotFoundError:
pass