본문 바로가기
Code/Python

Python 클래스 톺아보기 _ 1편

by hyelog 2025. 2. 28.

알고리즘을 대대적으로 개발하면서 Python 클래스를 제대로 활용해야 할 일이 많아졌다.

그런데 문득 의문이 들었다. 과연 나는 파이썬을 충분히 활용하고 있는가?

 

어언 3년째 파이썬을 써오고 있지만, 여전히 "이 기능 뭐였더라?" 하고 문서를 찾아보는 일이 많다.

특히 클래스의 특별 메서드(매직 메서드)와 고급 기능들은 막상 필요할 때 기억이 나지 않는다.

그래서 이번 기회에 클래스의 모든 것을 총정리한 나만의 위키를 만들기로 했다!

 

3월 2주차까지 총 3편으로 정리할 예정이며, 기본 개념부터 고급 기능까지 파이썬 클래스를 깊이 탐구해볼 계획이다.

 

📌 연재 계획

1편 - 기초와 매직 메서드

2편 - 속성 접근 제어와 관리 그리고 상속과 믹스인

3편 - 클래스 데코레이터와 메타클래스

 

이 글을 읽고 나면, 파이썬 클래스를 보다 더 능숙하게 활용할 수 있다라는 목표를 두고 시리즈를 시작한다.

미래의 나도 이 글을 나만의 위키처럼 활용하면서 파이썬 클래스를 요리조리 잘 다뤄보겠다ㅎㅎ

그럼, 첫 번째 이야기부터 시작해보자! 🚀

0. 클래스와 인스턴스 기초 보고 가기!

클래스는 객체의 설계도라고 할 수 있다. 기본적인 클래스 정의와 인스턴스 생성부터 살펴본다.

class Person:
    # 클래스 변수 - 모든 인스턴스가 공유
    species = "Homo Sapiens"

    # 초기화 메서드 - 인스턴스 생성 시 호출됨
    def __init__(self, name, age):
        # 인스턴스 변수 - 각 인스턴스마다 고유
        self.name = name
        self.age = age

    # 인스턴스 메서드
    def greet(self):
        return f"안녕하세요, 저는 {self.name}이고 {self.age}살입니다."

    # 클래스 메서드 - cls(클래스 자체)를 첫번째 인자로
    @classmethod
    def create_baby(cls, name):
        return cls(name, 0)

    # 정적 메서드 - 클래스나 인스턴스에 접근할 필요가 없을 때 / 아무 매개변수도 특별히 받지 않음. / 	일반 함수처럼 동작함
    @staticmethod
    def is_adult(age):
        return age >= 18
        
print(MyClass.class_method())  # 클래스 메서드 호출 가능
print(MyClass.static_method())  # 정적 메서드 호출 가능
  • __init__ 메서드는 생성자가 아니다. 인스턴스가 생성된 후 초기화하는 메서드이다.
  • self는 인스턴스 자신을 가리키는 관례적 이름이다.

1. 특별 메서드(매직 메서드)의 힘

파이썬의 특별 메서드(Double underscore 또는 Dunder 메서드)는 클래스에 특별한 행동을 부여한다.

1-1. 객체 표현 메서드

class Book:
    def __init__(self, title, author, pages):
        self.title = title
        self.author = author
        self.pages = pages

    # 공식적인 문자열 표현 (개발자용)
    def __repr__(self):
        return f"Book('{self.title}', '{self.author}', {self.pages})"

    # 비공식적인 문자열 표현 (사용자용)
    def __str__(self):
        return f"'{self.title}' by {self.author}"

__repr__은 repr(obj) 함수 호출 시, __str__은 str(obj) 또는 print(obj) 호출 시 사용된다.

1-2. 컨테이너 동작 구현

파이썬의 리스트나 딕셔너리처럼 동작하는 객체를 만들 수 있다.

  • __getitem__, __setitem__, __delitem__ 등을 구현하면 리스트나 딕셔너리처럼 동작하는 객체를 만들 수 있다.
  • 이를 활용하면 내가 원하는 자료구조나 커스텀 컨테이너를 만들 수 있다.
  • 예를 들어, 데이터베이스 결과를 리스트처럼 다룬다거나, 설정 값을 딕셔너리처럼 다루는 객체를 만들 때 유용함.
class SimpleList:
    def __init__(self, items):
        self._items = list(items)

    # 인덱싱 지원 (obj[index])
    def __getitem__(self, index):
        return self._items[index]

    # 항목 설정 (obj[index] = value)
    def __setitem__(self, index, value):
        self._items[index] = value

    # 컨테이너 길이 (len(obj))
    def __len__(self):
        return len(self._items)

    # 멤버십 테스트 (item in obj)
    def __contains__(self, item):
        return item in self._items
        
class MyDict:
		# 내부 딕셔너리
    def __init__(self):
        self.data = {}  
    
    # 키로 값 접근
    def __getitem__(self, key):
        return self.data[key] 

		# 키로 값 설정
    def __setitem__(self, key, value):
        self.data[key] = value  

		# 키 삭제
    def __delitem__(self, key):
        del self.data[key]  

		# len() 지원
    def __len__(self):
        return len(self.data)  

1-3. 컨텍스트 매니저 구현 (with 문)

  • 컨텍스트 매니저란? (with 문에서 동작하는 객체)
  • with 문을 사용할 때, 리소스(파일, 네트워크, 데이터베이스 연결 등)를 자동으로 열고 닫아주는 객체를 의미한다.

컨텍스트 매니저를 만들려면 __enter__()와 __exit__() 메서드를 구현해야한다.

class FileManager:
    def __init__(self, filename, mode):
        self.filename = filename
        self.mode = mode
        self.file = None # 파일 객체

    # with 문 진입 시 호출
    def __enter__(self):
        self.file = open(self.filename, self.mode)
        return self.file  # 파일 객체 반환 (with문의 as f 부분에 들어감)

    # with 문 종료 시 호출
    def __exit__(self, exc_type, exc_val, exc_tb):
        if self.file:
            self.file.close()
        # 예외를 처리했으면 True, 아니면 False 반환
        return False

 

동작 과정

  • with FileManager("example.txt", "w") as f: 실행 시 __enter__()가 호출되어 파일이 열림.
  • __enter__()의 반환값이 as f에 들어감.
  • with 블록을 벗어나면 __exit__()가 호출되어 파일이 자동으로 닫힘.
  • 예외가 발생하면 __exit__()가 예외를 처리할 수도 있음.

사용 예:

with FileManager('test.txt', 'w') as f:
    f.write('Hello, world!') # 자동으로 파일이 닫힘

 

2️⃣ 데이터베이스 연결 관리 예제

데이터베이스 연결을 자동으로 열고 닫는 컨텍스트 매니저도 만들 수 있음!

class DatabaseConnection:
    def __init__(self, db_name):
        self.db_name = db_name
        self.connection = None  # 가상의 DB 연결 객체

    def __enter__(self):
        self.connection = f"DB 연결: {self.db_name}"  # 가상의 DB 연결
        print(self.connection)
        return self.connection

    def __exit__(self, exc_type, exc_value, traceback):
        print("DB 연결 종료")  # 연결 종료
        return False  # 예외 발생 시 전파

# 사용 예시
with DatabaseConnection("my_database") as conn:
    print(f"{conn}에서 데이터 조회")

# 블록을 벗어나면 DB 연결이 자동으로 닫힘

1-4. 호출 가능 객체(__call__ 메서드 구현)

파이썬에서는 객체를 함수처럼 호출할 수 있도록 만들 수 있다.

즉, obj() 형태로 객체를 실행할 수 있다는 뜻이다.

class Adder:
    def __init__(self, n):
        self.n = n

    # 객체를 함수처럼 호출 가능하게 함 
    def __call__(self, x):
        return self.n + x

add5 = Adder(5) # "입력값에 5를 더하는 함수처럼" 동작.
print(add5(10))  # add_5(10) → 10 + 5 → 15

 

동작 과정

  • obj()를 실행하면 __call__()이 자동으로 호출됨.
  • 마치 함수처럼 obj() 형태로 사용 가능.

2️⃣ 함수 호출 로그 기록 예제

__call__()을 이용하면 함수를 감싸는 객체(데코레이터 역할) 를 만들 수도 있다!

class Logger:
    def __init__(self, func):
        self.func = func  # 감싸고 싶은 함수 저장

    def __call__(self, *args, **kwargs):
        print(f"실행: {self.func.__name__}({args}, {kwargs})")  # 로그 출력
        return self.func(*args, **kwargs)  # 원래 함수 실행 후 결과 반환

@Logger  # 데코레이터처럼 사용 가능
def greet(name):
    return f"안녕하세요, {name}님!"

print(greet("철수"))

 

동작 과정

  • Logger 객체가 greet 함수를 감싸서 호출 시 실행 로그를 남김.
  • greet("철수") 실행 시 Logger 객체의 __call__()이 작동하면서 로그가 찍히고, 원래 함수가 실행됨.

댓글