본문으로 건너뛰기 Python Classes | Object-Oriented Programming (OOP) Explained

Python Classes | Object-Oriented Programming (OOP) Explained

Python Classes | Object-Oriented Programming (OOP) Explained

이 글의 핵심

Learn Python OOP: classes, __init__, self, inheritance, encapsulation, property, magic methods, abstract classes, and polymorphism—with examples.

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)

MethodPurpose
__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

  1. Forgetting self in __init__ / methods.
  2. Mutable class attributes shared across instances—use instance attributes in __init__.
  3. Skipping super().__init__(...) when the parent must initialize.
  4. Assigning to a @property with no setter.

Summary

  1. Class = blueprint; instance = concrete object.
  2. init initializes; self is the instance.
  3. Inheritance + super(); understand MRO for multiple inheritance.
  4. Encapsulation with naming, __, and @property.
  5. Dunder methods customize operators and built-ins.
  6. @classmethod / @staticmethod for factories and utilities.
  7. ABC + @abstractmethod for contracts.

Next steps



Frequently Asked Questions (FAQ)

Q. When would I use this in practice?

A. Learn Python OOP: classes, init, self, inheritance, encapsulation, property, magic methods, abstract classes, and po…

Q. What should I read before this?

A. Follow the previous article or related articles links at the bottom of each post to learn in sequence. See the Python series index for the full picture.

Q. Where can I study this more deeply?

A. Check cppreference and the relevant library’s official documentation. The reference links at the end of the article are also worth using.


Other articles related to this topic.


Keywords Covered in This Article (Related Search Terms)

This article covers Python, Class, OOP, Inheritance, Encapsulation, Polymorphism.