OOps Python
Object Oriented Programming(OOP),
- class creation:
- why, and how
- Class vs Instance
2. Functions inside Classes
3. Inheritance
4. iterators
5. polymorphism & Encapsulation
6. Scope (Global & Local variable)
Object-Oriented Programming: Breaking Down My Week 2
DataraFlow Diaries #2
Week two moved from Python fundamentals into Object-Oriented Programming (OOP) — and by the end of it, I’d gone from “what is a class” to building a full Library Management System. Here’s a breakdown of everything that got me there.
Python is an object-oriented language, which means it lets you structure code using classes and objects for better organization and reusability. A class is basically a blueprint describing how an object should be defined — what data it holds, and what it can do.
1. Class Creation
Why, and how
A class alone doesn’t do anything — it’s just a template. An object (or instance) is the actual “thing” built from that template, with real data inside it.
class Myclass:
def __init__(self):
self.x = 5
p1 = Myclass()
print(p1.x)
5__init__ is a special method that runs automatically whenever a new object is created — it's where you set up whatever data that object should start with. self refers to "this specific object," and self.x = 5 means every object made from this class gets its own x.
Class vs. Instance
This distinction tripped me up more than once early on. p1 = Myclass (no parentheses) just points at the class itself — it doesn't create anything. p1 = Myclass() (with parentheses) actually calls the class, triggering __init__, and gives you a real object.
I also learned the difference between instance variables (unique to each object, like self.x) and class variables (shared by every object made from that class) while building a Counter:
Get Ayomide Sufian’s stories in your inbox
Join Medium for free to get updates from this writer.
python
class Counter:
total_count = 0
def __init__(self):
Counter.total_count += 1
instance1 = Counter()
instance2 = Counter()
instance3 = Counter()
print(Counter.total_count)Every object bumps the same shared number — that’s the difference. An instance variable is like everyone having their own notebook; a class variable is one shared whiteboard everyone can see and update.
2. Functions Inside Classes
Functions defined inside a class are called methods, and every one of them needs self as its first parameter, even if it doesn't use it, because Python automatically passes the calling object in behind the scenes.
class Car():
def __init__(self, brand, model):
self.brand = brand
self.model = model
def display_info(self):
print(f"The brand is {self.brand}, the model is {self.model}")errors I ran into: forgetting self in a method definition which causes a TypeError.
3. Inheritance
Inheritance lets a class (a subclass) reuse everything from another class (the parent), instead of rewriting shared logic from scratch.
class Vehicle:
def __init__(self, model, brand):
self.model = model
self.brand = brand
class Car(Vehicle):
def __init__(self, model, brand, doors):
super().__init__(model, brand)
self.doors = doorssuper().__init__(...) it hands the shared data up to the parent, which already knows how to set it up. The mistake I made constantly this week: defining a subclass's own __init__ and forgetting to call super().__init__() at all, which silently means the parent's setup never runs, and attributes you'd expect to exist just don't.
I also worked through multi-level inheritance — a Vehicle → Car → ElectricCar chain — where each level needs to accept everything it either uses itself or needs to forward further up.
4. Iterators
This was genuinely new territory, building objects that can be looped over with a for loop, using two special methods: __iter__() and __next__().
class CountDown:
def __init__(self, num):
self.num = num
def __iter__(self):
return self
def __next__(self):
if self.num <= 0:
raise StopIteration
current = self.num
self.num -= 1
return current5. Polymorphism & Encapsulation
Polymorphism is the idea that different objects can respond to the same method call in their own way:
class Car:
def move(self):
print("Car drives")
class Bicycle:
def move(self):
print("Bicycle pedals")
vehicles = [Car(), Bicycle()]
for v in vehicles:
v.move()One loop, no if isinstance() checks anywhere — each object just handles .move() in its own way. I later realized len() itself works exactly this way internally — every type (str, list, dict) has its own __len__(), and len() is just a generic wrapper calling whichever one applies.
Encapsulation is about controlling access to an object’s data — using single underscores (_protected, a convention) and double underscores (__private, actively hidden via name-mangling), paired with getter/setter methods instead of exposing raw data directly.
6. Scope (Global & Local Variables)
This section got me seighing all week. The big lesson: if you assign to a variable anywhere inside a function, Python treats it as local for the entire function, even lines before the assignment.
def show_len_behavior():
list1 = [1, 2, 3]
print(len(list1))
len = 100This throws UnboundLocalError on the first line, because Python scans the whole function ahead of time and decides len is a local variable throughout, shadowing the built-in entirely.
I also worked through global (letting a function modify a variable at the top level) and nonlocal letting a nested function modify a variable in its enclosing function.
The Assignment: Building a Library Management System
Everything above came together in the week’s final task — a full Library Management System with Book, EBook, and PrintedBook classes; Member, StudentMember, and TeacherMember with different borrowing limits; custom iterators for looping through a library's books or a member's borrowed books; private/protected attributes with proper getters and setters; and extra features like tracking book availability, searching by title or author, and a shared class-level counter tracking total books borrowed across every member.
Challenges I Faced (and How I Solved Them)
The single most repeated bug of the week was forgetting super().__init__() in a subclass, over and over, across nearly every exercise — until it finally became automatic to ask "did I forward everything the parent needs?" before running any new subclass.
Scope also caught me off guard — I had to stop reading code top-to-bottom the way I naturally would, and instead think about it the way Python actually processes it: scanning the whole function first, then running it line by line.
And completely separate from code logic — I lost a chunk of completed work when my project reverted to its original downloaded state without warning. Frustrating, but a clear signal I need to save my code before shutting down.






