본문 바로가기
프로그래밍/Python

[Python/파이썬] 모듈(Module)과 패키지(Package)

by 별준 2020. 8. 29.

- 참고 문헌 및 사이트

https://docs.python.org/3/


 

모듈 Module

우리가 이전 게시글에서 파이썬 코딩을 할 때, 주로 인터프리터를 실행시켜서 한줄씩 실행했었습니다. 인터프리터로 클래스나 함수를 작성하고 종료하면, 당연하겠지만 정의했던 것들이 사라집니다. 그래서 자주 사용되는 함수/클래스/변수나 좀 더 긴 프로그램을 작성하고자 할때, 우리는 편집기를 사용해서 코드를 작성하고 그 파일을 입력으로 읽어서 사용하는 것이 좋으며, 이렇게 작성하는 것을 스크립트를 작성한다고 합니다. 

또한, 프로그램이 길어짐에 따라서 코드의 가독성을 높히고 유지보수를 쉽게하기 위해서 여러 개의 파일로 나눌 수도 있고, 자주 사용하는 함수를 각 프로그램에 정의를 복사하는것이 아니라 단순 파일을 읽음으로써 사용할 수도 있습니다.

 

위와 같은 기능들을 제공하기 위해서 파이썬은 모듈이라는 파일을 제공하고, 이 파일 안에는 우리가 정의한 함수/클래스/변수 등이 있습니다. 우리는 이 파일을 입력으로 받아서 스크립트나 인터프리터에서 사용할 수 있죠.

모듈로부터 모듈 안에 있는 정의들이 다른 모듈이나 메인으로 import될 수 있습니다.

즉, 정리하자면 모듈은 파이썬 정의와 실행 문장들을 담고 있는 파일이고, 파일의 이름은 모듈 이름에 .py라는 확장자를 붙이면 됩니다. 그리고 모듈 내에서 모듈의 이름은 전역변수 __name__으로 제공되는데, 이는 나중에 어떻게 사용되는지 알아보겠습니다.

 

우선 피보나치 수열을 반환하는 함수를 작성한 fibo.py 라는 이름의 파일을 현재 디렉토리(인터프리터가 실행되는 디렉토리)에 만들고 인터프리터를 실행합니다. 

# fibo.py
# Fibonacci numbers module

def fib(n):
    a, b = 0, 1
    while a < n:
        print(a, end=' ')
        a, b = b, a + b
    print()

def fib2(n):
    result = []
    a, b = 0, 1
    while a < n:
        result.append(a)
        a, b = b, a + b
    return result

 그리고 다음과 같이 import문을 사용해서 import합니다.

>>> import fibo

이렇게 하면 이제 인터프리터에서 fibo에 정의된 함수를 사용할 수 있게 됩니다. 하지만 fibo에 정의된 함수들이 현재 심볼 테이블에 직접 들어가지는 않습니다. 자세한 것은 조금 있다가 다시 설명하도록 하겠습니다.

우리는 이 모듈 이름 fibo를 사용해서 아래와 같이 fib, fib2함수를 참조할 수 있습니다.(모듈이름.함수명으로 접근한다)

만약 fibo.fib 함수를 자주 사용할 것이라면, 전역 변수로 대입해서 사용할 수 있습니다.

현재 메인의 전역 심볼 테이블을 살펴보면 아래와 같이 확인할 수 있습니다.

조금 다를 순 있겠지만, 모듈 이름인 'fibo'와 새롭게 정의했던 'fib'를 볼 수 있죠.

 

모듈은 처음 언급했듯이 함수 정의뿐만 아니라 실행되는 문장들도 포함할 수 있습니다. 이 문장들은 모듈을 초기화하는데 사용되며, 처음 import될 때만 실행됩니다. (엄밀히 말하자면 함수도 실행되는 문장이다)

각 모듈은 각자의 심볼 테이블을 갖고 있는데, 그 모듈에서 정의된 함수들의 전역 심볼 테이블로 사용됩니다. 

이 때문에 전역 변수와 충돌이 일어나지 않게 됩니다. 모듈의 함수나 변수에 접근하려면 모듈이름.함수명 or 모듈이름.변수로 접근 가능합니다.

 

모듈은 다른 모듈들을 import할수 있습니다. 모든 import문들이 모듈의 처음에 위치하는 것이 관례이긴 하지만 반드시 지켜야하는 사항은 아닙니다. 모듈 안에서 import되는 모듈들은 import하고 있는 모듈의 전역 심볼 테이블에 속하게 됩니다.

 

다양한 import 방법

1. from module import function name of module

다음으로 모듈이름으로 import하는 것이 아닌 약간 변화시켜서 모듈에 있는 이름(함수, 변수)등을 직접 import하는 것을 살펴봅시다.

>>> from fibo import fib, fib2
>>> fib(500)

인터프리터를 다시 실행시켜서, 위 예시를 실행해봅시다. 결과는 아래와 같습니다.

아까 단순히 import fibo를 했을 때에는 fibo.fib(arg)와 fibo.fib2(arg)로 fibo 모듈 안의 함수를 실행할 수 있었지만,

from fibo import fib, fib2를 사용했을 때에는 import모듈안에 있는 fib, fib2 함수를 직접 import하고 있습니다.

여기서는 main의 전역 심볼 테이블에 fibo가 들어가게 되는 것이 아니라, fib와 fib2 함수가 main의 전역심볼테이블에 속하게 되며, 확인해보면 다음과 같습니다.

이처럼 fibo모듈의 함수 fib, fib2가 직접 전역 심볼 테이블에 속하고 있는 것을 볼 수 있습니다.

 

2. from module import *

모듈에서 정의하고 있는 모든 이름을 import하는 것도 있습니다.

이 import는 밑줄(_)로 시작하는 이름을 제외한 모든 이름을 import하게 됩니다. 하지만 이렇게 사용하는 것을 권장하지는 않습니다. 만약 기존에 사용하고 있는 이름과 동일한 이름이 import하는 모듈에 존재한다면, 기존에 정의된 이름을 사라지게 할 수 있기 때문이죠.

 

3. import module as function name of module

모듈 이름 다음 위치에 as가 온다면, as 다음의 이름으로 모듈을 전역 변수 테이블에 속하게 됩니다.

>>> import fibo as fib
>>> fib.fib(500)

이렇게 fibo 모듈을 fib라는 이름으로 참조가 가능합니다. 전역심볼테이블을 확인하면 다음과 같습니다.

fibo로 전역심볼테이블에 저장된 것이 아닌 fib라는 이름으로 저장되어 있는 것을 볼 수 있습니다.

 

1,3번을 혼합해서 사용할 수도 있습니다.

>>> from fibo import fib as fibonacci
>>> fibonacci(500)

위 import는 fibo 모듈의 fib 함수를 직접 import하는데 fib 함수의 이름을 fibonacci로 전역심볼테이블에 속한다는 의미입니다. 그래서 fibonacci라는 이름으로 바로 fibo모듈의 fib함수에 접근이 가능합니다.

마찬가지로 fib함수는 fibonacci라는 이름으로 전역심볼테이블에 속하기 때문에 fib함수로는 접근할 수 없으며 전역심볼테이블을 살펴봐도 fibonacci라는 이름만 존재합니다.

 

스크립트로 모듈 실행

방금 작성한 fibo.py를 아래와 같이 변경해봅시다.

# fibo.py
# Fibonacci numbers module

def fib(n):
    a, b = 0, 1
    while a < n:
        print(a, end=' ')
        a, b = b, a + b
    print()

def fib2(n):
    result = []
    a, b = 0, 1
    while a < n:
        result.append(a)
        a, b = b, a + b
    return result

if __name__ == "__main__":
    import sys
    fib(int(sys.argv[1]))

 이렇게 작성한 스크립트를 cmd에서 아래와 같이 실행해봅시다. <arguments>에는 숫자를 넣으면 됩니다.

> python fibo.py <arguments>

결과는 아래와 같이 나오게 됩니다.

모듈 안에 있는 실행할 수 있는 코드는 import를 할 때 실행이 되며, 스크립트 자체를 실행해도 실행됩니다. 하지만 인터프리터에서 fibo를 import하면 if문은 거짓이 되어서 if문의 내부코드는 실행이 되지 않습니다.

여기서 __name__의 역할이 드러나게 됩니다. 모듈을 import 할 때는 모듈의 전역변수 __name__은 모듈 이름이 되지만, 스크립트로 실행되면 모듈의 __name__은 "__main__"이 됩니다. 그래서 모듈을 스크립트 자체로 실행할 때는 if문이 참이되어 실행되지만, import되면 if문이 거짓이 되어서 실행이 되지 않습니다.

 

모듈의 검색 경로

우리가 fibo라는 모듈을 import할 때, 인터프리터는 먼저 fibo라는 내장 모듈이 있는지 찾습니다. 만약 찾지 못한다면 변수 sys.path(문자열 리스트)에 포함되는 디렉토리들에서 fibo.py 파일을 찾게 됩니다.. 

변수 sys.path는 프로그램 시작시에 아래의 값들로 초기화됩니다.

- 실행되는 모듈이 속한 디렉토리(ex, fibo.py가 속한 디렉토리)

- PYTHONPATH 환경변수 -> 환경변수로 PYTHONPATH에 경로를 추가하면 이 경로들이 추가됩니다.

- 설치 기본값(내장 모듈들을 사용하기 위한 기본 경로)

 

초기화 이후에는 sys.path를 수정할 수 있습니다. 리스트이기 때문에 append()함수를 통해서 경로를 추가할 수 있습니다.

>>> import sys
>>> sys.path.append('D:\\')

 

모듈을 위한 내장 함수

1. dir() 함수

dir 함수는 모듈이 정의하고 있는 이름을 반환합니다. 여기서 무슨 이름들이 해당 모듈에서 사용되고 있는지 알 수 있습니다.

dir함수를 실행할 때, 인자가 없으면 현재 정의한 이름을 나열합니다. 내장 함수와 변수는 나열하지 않습니다.

만약 내장 함수와 변수들의 목록을 알고 싶다면, 표준 모듈 bulitins를 참조하면 되는데, 자세히 다루지는 않겠습니다.

 

패키지

패키지는 모듈을 디렉토리 식으로 모듈 이름 공간을 구조화하는 방법입니다. 

아래 예시는 음향 파일과 데이터의 처리를 위한 모듈들의 패키지로 계층적 파일 시스템으로 나타내었습니다.

sound +----- __init__.py
      +----- formats     +----- __init__.py
      |                  +----- wavread.py
      |                  +----- wavwrite.py
      |                  +----- aiffread.py
      |                  +----- auread.py
      |                  +----- auwrite.py
      |                  +----- ...
      |                  
      +----- effects     +----- __init__.py
      |                  +----- echo.py
      |                  +----- surround.py
      |                  +----- reverse.py
      |                  +----- ...
      |                  
      +----- filters     +----- __init__.py
      |                  +----- equalizer.py
      |                  +----- vocoder.py
      |                  +----- karaoke.py
      |                  +----- ...

파이썬이 디렉토리를 패키지로 취급하기 위해서는 __init__.py이 필요합니다.

(python 3.3 버전 이상에서는 __init__.py가 없어도 패키지로 인식하지만, 다른 버전의 파이썬과 호환이 되도록 추가해주는 것이 좋습니다)

__init__.py는 빈 파일이여도 되지만, 패키지의 초기화 코드를 작성할 수 있고 또는 __all__ 변수를 설정할 수 있습니다. __all__ 변수는 뒤에서 설명하도록 하겠습니다.

 

사용자는 패키지로부터 개별 모듈을 import할 수 있습니다.

import sound.effects.echo

위 코드는 sound.effects.echo 모듈을 읽어옵니다. 패키지의 시작부터 점(.)을 붙여서 참조할 수 있습니다.

단순 모듈을 import하는 것처럼 패키지도 다양하게 import 할 수 있습니다.

 

from sound.effects import echo

위 코드도 sound.effects.echo 모듈을 읽어오지만, 차이점은 패키지 접두어 sound.effects 없이 echo 이름만으로 사용이 가능하다는 점입니다.

 

from sound.effects.echo import echofilter

위 코드는 echo 서브 모듈의 원하는 함수만을 직접 import하는 것입니다.

 

import문은 먼저 패키지에 정의가 되어있는지 검사를 하고, 그렇지 않으면 모듈이라고 가정하고 로드합니다. 만약 찾지 못한다면 ImportError를 일으키게 됩니다.

그리고 import item.subitem.subsubitem에서 마지막 subsubitem을 제외한 나머지 항목은 무조건 패키지여야 합니다.. !

또한, from package import item을 사용할 때, 여기서 item은 패키지의 서브 모듈일 수도 있고 서브 패키지일 수도 있으며, 패키지에 정의된 함수, 변수, 클래스일 수도 있음을 유의하라.

 

패키지에서 *로 import하면 어떻게 될까?

우리는 앞서 모듈을 import할 때, from module import *로 모듈안에 존재하는 모든 이름을 import할 수 있다고 배웠습니다.

그렇다면 아래와 같이 사용하면 어떨까요 ?

from sound.effects import *

sound.effects 패키지에 존재하는 모든 모듈들을 찾아서 import하는 것이라고 추측할 수 있지만, 모든 모듈을 읽어들이는 것이 아니라 sound.effect 패키지의 __init__.py의 __all__ 변수에 속해있는 모듈만 import 되게 됩니다.

# __init__.py in sound.effects
__all__ = ["echo", "surround", "reverse"]

sound.effect 패키지의 __init__.py에 __all__ 변수는 위와 같이 초기화하고, * 을 사용해 import하게 되면 sound.effect 패키지의 echo, surround, reverse 모듈을 import하게 되는 것입니다.

만약 __all__이 정의되지 않았다면, from sound.effects import * 는 아무 모듈도 import하지 않습니다.

 

패키지 간에는 내부 참조가 가능합니다.

패키지가 서브 패키지로 구조화될 때, 만약 surround 모듈에서 echo와 formats 모듈이 필요할 때 절대 경로로 다음과 같이 사용할 수 있을 것입니다.

# sound/effects/surround.py
from sound.effects import echo
from sound import formats

위 절대경로는 아래와 동일합니다.

# sound/effects/surround.py
from . import echo
from .. import formats

점 하나(.)는 현재 surround가 있는 경로(sound/effects)를 의미하고, 점 두개(..)는 현재 디렉토리의 부모 디렉토리, 즉 sound/를 의미합니다. 상대 경로로 import하는 경우에는 현재 모듈의 이름을 기준으로 둔다는 것을 기억합시다 !

댓글