- Python은 모듈 수준에서 임포트를 모두 선언하는 것이 일반적인 관례임
- 하지만 프로그램 실행 시 불필요한 의존성 모듈까지 즉시 로드되어 시작 속도와 메모리 사용량 문제가 발생함
- 기존에는 함수 내 임포트 등으로 수동 지연 임포트를 많이 사용했으나, 유지보수와 의존성 관리가 어렵다는 단점이 존재함
- 이번 PEP 810은 local, explicit, controlled, granular한 새로운 lazy 키워드로 명시적 지연 임포트 문법을 도입함
- 이 기능 도입으로 실제 필요 시점에만 모듈을 로드하며, 시작 지연·메모리 낭비 개선 및 코드 구조 투명성을 동시에 실현함
Python 임포트의 현 상황과 문제점
- Python에서는 대체로 모듈 맨 위에 import문을 작성하는 관행이 널리 통용됨
- 이 방식은 중복을 줄이고, 임포트 의존성 구조를 한 눈에 파악하며, 한 번만 임포트하여 런타임 오버헤드를 최소화함
- 그러나, 프로그램 실행 시 첫 번째 모듈(main)이 로드되면 실제 사용하지 않는 많은 의존성 모듈까지 즉시 읽혀지는 연쇄 임포트가 일어나기 쉬움
- 특히 CLI 툴에서 전체 헬프만 호출해도 수십 개 모듈이 선 로드되는 등, 모든 서브커맨드마다 불필요한 오버헤드가 발생함
기존 대안과 문제점
- 임포트를 함수 내부로 옮기는 등 수동으로 임포트 시점을 늦추는 방식이 자주 사용됨
- 하지만 이 방식은 일관성·유지보수성 저하, 전체 의존성 파악 난이도 증가 등 단점이 큼
- 표준 라이브러리 분석 결과, 성능 민감 코드에서 이미 전체 임포트의 약 17%가 함수 또는 메서드 내부에서 임포트 지연 목적으로 사용되고 있음
- 임포트 지연 관련 도구로는 importlib.util.LazyLoader, 서드파티 lazy_loader 패키지 등이 있으나, 모든 케이스를 충족하지 못하거나 단일 표준이 부재함
PEP 810: 명시적 지연 임포트 도입
-
새로운 lazy 소프트 키워드를 도입 (특정 문맥에서만 의미를 갖고, 변수명 등으로도 쓸 수 있음)
-
lazy는 import문 앞에만 사용하며, 함수/클래스/with/try 등 영역이나 star import에는 쓸 수 없음
-
각 임포트문 단위로 명확히 구분해 사용 시점까지 모듈 로드를 지연시킴
lazy import 모듈명
lazy from 모듈명 import 이름
명시적 지연 임포트의 구현 방식 및 syntactic rule
글로벌 플래그와 필터를 통한 동작 제어
런타임 동작 및 에러 처리
-
lazy import 사용 시 임포트 구문이 아닌 이름의 첫 접근 시점에 실제 임포트가 발생함
-
임포트에 실패할 경우, 예외 체인(traceback chaining) 으로 정의 위치와 발생 위치 모두를 명확하게 보여줌
lazy from json import dumsp # 오타
result = dumsp({"key": "value"}) # 실제 접근 시점에서 ImportError 발생
메모리 및 성능 이점
- 지연된 모듈은 sys.lazy_modules 집합에만 표시되고, 실제 사용 전 sys.modules에 등록되지 않음
- 사용 후에는 정상 모듈 객체로 대체되고, 추가적인 성능 페널티 없이 사용 가능
- 실제 워크로드 환경에서는 시작 지연 50~70% 감소, 메모리 30~40% 절감 효과가 나타남
동작 방식 요약
- lazy object의 처음 접근 시 reification(실제 임포트 및 대체)이 발생함
- 외부 코드에서 모듈의 __dict__ 접근 시에는 모든 lazy object가 강제 로드됨 (reification)
-
globals()로 딕셔너리 추출 시에는 lazy proxy가 유지되어 직접 접근 필요
타입 어노테이션 및 TYPE_CHECKING 최적화
-
lazy from 모듈 import 이름으로 타입만 사용하는 임포트에 런타임 비용 ZERO 보장
- 기존의 from typing import TYPE_CHECKING 조건문을 대체해 코드가 더 간결·명확해짐
기존 PEP 690과의 차이 및 구현상의 특징
- PEP 810은 명시적, 개별 임포트 단위, 간단한 프록시 객체 기반 opt-in 구조
- 반면 PEP 690은 global, 암시적 lazy import 구조였음
주의사항 및 모듈간 상호작용
- star import (*)는 lazy로 지원하지 않음 (항상 eager)
- 커스텀 import hook, loader는 reification 타이밍에 그대로 작동
- 멀티스레드 환경에서도 thread-safe하게 한 번만 임포트 및 안전한 바인딩 보장
- 동일 모듈의 lazy·eager 동시 사용 시 eager쪽이 항상 우선됨
코드 적용 및 마이그레이션 가이드
- 기존 코드에서 적용 시 프로파일링으로 필요한 임포트만 lazy로 변환, 점진적 적용 권장
-
__lazy_modules__ 활용시 Python 3.15 미만 버전에서도 호환
기타 주요 질문과 답변 포인트
-
임포트 타임 부작용 (예: 등록 패턴 등)은 첫 접근까지 지연됨. side effect가 필수라면 명시적 초기화 함수 패턴 추천
-
circular import(순환 임포트) 문제는 lazy import로 완전 해결 불가 (access가 늦춰져야만 완화 가능)
-
핫패스 성능은 first use 이후 lazy 체크가 완전히 사라져 자동 최적화됨 (바이트코드 adaptive specialization)
-
sys.modules 에는 reification(첫 사용) 후에만 실제 모듈이 등록됨
-
importlib.util.LazyLoader와 달리 별도 설정 불필요, 성능 유지, standard syntax 명확성
결론
- PEP 810은 Python import문에 lazy 키워드를 추가하여, 서브커맨드 CLI, 대형 애플리케이션, 타입 어노테이션 등 다양한 영역에서 불필요 모듈 로딩으로 인한 성능 문제를 간결하고 예측 가능하게 최적화할 수 있게 해줌
- 새 키워드는 도입 시점과 대상을 세밀하게 지정할 수 있어 실서비스에서 점진적 도입 및 성능 튜닝에 적합함
- Python import 체계의 실질적 진화로, 가시성, 유지보수성, 성능 세 가지 요구를 동시에 충족함