본문 바로가기
DEV/파이썬 이론

파이썬 코딩 :: 파이썬 패키지(Package)

by 올커 2022. 9. 6.

 

파이썬 패키지(Package)


1. 패키지(Package)

 - 패키지 : 모듈을 기능별 묶음으로 묶어놓은 단위

    *모듈 : 코드를 작은 단위로 나눠서 저장해 놓은 파일

 - 패키지를 사용함을 통해 프로그램의 구성요소를 잘 정리할 수 있으며, 쉽게 재활용이 가능하다.

 

2. 패키지의 구성

  - 패키지는 기본적으로 디렉토리 단위이며, 내부에 __init__.py, 그리고 사용될 모듈들로 구성된다.

shapes/
    __init__.py
    area.py
    volume.py
run.py

  - 패키지를 불러올 땐 모듈과 동일하게 import를 사용한다.

    패키지 호출  →  import 패키지명

import shapes

    그러나 위와 같이  패키지를 불러올 경우, 파이썬에서는 패키지 내에 있는 모듈들은 가져올 수 없다. 


    * __init__.py (init : 초기화, initialize)

      ▶ 패키지를 초기화 할 때 사용되는 파일이며, 모듈을 import하면 동시에 같이 실행된다.

           이러한 성격이 있기 때문에, 패키지 안에 있는 모듈들을 __init__.py파일에서 호출하게되면,

           위의 'import shapes'와 같은 호출만으로도 패키지 내에 있는 모듈들을 가져올 수 있다.

       ▼ __init__.py에서 모듈로 불러오기

# __init__.py
from shapes import area, volume
# run.py
import shapes

print(shapes.area.square(2))
print(shapes.volume.cube(2))

>>> 4
>>> 8

       ▼ __init__.py에서 함수로 불러오기 (*run.py에서 모듈명 없이 함수명만으로 호출이 가능)

# __init__.py
from shapes.area import circle, square
# run.py
import shapes

print(shapes.circle(2))
print(shapes.square(2))

>>> 12.56
>>> 4

      ▶ 여러 모듈에서 사용되는 변수 또한 __init__.py에서 호출하면 동일하게 사용이 가능하다.

      ▶ 파이썬 3.3ver 이전에는 __init__.py 파일이 필수로 있어야 import 할 수 있었다.

           현재는 필수는 아니지만, 호환성을 위해 __init__.py를 만들어주는 것을 권장한다.


    * 특수변수 __all__

      ▶ __init__.py에서 import *를 하였을 때에는 '__00__'과 같은 변수들만 import 된다.

# run.py
from shape import *

print(dir())

>>> ['__annotations__', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__']

         import *를 했을 때 다른 변수들을 가져오기 위해서 특수변수 __all__을 사용하면 된다.

         사용방법은 아래와 같이 가져와야 할 정보들을 리스트에 문자열로 넣어서 사용한다.

       ▼ 각 모듈에서 __all__을 정의하는 경우

# shapes/area.py
# __all__ 정의
__all__ = ['circle', 'square'] 

PI = 3.14

# 원의 면적을 구해 주는 함수
def circle(radius):
    return PI * radius * radius  

# 정사각형의 면적을 구해 주는 함수
def square(length):
    return length * length

         run.py를 실행하면 아래와 같이 'circle'와 'square'가 추가된 것을 확인할 수 있다.

# run.py
from shapes.area import *

print(dir())
>>> ['__annotations__', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'circle', 'square']

       ▼ __init__.py에서 __all__을 정의하는 경우 (모듈 자체를 불러옴)

# __init__.py
__all__ = ['area', 'volume']

         run.py를 실행하면 아래와 같이 'area'와 'volume'이 추가된 것을 확인할 수 있다.

# run.py
from shape import *

print(dir())

>>> ['__annotations__', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'area', 'volume']

    모듈 호출 → import 패키지명.모듈명 (as 별칭)

# 방법 1
import shapes.volume as vol


# 방법 2
from shapes import volume

    함수 호출 → 패키지명.모듈명.함수명

## 모듈 import, alias 미사용시
import shapes.volume

shapes.volume.cube(3)


## 모듈 import, alias 사용시
import shapes.volume as vol

vol.cube(3)


## 함수 직접 호출
from shape.area import square

square(3)

3. 서브 패키지

 - 패키지 안에 또 다른 패키지가 있을 경우, 안에 있는 패키지를 서브패키지라고 말한다.

mymath/
    shapes/
        __init__.py
        area.py
        volume.py
    stats/
        __init__.py
        average.py
        spread.py
    __init_.py
run.py

   여기서 shape 패키지와 stats 패키지는 서브패키지라고 할 수 있다.


4. Import

 - 구조가 복잡할 경우 import를 제대로 하지 않으면 한참을 고민하게 되니 아래를 잘 숙지해야 한다.

(1) import ...

# 패키지 임포트
import mymath

# 서브패키지 임포트
import mymath.shapes

# 모듈 임포트
import mymath.shapes.area

# 모듈 안에 있는 변수나 함수는 이 방식으로 임포트 할 수 없음 
import mymath.shapes.area.circle # 오류

 - import 뒤에 호출할 모듈이나 패키지를 써준다. 그러나 모듈 안에 있는 변수나 함수는 이 방식으로 호출이 불가하다.

 - 또, (서브)패키지를 호출할 때에는 임포트할 대상을 (서브)패키지의 __init__.py에 입력해두어야 한다.

2) from ... import ...

# 패키지 안에 있는 패키지 임포트
from mymath import shapes

# 패키지 안에 있는 모듈 임포트
from mymath.shapes import area

# 모듈 안에 있는 함수 임포트
from mymath.shapes.area import circle

# import 뒤에는 . 을 쓸 수 없음 
from mymath import shapes.area # 오류

 - from 뒤에는 모듈이나 패키지, import 뒤에는 모듈이나 패키지 내부에서 호출할 대상을 써준다.

 - 주의할 점은 import 뒤에는 4번째 문처럼 '.'을 쓰게되면 오류가 난다.


5. 상대경로

 - 패키지를 import할 때 상대경로를 사용하면 더 간단하게 코드를 짤 수 있다.

mymath/
    shapes/
        __init__.py
        area.py
        volume.py
    stats/
        __init__.py
        average.py
        spread.py
    __init_.py
run.py

 - 상대경로는 위치에 따라 아래와 같이 '.' 과 '..' 2가지로 사용이 가능하다.

# 현재 경로
from . import *

# 상위 경로
from .. import *

 - 위 경로의 shapes내에 있는 __init__.py에서 mymath.shapes 패키지를 불러올 경우 아래와 같이 변경이 가능하다.

# 기존(절대경로)
from mymath.shapes import area, volume

# 변경(상대경로)
from . import area, volume

 - 함수를 가져올 경우에도 마찬가지로 사용이 가능하다.

# 기존(절대경로)
from mymath.shapes.area import *
from mymath.shapes.volume import *

# 변경(상대경로)
from .area import *
from .volume import *
# 기존(절대경로)
from mymath.stats.average import data_mean

# 변경(상대경로)
from ..stats.average import data_mean

 - 상대경로는 코드가 간소화되지만 직관성이 떨어지고 패키지 구조가 어떻게 되는지 파악하기 힘든 단점이 있으므로,

   경로가 너무 복잡해지는 경우에는 절대경로로 입력하는 것이 좋다.

 

반응형

댓글