-
Notifications
You must be signed in to change notification settings - Fork 0
KR_Package
somaz edited this page Apr 21, 2025
·
5 revisions
패키지는 모듈들을 모아놓은 디렉터리로, 관련된 모듈들을 체계적으로 관리할 수 있게 해준다.
mypackage/
__init__.py
module1.py
subpackage/
__init__.py
module2.py✅ 패키지의 주요 특징:
- 코드의 계층적 구조화: 관련 모듈을 논리적 그룹으로 조직
- 네임스페이스 관리: 모듈 이름 충돌 방지
- 코드 재사용성 향상: 기능별 분리와 모듈화
- 배포 및 설치 용이: pip 등 패키지 관리자를 통한 배포 가능
- 독립적인 개발 및 테스트: 패키지 단위로 개발 및 테스트 수행
# 전체 모듈 임포트
import mypackage.module1
# 특정 함수만 임포트
from mypackage.module1 import function1
# 서브패키지의 모듈 임포트
from mypackage.subpackage import module2✅ 효과적인 패키지 구조 설계:
- 기능별 구분: 관련 기능은 같은 모듈이나 서브패키지로 그룹화
- 적절한 깊이: 너무 깊은 계층은 피하고 2-3단계 이내로 유지
- 일관된 명명 규칙: 모듈명은 소문자와 언더스코어 사용
- 의존성 관리: 순환 참조를 피하고 명확한 의존성 계층 설계
- 공개 API 정의:
__init__.py에서 공개할 항목 명시적 정의
# mypackage/__init__.py
# 버전 정보
VERSION = '1.0.0'
# 공개할 모듈 지정
__all__ = ['module1', 'module2']
# 패키지 초기화 코드
print("Initializing package...")
# 하위 모듈 자동 임포트
from .module1 import function1✅ init.py 활용 방법:
- 패키지 식별: 디렉터리를 패키지로 인식하게 함 (Python 3.3+ 에서는 선택사항)
- 패키지 초기화: 패키지 임포트 시 실행할 초기화 코드 포함
- 공개 API 정의:
__all__변수로from package import *시 임포트될 이름 지정 - 서브모듈 자동 임포트: 편의를 위해 중요 서브모듈 내용 자동 임포트
- 버전 관리: 패키지 버전 정보 제공
- 문서화: 패키지 사용법에 대한 docstring 포함
# 현재 디렉터리
from . import module1
# 부모 디렉터리
from .. import other_module
# 부모의 다른 패키지
from ..other_package import some_module✅ 상대 경로 임포트 사용 시 주의사항:
- 메인 스크립트에서는 상대 경로 임포트 사용 불가 (패키지 내 모듈에서만 사용)
- 명시적인 상대 경로 사용 (암묵적 상대 경로는 Python 3에서 제거됨)
- 과도한 상대 경로 임포트(
...등) 지양: 코드 이해도 저하 - 상대 경로 임포트는 패키지 내 모듈 재구성 시 유리
-
from __future__ import absolute_import를 사용하여 명시적 임포트 강제 가능
# 여러 위치에 분산된 패키지
path1/mypackage/module1.py
path2/mypackage/module2.py
# PYTHONPATH에 path1과 path2 추가
import mypackage.module1
import mypackage.module2✅ 네임스페이스 패키지의 특징과 장점:
-
__init__.py불필요: Python 3.3+ 부터는 파일이 없어도 패키지 인식 - 분산 배포 가능: 여러 위치에 같은 패키지명으로 코드 배포 가능
- 동적 확장: 기존 패키지에 새 기능 추가 시 유리
- 협업 개발: 여러 팀이 독립적으로 같은 패키지 확장 가능
- 플러그인 구조: 확장 기능을 별도 배포하여 플러그인처럼 사용
# PEP 420 네임스페이스 패키지 상세 예제
# 디렉터리 구조:
# /path1/spam/module1.py
# /path2/spam/module2.py
# PYTHONPATH=/path1:/path2 python
import spam.module1 # /path1/spam/module1.py
import spam.module2 # /path2/spam/module2.py
# 패키지 경로 확인
import spam
print(spam.__path__) # ['/path1/spam', '/path2/spam'] 같은 형태로 출력# setup.py
from setuptools import setup, find_packages
setup(
name="mypackage",
version="1.0.0",
packages=find_packages(),
install_requires=[
'requests>=2.22.0',
],
)✅ 패키지 배포 프로세스:
-
setup.py작성: 패키지 메타데이터 및 의존성 정의 -
README.md: 사용법 및 기능 설명 문서 작성 -
LICENSE: 라이선스 파일 추가 - 테스트 실행: 배포 전 모든 테스트 통과 확인
- 패키지 빌드:
python setup.py sdist bdist_wheel - PyPI 업로드:
twine upload dist/* - 설치 확인:
pip install mypackage
현대적인 파이썬 패키지는 보다 체계적인 구조를 갖는다.
myproject/
│
├── src/
│ └── mypackage/
│ ├── __init__.py
│ ├── module1.py
│ ├── module2.py
│ └── subpackage/
│ ├── __init__.py
│ └── module3.py
│
├── tests/
│ ├── __init__.py
│ ├── test_module1.py
│ └── test_module2.py
│
├── docs/
│ ├── conf.py
│ ├── index.rst
│ └── api.rst
│
├── setup.py
├── pyproject.toml
├── setup.cfg
├── README.md
├── CHANGELOG.md
├── LICENSE
└── .gitignore
✅ 현대적 패키지 구조의 특징:
-
src/디렉터리: 실제 패키지 코드를 별도 디렉터리에 배치 - 명확한 테스트 분리:
tests/디렉터리에 테스트 코드 배치 - 문서화:
docs/디렉터리에 문서 파일 (Sphinx 등 사용) - 빌드 시스템:
pyproject.toml로 빌드 요구사항 정의 (PEP 517/518) - 구성 파일 분리:
setup.cfg로 설정 정보 관리 - CI/CD 통합:
.github/workflows/등 CI 구성 파일 포함 - 개발 환경:
requirements-dev.txt또는tox.ini등 개발 의존성 관리
파이썬 패키지를 관리하는 다양한 도구들이 있다.
# pip을 사용한 패키지 설치
pip install package-name
# 가상 환경 생성 및 활성화 (venv)
python -m venv myenv
source myenv/bin/activate # Linux/Mac
myenv\Scripts\activate # Windows
# Poetry를 사용한 의존성 관리
poetry init
poetry add requests
poetry install
# Conda를 사용한 환경 및 패키지 관리
conda create -n myenv python=3.9
conda activate myenv
conda install numpy pandas✅ 주요 패키지 관리 도구:
- pip: 기본 패키지 설치 도구, PyPI 저장소 사용
- venv/virtualenv: 격리된 파이썬 환경 생성, 프로젝트별 의존성 관리
- Poetry: 의존성 관리 및 패키지 빌드, 배포를 위한 현대적 도구
- Pipenv: pip + virtualenv 결합, Pipfile로 의존성 관리
- Conda: 과학 계산 중심 패키지 관리자, 비-파이썬 의존성도 관리
- PDM: PEP 582 기반 현대적 패키지 관리자
패키지에는 종종 Python 코드 외의 데이터 파일(템플릿, 설정 파일 등)이 포함된다.
# 패키지 데이터 파일 포함 (setup.py)
setup(
# ... 기타 설정 ...
package_data={
'mypackage': ['data/*.json', 'templates/*.html'],
},
# 또는
include_package_data=True, # MANIFEST.in 사용 시
)
# MANIFEST.in
include mypackage/data/*.json
include mypackage/templates/*.html
# 데이터 파일 접근 (코드에서)
import pkg_resources
data_path = pkg_resources.resource_filename('mypackage', 'data/config.json')
# Python 3.7+ 에서는 importlib.resources 사용 가능
from importlib import resources
with resources.path('mypackage.data', 'config.json') as path:
config_path = path✅ 데이터 파일 관리 방법:
-
package_data: setup.py에서 패키지에 포함될 데이터 파일 지정 -
MANIFEST.in: 소스 배포에 포함될 파일 패턴 정의 -
pkg_resources: 패키지 내 리소스 접근을 위한 유틸리티 (약간의 오버헤드) -
importlib.resources: Python 3.7+ 에서 제공하는 현대적 리소스 접근 방법 - 상대 경로 사용 지양:
__file__기준 상대 경로는 배포 형태에 따라 문제 발생 가능
# mypackage/__init__.py
def get_module1():
import mypackage.module1
return mypackage.module1
# 사용 시점에 임포트
def function_that_uses_module1():
module1 = get_module1()
return module1.some_function()# mypackage/plugins/__init__.py
import importlib
import pkgutil
def load_plugins():
"""모든 플러그인 모듈을 동적으로 로드"""
plugins = {}
# 현재 패키지(plugins)의 경로
package_path = __path__
prefix = __name__ + "."
# 모든 하위 모듈 탐색
for _, name, ispkg in pkgutil.iter_modules(package_path, prefix):
if not ispkg: # 패키지가 아닌 모듈만 처리
module = importlib.import_module(name)
if hasattr(module, 'register'):
plugin_name = name.split('.')[-1]
plugins[plugin_name] = module
return plugins
# 사용 예:
plugins = load_plugins()
for name, plugin in plugins.items():
plugin.register()# mypackage/__init__.py
# 공개 API만 노출
__all__ = ['public_function', 'PublicClass']
# 내부 모듈에서 필요한 것만 가져오기
from .module1 import public_function, PublicClass
from .module2 import _internal_function # 언더스코어로 내부용 표시
# 서브패키지는 명시적으로 임포트해야 함
# 예: import mypackage.subpackage# 최적화된 구현이 있으면 사용, 없으면 순수 파이썬 구현 사용
try:
from mypackage._fast_impl import fast_function
except ImportError:
from mypackage._pure_impl import fast_function
# 버전별 호환성
import sys
if sys.version_info >= (3, 8):
from importlib.metadata import version
else:
from importlib_metadata import version✅ 고급 패키지 기법 활용:
- 지연 임포트: 시작 시간 단축 및 불필요한 의존성 로드 방지
- 플러그인 시스템: 확장 가능한 아키텍처 구현
- 네임스페이스 제어: 깔끔한 API 제공과 내부 구현 세부사항 숨기기
- 조건부 임포트: 환경에 따른 최적 구현 선택
- 버전 호환성: 다양한 파이썬 버전에서 작동하는 코드 작성
# tests/test_module1.py
import unittest
from mypackage import module1
class TestModule1(unittest.TestCase):
def test_function1(self):
result = module1.function1(10)
self.assertEqual(result, 20)
# pytest 사용 예
# tests/test_module2.py
import pytest
from mypackage import module2
def test_function2():
assert module2.function2(5) == 10
# 테스트 실행
# python -m unittest discover
# pytest# setup.cfg
[flake8]
max-line-length = 88
exclude = .git,__pycache__,build,dist
[mypy]
python_version = 3.8
warn_return_any = True
warn_unused_configs = True
[coverage:run]
source = mypackage
omit = tests/*# pyproject.toml
[tool.black]
line-length = 88
target-version = ['py38']
[tool.isort]
profile = "black"
line_length = 88✅ 테스트 및 품질 관리 방법:
- 단위 테스트:
unittest,pytest등을 활용한 모듈별 테스트 - 통합 테스트: 여러 모듈 간 상호작용 테스트
- 코드 커버리지:
coverage로 테스트 범위 확인 - 정적 분석:
mypy,pyright등으로 타입 검사 - 코드 스타일:
black,flake8,isort등으로 일관된 스타일 유지 - 지속적 통합: GitHub Actions, Travis CI 등으로 자동 테스트 실행
# mypackage/module1.py
def function1(param1, param2=None):
"""
중요한 기능을 수행하는 함수
Args:
param1 (int): 첫 번째 매개변수
param2 (str, optional): 두 번째 매개변수. 기본값은 None
Returns:
bool: 성공 여부를 반환
Raises:
ValueError: param1이 0보다 작을 경우
Examples:
>>> function1(10)
True
>>> function1(-5)
Traceback (most recent call last):
...
ValueError: param1은 양수여야 합니다
"""
if param1 < 0:
raise ValueError("param1은 양수여야 합니다")
# 함수 구현...
return True# docs/conf.py
extensions = [
'sphinx.ext.autodoc',
'sphinx.ext.viewcode',
'sphinx.ext.napoleon',
]
# docs/index.rst
.. MyPackage documentation master file
Welcome to MyPackage's documentation!
=====================================
.. toctree::
:maxdepth: 2
:caption: Contents:
api
examples
# docs/api.rst
API Reference
============
.. automodule:: mypackage.module1
:members:
:undoc-members:
:show-inheritance:✅ 효과적인 문서화 방법:
- 일관된 docstring 형식: Google, NumPy, reStructuredText 중 선택
- API 문서 자동화: Sphinx, pdoc 등의 도구로 API 문서 자동 생성
- 튜토리얼과 예제: 실제 사용 사례와 코드 예제 제공
- 변경 로그: 버전별 변경사항 문서화
- 온라인 호스팅: Read the Docs, GitHub Pages 등에 문서 배포
✅ 모범 사례:
- 명확한 패키지 구조 설계: 직관적이고 논리적인 모듈 구조 설계
- init.py 파일 적절히 활용: 패키지 초기화와 API 정의
- 순환 참조 피하기: 모듈 간 순환 의존성 제거
- 상대 경로 임포트 주의: 명시적 상대 경로 사용 및 과도한 중첩 피하기
- 패키지 문서화: 상세한 docstring과 사용 예제 제공
- 명확한 버전 관리: 시맨틱 버저닝(SemVer) 준수
- 하위 호환성 유지: API 변경 시 버전 관리와 문서화
- 테스트 자동화: CI/CD 파이프라인 구축으로 품질 보장
- 부가 파일 포함: LICENSE, README, CHANGELOG 등 필수 문서 포함
- 사용자 피드백 반영: 이슈 트래커 활용 및 커뮤니티 소통