For AI agents: use /llms.txt for the Nakafa content index.
Python is a multi-paradigm language: procedural, functional, and object-oriented styles can coexist. In Python, runtime values such as numbers, functions, classes, and modules are objects with a type, identity, and value.
Objects may expose attributes and operations through their types. This uniform object model is why a function can be stored in a list, a class can be passed to another function, and a number can provide methods without forcing every program into an object-oriented design.
In a procedural style, a program is organized around operations and explicit data flow. Data structures and the functions that receive them can remain separate, even in a language that also supports classes.
#include <stdio.h>
// Example of procedural approach in C language
struct ParkSystem {
int max;
int occ;
};
int occupy(struct ParkSystem* ps) {
if (ps->max <= ps->occ) {
return -1;
}
ps->occ++;
return 0;
}
void leave(struct ParkSystem* ps) {
if (ps->occ > 0) {
ps->occ--;
}
}
int main() {
struct ParkSystem ps = {100, 0};
occupy(&ps);
printf("%d %d", ps.max, ps.occ);
return 0;
}Here, the C struct stores parking state and each function receives a pointer to that state. The organization is procedural; it does not imply that every procedural function must mutate its input.
In an object-oriented style, a class can group related state and behavior behind one interface. Attributes represent object state, while methods define operations that use or update it.
# Example of object-oriented approach in Python
class ParkSystem:
def __init__(self, max_capacity):
self.max = max_capacity
self.occ = 0
def occupy(self):
if self.max <= self.occ:
return -1
self.occ += 1
return 0
def leave(self):
if self.occ > 0:
self.occ -= 1
# Usage
ps = ParkSystem(100)
ps.occupy()
print(f"Max: {ps.max}, Occupied: {ps.occ}")The Python version groups parking invariants and operations in one class. That boundary can protect valid state, but only if its methods actually validate their inputs and avoid exposing mutable internals carelessly.
Let's use a parking system as an example to understand the concept of objects. In the real world, a parking system has characteristics like maximum capacity and number of occupied spaces. This system can also perform actions like accepting cars entering or leaving.
class ParkingSystem:
def __init__(self, capacity):
"""Initialize parking system with certain capacity"""
if capacity <= 0:
raise ValueError("Capacity must be positive")
self.capacity = capacity
self.vehicles = [] # List of parked vehicles
def park_vehicle(self, vehicle_plate):
"""Method to park a vehicle"""
if vehicle_plate in self.vehicles:
return f"Vehicle {vehicle_plate} is already parked"
if len(self.vehicles) >= self.capacity:
return f"Parking full! Cannot park {vehicle_plate}"
self.vehicles.append(vehicle_plate)
return f"Vehicle {vehicle_plate} successfully parked"
def exit_vehicle(self, vehicle_plate):
"""Method to exit parking"""
if vehicle_plate in self.vehicles:
self.vehicles.remove(vehicle_plate)
return f"Vehicle {vehicle_plate} exited parking"
else:
return f"Vehicle {vehicle_plate} not found"
def get_status(self):
"""Method to view parking status"""
occupied = len(self.vehicles)
available = self.capacity - occupied
return {
'capacity': self.capacity,
'occupied': occupied,
'available': available,
'vehicles': tuple(self.vehicles)
}
def is_full(self):
"""Method to check if parking is full"""
return len(self.vehicles) >= self.capacity
# Example usage
parking = ParkingSystem(5)
print(parking.park_vehicle("B 1234 CD"))
print(parking.park_vehicle("B 5678 EF"))
print(parking.get_status())
print(parking.is_full())"Everything is an object" means Python treats runtime values through one object model. Available attributes and methods still depend on each value's type.
# Numbers in Python are objects
number = 42
# Numbers have methods
print(number.bit_length()) # Method to calculate bit length
print(number.__add__(8)) # Method for addition (same as number + 8)
# Float numbers are also objects
price = 15.75
print(price.is_integer()) # Method to check if it's a whole number
print(price.as_integer_ratio()) # Method to convert to ratio
# Even mathematical operation results are objects
result = 10 + 5
print(type(result)) # <class 'int'>
print(dir(result)) # View all available methods# Strings in Python are very powerful objects
message = "Learning Python is Fun"
# Strings have many useful methods
print(message.upper()) # LEARNING PYTHON IS FUN
print(message.lower()) # learning python is fun
print(message.title()) # Learning Python Is Fun
print(message.count('n')) # Count letter 'n'
print(message.replace('Python', 'Programming')) # Replace word
# Methods for checking
email = "user@example.com"
print(email.endswith('.com')) # True
print(email.startswith('user')) # True
print(email.find('@')) # Position of '@' character
# Methods for formatting
name = "alice"
age = 25
formatted = "Name: {}, Age: {}".format(name.title(), age)
print(formatted)
# String split and join
words = message.split() # Split into word list
print(words)
rejoined = " ".join(words) # Join back together
print(rejoined)# Lists in Python are dynamic objects
fruits = ["apple", "orange", "mango"]
# Lists have methods for data manipulation
fruits.append("banana") # Add at the end
print(fruits)
fruits.insert(1, "strawberry") # Add at specific position
print(fruits)
fruits.remove("orange") # Remove specific item
print(fruits)
# Methods for searching and sorting
numbers = [3, 1, 4, 1, 5, 9, 2, 6]
print(numbers.count(1)) # Count occurrences
print(numbers.index(4)) # Find position
numbers.sort() # Sort
print(numbers)
numbers.reverse() # Reverse order
print(numbers)
# List comprehension - pythonic way
squares = [x**2 for x in range(1, 6)]
print(squares) # [1, 4, 9, 16, 25]
# Methods for advanced operations
original = [1, 2, 3]
copy_list = original.copy() # Create copy
original.extend([4, 5]) # Add multiple items
print(f"Original: {original}")
print(f"Copy: {copy_list}")# Dictionaries in Python are flexible objects
student = {
"name": "Alice",
"age": 22,
"major": "Computer Science",
"gpa": 3.8
}
# Dictionaries have methods for manipulation
print(student.get("name")) # Get value safely
print(student.get("height", "Unknown")) # Default value if key doesn't exist
# Methods to view dictionary contents
print(student.keys()) # All keys
print(student.values()) # All values
print(student.items()) # Key-value pairs
# Methods for update and manipulation
student.update({"semester": 6, "age": 23}) # Update multiple values
print(student)
# Pop method to get and remove
gpa = student.pop("gpa")
print(f"Removed GPA: {gpa}")
print(student)
# Dictionary comprehension
grades = {"Math": 85, "Physics": 92, "Chemistry": 78}
passed = {subject: grade for subject, grade in grades.items() if grade >= 80}
print(passed)
# Nested dictionary
university = {
"students": {
"CS": ["Alice", "Bob"],
"Math": ["Charlie", "Diana"]
},
"location": "Jakarta"
}
print(university["students"]["CS"])One interesting thing about Python is that functions are also objects. This allows you to treat functions like regular data.
# Functions in Python are first-class objects
def greet(name):
return f"Hello, {name}!"
def farewell(name):
return f"Goodbye, {name}!"
# Functions have attributes
print(greet.__name__) # Function name
print(type(greet)) # <class 'function'>
# Functions can be stored in variables
my_function = greet
print(my_function("Alice")) # Hello, Alice!
# Functions can be stored in lists
functions = [greet, farewell]
for func in functions:
print(func("Bob"))
# Functions can be used as parameters
def call_function(func, name):
return func(name)
result = call_function(greet, "Charlie")
print(result)
# Functions can be returned from other functions
def get_greeting_function(language):
def english_greet(name):
return f"Hello, {name}!"
def indonesian_greet(name):
return f"Halo, {name}!"
if language == "english":
return english_greet
if language == "indonesian":
return indonesian_greet
raise ValueError(f"Unsupported language: {language}")
# Using returned function
greet_func = get_greeting_function("indonesian")
print(greet_func("Diana")) # Halo, Diana!
# Lambda functions are also objects
square = lambda x: x ** 2
print(type(square)) # <class 'function'>
print(square(5)) # 25A class defines how its instances are constructed and how their shared behavior is resolved. The class itself is also an object, and each instance carries its own instance state.
# Creating class as blueprint
class Student:
# Class attribute (shared by all instances)
university = "University of Indonesia"
def __init__(self, name, major, semester):
# Instance attributes (unique for each instance)
self.name = name
self.major = major
self.semester = semester
self.courses = []
def add_course(self, course):
"""Method to add a course"""
self.courses.append(course)
return f"{self.name} is taking course {course}"
def get_info(self):
"""Method to get student information"""
return {
'name': self.name,
'major': self.major,
'semester': self.semester,
'courses': self.courses,
'university': self.university
}
def __str__(self):
"""Special method for string representation"""
return f"Student({self.name}, {self.major})"
def __len__(self):
"""Special method to get number of courses"""
return len(self.courses)
# Creating instances (objects) from class
student1 = Student("Alice", "Computer Science", 4)
student2 = Student("Bob", "Mathematics", 6)
# Each instance is an object with attributes and methods
print(student1.add_course("Python Programming"))
print(student1.add_course("Data Structures"))
print(student2.add_course("Calculus"))
print(student2.add_course("Linear Algebra"))
# Using methods
print(student1.get_info())
print(student2.get_info())
# Special methods (__str__ and __len__)
print(student1) # Student(Alice, Computer Science)
print(len(student1)) # 2 (number of courses)
# Instances have accessible attributes
print(f"Name: {student1.name}")
print(f"Major: {student1.major}")
print(f"University: {student1.university}")
# Class is also an object
print(type(Student)) # <class 'type'>
print(Student.__name__) # Student
print(Student.university) # University of IndonesiaSpecial methods begin and end with double underscores. Python invokes them through syntax and built-ins such as +, abs(), repr(), and truth testing. Implement one only when its meaning is natural for the domain.
from math import hypot
class Vector2D:
def __init__(self, x, y):
self.x = x
self.y = y
def __str__(self):
"""Readable representation for people."""
return f"({self.x}, {self.y})"
def __repr__(self):
"""Unambiguous representation for debugging."""
return f"Vector2D(x={self.x!r}, y={self.y!r})"
def __abs__(self):
"""Return the vector magnitude."""
return hypot(self.x, self.y)
def __eq__(self, other):
"""Compare coordinates when the other value is a Vector2D."""
if not isinstance(other, Vector2D):
return NotImplemented
return self.x == other.x and self.y == other.y
def __add__(self, other):
"""Return a new vector containing the coordinate-wise sum."""
if not isinstance(other, Vector2D):
return NotImplemented
return Vector2D(self.x + other.x, self.y + other.y)
def __bool__(self):
"""The zero vector is false; every other vector is true."""
return bool(self.x or self.y)
first = Vector2D(3, 4)
second = Vector2D(1, -2)
print(first) # (3, 4) via __str__
print(repr(first)) # Vector2D(x=3, y=4)
print(abs(first)) # 5.0 via __abs__
print(first == Vector2D(3, 4)) # True via __eq__
combined = first + second # __add__ returns a new vector
print(combined) # (4, 2)
print(first) # (3, 4), unchanged
zero = Vector2D(0, 0)
print(bool(first)) # True
print(bool(zero)) # FalseLet's create a more complex example to understand how OOP is applied in real scenarios.
class Book:
def __init__(self, title, author, isbn):
self.title = title
self.author = author
self.isbn = isbn
self.is_borrowed = False
self.borrower = None
def __str__(self):
status = "Borrowed" if self.is_borrowed else "Available"
return f"{self.title} by {self.author} - {status}"
class Member:
def __init__(self, name, member_id):
self.name = name
self.member_id = member_id
self.borrowed_books = []
def __str__(self):
return f"Member: {self.name} (ID: {self.member_id})"
class Library:
def __init__(self, name):
self.name = name
self.books = []
self.members = []
def add_book(self, book):
"""Add book to library"""
self.books.append(book)
return f"Book '{book.title}' successfully added"
def register_member(self, member):
"""Register new member"""
self.members.append(member)
return f"Member {member.name} successfully registered"
def borrow_book(self, member_id, isbn):
"""Borrow book"""
# Find member
member = None
for m in self.members:
if m.member_id == member_id:
member = m
break
if not member:
return "Member not found"
# Find book
book = None
for b in self.books:
if b.isbn == isbn and not b.is_borrowed:
book = b
break
if not book:
return "Book not available"
# Process borrowing
book.is_borrowed = True
book.borrower = member
member.borrowed_books.append(book)
return f"{member.name} successfully borrowed '{book.title}'"
def return_book(self, member_id, isbn):
"""Return book"""
member = None
for m in self.members:
if m.member_id == member_id:
member = m
break
if not member:
return "Member not found"
book = None
for b in member.borrowed_books:
if b.isbn == isbn:
book = b
break
if not book:
return "Book not found in borrowed list"
# Process return
book.is_borrowed = False
book.borrower = None
member.borrowed_books.remove(book)
return f"{member.name} successfully returned '{book.title}'"
def get_available_books(self):
"""Get list of available books"""
available = [book for book in self.books if not book.is_borrowed]
return available
def get_member_books(self, member_id):
"""Get list of books borrowed by member"""
for member in self.members:
if member.member_id == member_id:
return member.borrowed_books
return []
# Example usage of library system
library = Library("Central Library")
# Adding books
book1 = Book("Python Programming", "John Smith", "978-1234567890")
book2 = Book("Data Science Basics", "Jane Doe", "978-0987654321")
book3 = Book("Machine Learning", "Bob Johnson", "978-1122334455")
print(library.add_book(book1))
print(library.add_book(book2))
print(library.add_book(book3))
# Registering members
member1 = Member("Alice Cooper", "M001")
member2 = Member("Bob Wilson", "M002")
print(library.register_member(member1))
print(library.register_member(member2))
# Borrowing books
print(library.borrow_book("M001", "978-1234567890"))
print(library.borrow_book("M002", "978-0987654321"))
# Viewing book status
print("\nList of all books:")
for book in library.books:
print(book)
print("\nAvailable books:")
for book in library.get_available_books():
print(book)
print(f"\nBooks borrowed by Alice: {len(library.get_member_books('M001'))} books")
# Returning books
print(library.return_book("M001", "978-1234567890"))Python's object model explains why values, functions, and classes can move through the same language mechanisms. Use object-oriented composition when state and behavior form a useful boundary, but keep a procedural or functional design when it communicates the problem more directly.