deep-dive2024-09-05·7 min·274/348

Python __init__.py 역할과 활용 방법

Python 패키지의 __init__.py 파일의 역할과 다양한 활용 방법을 알아봅니다.

Python init.py 역할과 활용 방법

Python에서 __init__.py는 패키지를 정의하는 데 사용되는 특별한 파일입니다. 이 파일이 없으면 Python은 디렉토리를 일반 디렉토리로 인식합니다.

Environment

$ python --version
Python 3.11.5

$ ls -la mypackage/
__init__.py
core.py
utils.py
models/

Problem: init.py 없이 패키지 구조 만들기

# __init__.py 없는 디렉토리 구조
$ mkdir -p testpackage/subpackage
$ echo "def hello(): print('Hello')" > testpackage/core.py
$ echo "x = 1" > testpackage/subpackage/data.py

$ python -c "from testpackage import core"
# ImportError: cannot import name 'core' from partially initialized module

Analysis: init.py의 역할

# __init__.py가 하는 일:

# 1. Python에게 이 디렉토리가 패키지임을 알림
# 2. 패키지 초기화 코드 실행
# 3. public 인터페이스 정의
# 4. __all__로 export 관리

# 빈 __init__.py도 패키지로 인식되게 함

Solution: init.py 활용법

기본 패키지 구조

# mypackage/__init__.py
# This file makes 'mypackage' a package
print("mypackage initialized")

# mypackage/core.py
def process(data):
    return data * 2

# Usage
$ python -c "from mypackage import core; print(core.process(5))"
mypackage initialized
10

public 인터페이스 정의

# mypackage/__init__.py
from .core import process, validate
from .utils import format_date, parse_date
from .models import User, Product

__all__ = [
    'process',
    'validate',
    'format_date',
    'parse_date',
    'User',
    'Product',
]

__version__ = '1.0.0'
__author__ = 'Joel Park'

Lazy Import 최적화

# mypackage/__init__.py
# Bad: Import everything at package load time
# from .heavy_module import HeavyClass  # Slow!

# Good: Lazy import
def __getattr__(name):
    if name == 'HeavyClass':
        from .heavy_module import HeavyClass
        return HeavyClass
    raise AttributeError(f"module {__name__!r} has no attribute {name!r}")

# Usage - HeavyClass is only imported when accessed
from mypackage import HeavyClass  # Import happens here

Package Configuration

# mypackage/__init__.py
import os
from pathlib import Path

# Package root directory
PACKAGE_ROOT = Path(__file__).parent

# Default configuration
DEFAULT_CONFIG = {
    'debug': False,
    'log_level': 'INFO',
    'data_dir': PACKAGE_ROOT / 'data',
}

class Config:
    _instance = None
    
    def __new__(cls):
        if cls._instance is None:
            cls._instance = super().__new__(cls)
            cls._instance._config = DEFAULT_CONFIG.copy()
        return cls._instance
    
    def get(self, key, default=None):
        return self._config.get(key, default)
    
    def set(self, key, value):
        self._config[key] = value

# Singleton config instance
config = Config()

Subpackage 구조

# mypackage/__init__.py
from .core import Core
from .utils import Helper

__all__ = ['Core', 'Helper']

# mypackage/core/__init__.py
from .engine import Engine
from .processor import Processor

__all__ = ['Engine', 'Processor']

# mypackage/core/engine.py
class Engine:
    pass

# Usage
from mypackage.core import Engine
from mypackage import Core, Helper

Advanced: 동적 패키지 생성

#动态 패키지 생성
import importlib
import sys

def create_package(name, modules):
    """Create a package dynamically"""
    import types
    
    package = types.ModuleType(name)
    package.__path__ = []
    
    for module_name, module_code in modules.items():
        module = types.ModuleType(f"{name}.{module_name}")
        exec(module_code, module.__dict__)
        setattr(package, module_name, module)
        sys.modules[f"{name}.{module_name}"] = module
    
    sys.modules[name] = package
    return package

# Usage
my_package = create_package('dynamic_pkg', {
    'utils': 'def helper(): return "Dynamic!"',
    'data': 'value = 42'
})

print(my_package.utils.helper())  # Dynamic!
print(my_package.data.value)      # 42

Lessons Learned

  1. 패키지 식별: __init__.py는 Python에게 해당 디렉토리가 패키지임을 알리는 역할을 합니다.

  2. Public API 관리: __init__.py에서 import와 __all__을 사용하여 패키지의 public API를 명확히 정의하세요.

  3. Lazy Import: 무거운 모듈은 필요한 시점에 임포트하여 패키지 로딩 시간을 줄일 수 있습니다.

  4. 초기화 코드: 패키지 레벨의 설정이나 초기화 코드를 __init__.py에 작성할 수 있습니다.

  5. Subpackage 구조: 복잡한 프로젝트에서는 하위 패키지를 만들고 각각의 __init__.py로 구조를 관리하세요.


This blog does not accept any external sponsorships, affiliate marketing, or ad revenue.