Module and Package

Sang Jun Lee·2020년 6월 28일
0

매번 비슷한 클래스와 함수를 작성해서 스크립트를 작성한다면 코드도 길어지고 중복되는 부분이 생길겁니다. 이런 경우에 사용하는 것이 모듈과 패키지로 개념과 사용법에 대해 설명해보겠습니다.

Module

변수, 함수, 클래스등을 모아둔 파일을 module(모듈)이라고 합니다.

예를 들어 multiplication.py라는 파일을 모듈로 만

  # 파일명 multiplication.py 라고 합시다
   
  def multiply(a,b):
    return (a*b)

두 변수를 받는 함수로 파일을 만들었고 이 파일이 모듈이 됩니다. 아래 main.py 파일을 만들고 multiplication.py 파일을 불러와 실행하는 것을 해보도록 하겠습니다.

  import multiplication
  
  print(multiplication.multiply(4,5))   # 결과값은 20

모듈을 사용할 때는 import로 불러온 뒤 모듈.변수, 모듈.함수() 형식으로 사용합니다.

  from multiplication import multiply
  
  print(multiply(6,7)) # 결과값은 42

위 처럼 from import로 변수와 함수를 가져오면 모율 이름을 붙이지 않고 사용할 수도 있습니다.

시작점

파이썬의 코드를 인터넷에서 보다보면 if name == 'main': 으로 시작하는 부분을 볼 수 있습니다. 이 코드는 현재 스크립트 파일이 실행되는 상태를 파악하기 위해 사용하며 모듈등을 불러와서 사용하게 되는 주 파일에서 주로 사용하게 됩니다.

  #hello.py
  
  print('hello 모듈 시작')
  print('hello.py __name__:', __name__)
  print('hello 모듈 끝')
같은 폴더 안에 main.py를 만들어 아래처럼 실행해보겠습니다.
  #main.py
  import hello
  
  print('main.py __name__:', __name__)

실행결과는

hello 모듈 시작
hello.py __name__: hello
hello 모듈 끝
main.py __name__: __main__

hello.py에서의 __name__의 반환값과 main.py에서의 반환값이 다릅니다. 어떤 스크립트 파일이든 파이썬 인터프리터가 최초로 실행한 스크립트 파일의 __name__에는 '__main__'이 들어갑니다. 이는 프로그램의 시작점(entry point)이라는 뜻입니다.

파이썬은 최초로 시작하는 스크립트 파일과 모듈의 차이가 없습니다. 어떤 스크립트 파일이든 시작점도 될 수 있고, 모듈도 될 수 있습니다. 그래서 name 변수를 통해 현재 스크립트 파일이 시작점인지 모듈인지 판단합니다.

if __name__ == '__main__':처럼 __name__ 변수의 값이 '__main__'인지 확인하는 코드는 현재 스크립트 파일이 프로그램의 시작점이 맞는지 판단하는 작업입니다. 즉, 스크립트 파일이 메인 프로그램으로 사용될 때와 모듈로 사용될 때를 구분하기 위한 용도입니다.

Package

모듈은 스크립트 파일이 한개지만 패키지는 폴더(디렉토리)로 구성됩니다.

계산기 calculator 패키지를 만든다고 하고 위에서 만든 모듈도 활용하여 패키지를 만들어보겠습니다.

위 그림과 같은 구조로 패키지가 생성됩니다. calculator폴더 안에 __init__.py 파일을 저장해야 하고 이 파일이 있으면 해당 폴더는 패키지로 인식됩니다. 기본적으로 내용은 비워둘 수 있습니다. 파이썬 버전 3.3 이상부터는 없어도 패키지로 인식되는점 알아주세요. 하지만 하위버전를 쓰는 경우도 있기에 왠만하면 작성하는 것을 권장합니다.

# __init__.py 파일의 내용
 
__all__ = ['bill']
------------------------
# 패지키내 모든 모듈 import
# from 패키지명 import *
from models.account import *
bill.charge(1, 50)

추가적으로 __init__.py 파일에서 중요한 변수로 __all__ 이라는 리스트 변수가 있는데, 이 변수는 "from 패키지명 import *" 문을 사용할 때, 그 패키지 내에서 import 가능한 모듈들의 리스트를 담고 있습니다. 즉, __all__ 에 없는 모듈은 import 되지 않고 에러가 발생한다.

이제 다시 패키지를 작성하도록 하겠습니다.

  #add_and_multiply.py
  from .multiplication import multiply 
  # 위에서 작성했던 multiplication.py을 모듈로 불러와 곱셈과 덧셈을
  # 구하는 모듈을 만들었습니다.
  def add_and_multiply(a,b):
    return multiply(a,b) + (a+b)

main.py 의 내용도 수정하여 작성하겠습니다.

  # absolute path
  #from calculator.add_and_multiply import add_and_multiply 

  # relative path
  from .calculator.add_and_multiply import add_and_multiply

  if __name__ == '__main__':
    print(add_and_multiply(1,2)) 

주석에 absolute path와 relative path가 적혀 있습니다. 모듈이나 패키지를 불러올 때 두 방식중 하나를 선택하여 진행하면 됩니다. 여기 주의할 사항이 있습니다.

absolute path
from calculator.add_and_multiply import add_and_multiply
relative path
from .calculator.add_and_multiply import add_and_multiply

형식적으로는 relative path는 점(dot)를 찍고 시작하는 것을 볼 수 있는데요. 하나의 점이 의미하는 것은 모듈이나 패키지가 현재 위치의 같은 디렉토리에 있어 그것을 참조한다는 것입니다. 점을 두개 찍으면 상위의 부모 디렉토리를 참조한다는 의미입니다. 다만 위의 main.py에서는 relative path를 사용할 수 없습니다. 위의 파일 실행시키면

from .calculator.add_and_multiply import add_and_multiply
ImportError: attempted relative import with no known parent package

에러가 발생합니다. 파이썬 홈페이지(https://docs.python.org/3/tutorial/modules.html#intra-package-references)에서는 Since the name of the main module is always "main", modules intended for use as the main module of a Python application must always use absolute imports. 꼭 absolute path로 임포트 하도록 하고 있습니다.

두가지 방식의 장단점을 설명하겠습니다.

1) absolute path 
  장점 : 깔끔하여 보여지고 직관적입니다. 불러오는 소스에 대해 어디에
        위치해 있는지 설명하기 쉽습니다. 
  단점 : 디렉토리의 구조가 복잡할 경우 불러오는 이름이 너무 길어질 수
  있습니다.
2) Relative path
  장점 : 구조의 복잡함에도 점을 이용해 길이를 줄여 작성 및 보기 좋게
        할 수 있습니다.
  단점 : 공통 프로젝트로 인해 디렉토리 구조가 바뀔 시 지속적인 변경의
        어려움이 발생할 수 있습니다. 그리고 소스의 위치를 말하기
        어려울 수도 있습니다.

모듈과 패키지의 위치

모듈이나 패키지를 불러쓰기 위해서는 모듈이 어디있는지 알아야 합니다. 우리가 import를 통해 부르게 되면 파이썬은 아래순서로 찾습니다.

1. sys.modules를 통해 이미 import된 모듈과 패키지를 확인합니다.

2. 만약에 없다면 다음으론 built-in modules에서 파이썬에서 기본적으로 제공하는 공식 라이브러리를 확인합니다.

3. 그래도 없다면 sys.path를 통해 확인하게 됩니다. sys.path에 원하는 경로를 지정하여 주면 같은 디렉토리에 없더라도 원하는 모듈을 import하여 사용할 수 있습니다.

>>> sys.path.append("C:/doit/mymod")
>>> sys.path
['', 'C:\\Windows\\SYSTEM32\\python37.zip', 'c:\\Python37\\DLLs', 
'c:\\Python37\\lib', 'c:\\Python37', 'c:\\Python37\\lib\\site-packages', 
'C:/doit/mymod']
>>>
profile
Live now and Dream better tomorrow

0개의 댓글