Python Classes | Object-Oriented Programming (OOP) Explained
이 글의 핵심
Hands-on guide to Python classes: constructors, self, class vs instance attributes, inheritance, properties, and dunder methods.
Introduction: What is OOP?
Object-oriented programming models programs as objects that combine state and behavior.
Procedural vs OOP (sketch):
# Procedural
def create_account(owner, balance):
return {"owner": owner, "balance": balance}
def deposit(account, amount):
account["balance"] += amount
# OOP
class BankAccount:
def __init__(self, owner, balance):
self.owner = owner
self.balance = balance
def deposit(self, amount):
self.balance += amount
def withdraw(self, amount):
if amount > self.balance:
return False
self.balance -= amount
return True
Benefits: encapsulation, reuse via inheritance, clearer boundaries for change.
1. Classes and instances
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
def greet(self):
return f"Hi, I'm {self.name}."
def is_adult(self):
return self.age >= 18
p = Person("Alice", 25)
print(p.greet())
Understanding self
class Counter:
def __init__(self):
self.count = 0
def increment(self):
self.count += 1
return self.count
c1 = Counter()
c2 = Counter()
Class vs instance attributes
class Student:
school = "Example High"
def __init__(self, name):
self.name = name
2. Constructors and __del__
class BankAccount:
def __init__(self, owner, balance=0):
self.owner = owner
self.balance = balance
def deposit(self, amount):
self.balance += amount
return self.balance
__del__ exists but rely on context managers (with) for cleanup instead of destructor semantics.
3. Inheritance and super()
class Animal:
def __init__(self, name):
self.name = name
def speak(self):
return "sound"
class Dog(Animal):
def speak(self):
return f"{self.name}: woof!"
class Cat(Animal):
def speak(self):
return f"{self.name}: meow!"
print(isinstance(Dog("x"), Animal))
class Employee:
def __init__(self, name, salary):
self.name = name
self.salary = salary
class Manager(Employee):
def __init__(self, name, salary, team_size):
super().__init__(name, salary)
self.team_size = team_size
Multiple inheritance and MRO
class Flyer:
def fly(self):
return "flying"
class Swimmer:
def swim(self):
return "swimming"
class Duck(Flyer, Swimmer):
pass
print(Duck.__mro__)
4. Encapsulation and @property
Convention: _protected, __private (name mangling to _ClassName__attr).
class BankAccount:
def __init__(self, owner, balance):
self.owner = owner
self.__balance = balance
def get_balance(self):
return self.__balance
def deposit(self, amount):
if amount > 0:
self.__balance += amount
return True
return False
class Circle:
def __init__(self, radius):
self._radius = radius
@property
def radius(self):
return self._radius
@radius.setter
def radius(self, value):
if value < 0:
raise ValueError("radius must be non-negative")
self._radius = value
@property
def area(self):
return 3.14159 * self._radius ** 2
5. Magic methods (dunder)
| Method | Purpose |
|---|---|
__init__ | Initialize instance |
__str__ / __repr__ | String forms |
__len__, __getitem__ | Container protocol |
__add__, __eq__ | Operators / equality |
class Vector:
def __init__(self, x, y):
self.x = x
self.y = y
def __add__(self, other):
return Vector(self.x + other.x, self.y + other.y)
def __repr__(self):
return f"Vector({self.x}, {self.y})"
6. @classmethod and @staticmethod
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
@classmethod
def from_birth_year(cls, name, birth_year):
from datetime import date
age = date.today().year - birth_year
return cls(name, age)
class MathUtils:
@staticmethod
def is_even(n):
return n % 2 == 0
7. Abstract base classes
from abc import ABC, abstractmethod
class Shape(ABC):
@abstractmethod
def area(self):
pass
class Rectangle(Shape):
def __init__(self, w, h):
self.w, self.h = w, h
def area(self):
return self.w * self.h
8. Polymorphism
Same interface, different behavior—often via overridden methods or shared ABCs.
9. Practical example: library system
from datetime import datetime, timedelta
class Book:
def __init__(self, isbn, title, author, available=True):
self.isbn = isbn
self.title = title
self.author = author
self.available = available
def __str__(self):
status = "available" if self.available else "on loan"
return f"[{self.isbn}] {self.title} — {self.author} ({status})"
def __eq__(self, other):
return self.isbn == other.isbn
class Member:
def __init__(self, member_id, name):
self.member_id = member_id
self.name = name
self.borrowed_books = []
def __str__(self):
return f"Member {self.name} ({self.member_id})"
class Library:
def __init__(self):
self.books = {}
self.members = {}
self.loan_records = []
def add_book(self, book):
self.books[book.isbn] = book
print(f"Added book: {book.title}")
def register_member(self, member):
self.members[member.member_id] = member
print(f"Registered member: {member.name}")
def borrow_book(self, member_id, isbn):
if member_id not in self.members:
return "Unknown member"
if isbn not in self.books:
return "Unknown book"
book = self.books[isbn]
member = self.members[member_id]
if not book.available:
return f"'{book.title}' is already on loan"
book.available = False
member.borrowed_books.append(isbn)
due = datetime.now() + timedelta(days=14)
self.loan_records.append({
"member": member_id,
"book": isbn,
"borrow_date": datetime.now(),
"due_date": due,
})
return (
f"{member.name} borrowed '{book.title}' "
f"(due {due.strftime('%Y-%m-%d')})"
)
def return_book(self, member_id, isbn):
if isbn not in self.books:
return "Unknown book"
book = self.books[isbn]
member = self.members[member_id]
if isbn not in member.borrowed_books:
return "This book was not borrowed by this member"
book.available = True
member.borrowed_books.remove(isbn)
return f"{member.name} returned '{book.title}'"
def list_available_books(self):
return [b for b in self.books.values() if b.available]
# Example usage
library = Library()
library.add_book(Book("978-1", "Python Basics", "A. Author"))
library.register_member(Member("M001", "Dana"))
print(library.borrow_book("M001", "978-1"))
print(library.return_book("M001", "978-1"))
OOP principles (short)
- Encapsulation: hide internals; expose methods/properties.
- Inheritance: extend behavior; use
super()intentionally. - Polymorphism: program to interfaces (duck typing or ABCs).
- Abstraction: hide complexity behind simple APIs.
Common mistakes
- Forgetting
selfin__init__/ methods. - Mutable class attributes shared across instances—use instance attributes in
__init__. - Skipping
super().__init__(...)when the parent must initialize. - Assigning to a
@propertywith no setter.
Summary
- Class = blueprint; instance = concrete object.
__init__initializes;selfis the instance.- Inheritance +
super(); understand MRO for multiple inheritance. - Encapsulation with naming,
__, and@property. - Dunder methods customize operators and built-ins.
@classmethod/@staticmethodfor factories and utilities.- ABC +
@abstractmethodfor contracts.
Next steps
- Python modules and packages
- Python exception handling
- Python decorators