본문 바로가기
Data & Research

[Deep Learning/Python 일반] Magic Method

by 물박사의 저장공간 2025. 11. 3.

딥러닝 Data를 전처리하는 Github코드를 보다보면 자주 등장하는 클래스의 매서드들이 있습니다. __len__, __getitem__ 같은 것들인데요. 흔히 매직 메서드(Magic Methods) 또는 던더 메서드(Dunder Methods)라고 불리는 것들에 대해서 간단히 정리해보겠습니다. 

 

__len__(self) 메서드의 목적은 클래스 인스턴스가 길이를 가지는 컨테이너(Container) 객체처럼 작동하도록 만드는 것입니다.

 

__getitem__(self, key) 메서드의 목적은 클래스 인스턴스가 인덱싱(obj[key])이나 슬라이싱(obj[start:stop]) 연산을 지원하도록 하여, 객체 내부의 데이터를 컨테이너처럼 접근할 수 있게 만드는 것입니다.

 

이러한 특수 메서드들의 궁극적인 목적은 다음과 같습니다:

  1. Python 데이터 모델과의 통합: 사용자 정의 클래스를 Python의 내장 기능(예: len(), for 루프, +, -, == 연산자 등)과 자연스럽게 통합하여 사용할 수 있도록 프로토콜을 정의하는 것입니다.
  2. 연산자 오버로딩 (Operator Overloading): 특히 __add__ (덧셈), __sub__ (뺄셈), __eq__ (같음 비교) 등은 클래스의 인스턴스에 대해 사용자 정의 연산을 수행할 수 있게 해줍니다. 예를 들어, 벡터 클래스에서 + 연산자가 두 벡터의 성분별 합(Component-wise sum)을 수행하도록 정의할 수 있습니다.
  3. 파이썬틱(Pythonic) 코드 작성: 클래스를 사용하는 개발자가 복잡한 내부 메서드 호출 대신, len(), [], +와 같은 직관적인 파이썬 구문을 사용하여 객체와 상호작용할 수 있게 하여 코드의 가독성생산성을 크게 향상시킵니다.

 

아래는 딥러닝 학습용 Dataloader에서 매직매서드를 사용하는 예시입니다. 

class CustomImageDataset(Dataset):
    """
    딥러닝 학습을 위한 커스텀 이미지 데이터셋 클래스.
    Dataset 클래스를 '상속'받습니다.
    """
    
    def __init__(self, image_paths, labels, transform=None):
        """
        [목적 1: __init__]
        데이터셋 객체가 생성될 때(인스턴스화될 때) 호출됩니다.
        필요한 모든 초기 설정(파일 경로, 레이블, 전처리)을 여기서 정의합니다.
        """
        print(" -> (1) __init__ 호출됨: 데이터셋 초기화 중...")
        self.image_paths = image_paths  # 모든 이미지 파일 경로 리스트
        self.labels = labels          # 모든 레이블 리스트
        self.transform = transform      # 적용할 전처리 (Augmentation 등)
        print(" -> (1) __init__ 완료: 경로/레이블/전처리 저장 완료.")

    def __len__(self):
        """
        [목적 2: __len__]
        데이터셋의 총 샘플 개수를 반환합니다.
        DataLoader가 전체 데이터 크기를 파악하고, 1 에폭당 스텝 수를 계산할 때 사용합니다.
        """
        print("  -> (2) __len__ 호출됨 (DataLoader가 크기 파악 중)")
        return len(self.image_paths)

    def __getitem__(self, idx):
        """
        [목적 3: __getitem__]
        DataLoader로부터 특정 인덱스(idx)를 요청받았을 때 호출됩니다.
        'idx'에 해당하는 데이터를 디스크에서 '실제로 로드'하고 '전처리'하여 반환합니다.
        
        * 이것이 'Lazy Loading'의 핵심입니다.
        """
        print(f"   -> (3) __getitem__ 호출됨 (index: {idx})")
        
        # 3a. 디스크에서 데이터 로드
        img_path = self.image_paths[idx]
        image = Image.open(img_path).convert('RGB') # PIL로 이미지 로드
        
        # 3b. 해당 레이블 가져오기
        label = self.labels[idx]
        
        # 3c. (on-the-fly) 전처리 적용
        if self.transform:
            image = self.transform(image)  # 예: Tensor 변환, 정규화 등
        
        # 3d. (이미지 텐서, 레이블) 튜플 반환
        return image, label