모듈

안내: Think Python 14장 내용의 일부를 번역 및 요약수정하여 정리한 내용입니다.

모듈이란?

한 번 구현한 함수, 상수, 변수, 클래스, 객체 등 여러 파일에서 공유하여 사용하면 프로그램을 보다 효율적으로 구현할 수 있다. 이를 위해 모듈을 활용한다.

파이썬에서 모듈은 간단하게 말하면 하나의 파이썬 소스코드 파일이다. 파일의 확장자가 py이다. 모듈은 언제든지 불러와서(import) 모듈에 포함된 내용을 활용할 수 있다.

모듈 종류

모듈은 크게 세 종류로 나뉜다.

  • 내장 모듈(built-in module): 파이썬에서 기본적으로 제공하는 모듈
    • 파이썬을 설치할 때 기본으로 제공되는 모듈
    • 예제: math, urllib.request, random, turtle, os, sys 등등
  • 제3자 라이브러리 모듈: 제3자에 의해 제공된 라이브러리에 포함된 모듈
    • 제3자가 제공한 라이브러리를 설치할 때 제공되는 모듈
    • 예제: numpy.random, matplotlib.pyplot, pygame.mixer 등등
  • 사용자 정의 모듈: 개인 프로젝트를 진행하면서 작성한 모듈
    • 프로젝트 관리를 위해 사용되는 모듈
    • 예제: 아래 예제에서 소개되는 wc.py 파일

내장 모듈

파일썬을 설치하면 다양한 모듈이 함께 설치되며, 그런 모듈을 내장 모듈(built-in modules)이라 부른다. 지금까지 사용한 모듈은 random, math, os, sys 등이며, import 모듈명 형식으로 모듈을 불러왔다. 단, .py 확장자는 생략한다.

예를 들어, math 모듈을 불러오려면 다음과 같이 한다.

In [1]:
import math

그러면 math 모듈에서 정의된 많은 수학 관련 함수들을 사용할 수 있다. 예를 들어, 로그(log) 함수의 사용은 다음과 같다.

In [2]:
math.log(2)
Out[2]:
0.6931471805599453

무작위 수 생성 함수들을 모아놓은 random 모듈에 0과 1사이의 실수를 무작으로 생성하는 함수 random을 아래와 같이 사용한다.

In [3]:
import random

random.random()
Out[3]:
0.18428897078569628

random 함수는 실행할 때마다 새로운 수를 무작위로 생성한다.

In [4]:
random.random()
Out[4]:
0.8468363113674645

제3자 라이브러리 모듈

제3자에 의해 제공된 라이브러리에 포함된 모듈을 사용하려면 먼저 추가 패키지를 설치해야 한다. 예를 들어, 단순한 2차원 그래프를 그리는 도구가 필요하면 맷플롯립(matplotlib)을, 데이터분석을 위한 다양한 도구가 필요하면 넘파이(numpy)를 기본적으로 설치해야 한다. 제3자 제공 파이썬 패키지를 설치하는 방법은 해당 패키지의 홈페이지를 참고해야 한다. 보통은 파이썬을 설치할 때 함께 제공되는 파이썬 패키지 매니저인 pip 명령어를 활용한다.

파이썬과 관련된 주요 패키지를 함께 제공하는 앱이나 웹서버 등이 있어서 개인적으로 추가하고 관리하기 어려운 경우 사용할 수 있다. 예를 들어, 아나콘다 패키지는 matplotlib, numpy 등 파이썬 데이터분석과 관련된 다수의 패키지를 함께 제공한다. 또한 Repl.it, 구글 코랩 등도 파이썬 기본 이외에 다양한 패키지를 함께 제공한다.

현재 이 노트북을 실행하는 서버 또한 matplotlib, numpy 등을 포함해서 다양한 제3자 제공 라이브러리가 함께 설치되어 있다. Repl.it 및 구글 코랩에서 문제없이 작동하는 코드만 여기에서 사용한다.

제3자 라이브러리 모듈 불러오기

예를 들어, 고차원 어레이를 편하게 다룰 수 있도록 도와주는 많은 도구를 담고 있는 넘파이 패키지를 활용하려면 아래와 같이 numpy 패키지를 불러와야 한다. 그러면 관련된 많은 모듈과 도구들을 활용할 수 있다.

별칭 사용하기

모듈이나 패키지를 불러올 때 별칭을 지정하여 활용할 수 있다. 많이 사용되는 모듈이나 패키지는 많은 사람들이 관습적으로 사용하는 별칭이 있다. numpy 패키지의 경우 보통 np로 줄여 부른다. 별칭을 사용하는 방식은 다음과 같다.

In [5]:
import numpy as np

넘파이 패키지에 random 모듈이 포함되어 있다. 파이썬에서 기본으로 제공하는 내장 모듈인 random 보다 많은 기능을 갖춘 도구들이 넘파이 random 모듈이 제공한다.

앞서 언급했던 파이썬 내장 모듈 random에서 정의된 random 함수에 해당하는 함수가 넘파이 random 모듈에 동일한 이름으로 정의되어 있다. 하지만 넘파이 random 모듈을 사용하려면 반드시 np를 아래와 같이 추가해야 한다. 그렇지 않으면 파이썬 내장 모듈과 이름이 혼동되어 문제가 발생할 수 있다.

In [6]:
np.random.random()
Out[6]:
0.25162557537896735
In [7]:
np.random.random()
Out[7]:
0.9345326527438754

패키지나 모듈을 불러올 때 별칭을 지정하면 반드시 별칭으로 사용해야 한다. 그렇지 않으면 오류가 발생한다.

In [8]:
numpy.random.random()
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
<ipython-input-8-0d49693ea4bf> in <module>
----> 1 numpy.random.random()

NameError: name 'numpy' is not defined

원래의 패키지 또는 모듈의 이름을 사용하려면 한 번 불러와야 한다. 그러면 원래 이름과 별칭 모두 사용할 수 있다.

In [9]:
import numpy
In [10]:
numpy.random.random()
Out[10]:
0.8479662358903914
In [11]:
np.random.random()
Out[11]:
0.4279138518269646

사용자 정의 모듈

먼저, 아래 코드를 담고 있는 wc.py 파일을 먼저 작성한다. wc.py 파일을 이용하여 사용자가 임의로 작성한 파이썬 코드 파일을 모듈로 활용하는 법을 설명한다.

주의: 설명을 위해 wc.py 파일이 현재 작업 디렉토리의 하위 디렉토리인 codes 디렉토리에 저장되어 있다고 가정한다.

파일의 내용은 아래와 같다.

__init__.py 파일 작성

현재 작업 디렉토리가 아닌 다른 디렉토리에 포함된 모듈을 불러오려면 해당 디렉토리에 __init__.py 파일이 생성되어 있어야 한다. 따라서 codes 라는 하위 디렉토리에 포함된 파일들의 리스를 확인하면 wc.py__init__.py 두 개의 파일이 포함되어 있어야 한다.

주의: __init__.py 파일은 아무 내용이 없는 빈 파일이어도 된다. 다른 용도는 여기서 다루지 않는다.

현재 작업 디렉토리 확인

파이썬 명령어를 이용하여 현재 작업 디렉토리(current working directory, 줄임말: cwd)를 확인하는 방법은 다음과 같다.

In [12]:
import os
cwd = os.getcwd()
print(cwd)
/tf/GitHub/ProgInPython/notebooks

현재 작업 디렉토리에 codes라는 디렉토리 포함여부 확인은 다음과 같다.

In [13]:
'codes' in os.listdir(cwd)
Out[13]:
True

codes 디렉토리에 포함된 파일들의 리스트 확인하면 wc.py__init__.py 두 파일이 포함되어 있음을 볼 수 있다.

In [14]:
os.listdir("./codes")
Out[14]:
['turtle_princeton',
 'wc.py',
 '.DS_Store',
 'receipt.txt',
 'turtleWar',
 'transactions.py',
 'mypolygon1.py',
 'transactions.txt',
 'polygon.py',
 'turtle-recursion',
 '__init__.py',
 'pie.py',
 '__pycache__',
 'mypolygon4.py',
 'mypolygon3.py',
 'receiptPython.txt',
 'HFProgramming_Ch07_GUI',
 'mypolygon2.py',
 'receipt.py',
 'special.py',
 'flower.py']

사용자 정의 모듈 불러오기

wc.py 모듈에 포함되어 있는 linecount 함수를 활용하기 위해서 먼저 wc.py 모듈을 불러와야 한다.

현재 작업 디렉토리 모듈 불러오기

만약 wc.py 모듈이 현재 디렉토리에 포함되어 있다면 아래와 같이 불러오면 된다.

import wc

또한 wc.py에 작성된 코드 마지막 줄을 아래와 같이 작성해야 오류가 발생하지 않는다.

print(linecount('wc.py'))

경로 설정 문제

그런데 wc.py 모듈이 현재 디렉토리의 하위 디렉토리인 codes 에 포함되어 있기 때문에 불러오기 과정이 좀 더 복잡하다. 단순히 import wc 명령어를 사용하면 모듈을 찾을 수 없다는 오류(ModuleNotFoundError)가 발생한다.

In [15]:
import wc
---------------------------------------------------------------------------
ModuleNotFoundError                       Traceback (most recent call last)
<ipython-input-15-7068d0230b9c> in <module>
----> 1 import wc

ModuleNotFoundError: No module named 'wc'

이와 같이 현재 작업디렉토리가 아닌 곳에 포함된 사용자 정의 모듈은 다른 방식으로 불러와야 한다. 다양한 방식이 있지만 여기서는 라이브러리 경로(library path)에 특정 디렉토리를 추가하는 방식을 사용한다.

주의: 여기서 라이브러리 경로에 특정 경로를 추가하는 방식은 임시적이다. 라이브러리 경로를 영구적으로 변경하려면 다른 방식을 따라야 한다.

파이썬 라이브러리 경로 확인하기

먼저 파이썬이 기본적으로 사용하는 라이브러리들의 경로를 확인해보자. sys.path 변수에 파이썬이 기본적으로 지원하는 라이브러리들의 경로가 리스트로 저장되어 있다.

In [16]:
import sys
sys.path
Out[16]:
['/usr/lib/python36.zip',
 '/usr/lib/python3.6',
 '/usr/lib/python3.6/lib-dynload',
 '',
 '/usr/local/lib/python3.6/dist-packages',
 '/usr/lib/python3/dist-packages',
 '/usr/local/lib/python3.6/dist-packages/IPython/extensions',
 '/root/.ipython']

파이썬 라이브러리 경로에 임시 경로 추가하기

sys.path 에 저장된 라이브러리들의 경로들의 리스트에 원하는 경로를 추가한다.

여기서는 현재 작업디렉토리의 하위 폴더인 codes를 경로에 추가하며, 리스트에 항목을 추가하는 append 메소드를 활용한다.

In [17]:
sys.path.append(cwd + "/codes")

이제 새로운 경로가 추가된 것을 확인할 수 있다.

In [18]:
sys.path
Out[18]:
['/usr/lib/python36.zip',
 '/usr/lib/python3.6',
 '/usr/lib/python3.6/lib-dynload',
 '',
 '/usr/local/lib/python3.6/dist-packages',
 '/usr/lib/python3/dist-packages',
 '/usr/local/lib/python3.6/dist-packages/IPython/extensions',
 '/root/.ipython',
 '/tf/GitHub/ProgInPython/notebooks/codes']

라이브러리 경로에 포함된 디렉토리의 모듈 불러오기

라이브러리 경로에 포함된 디렉토리의 모듈은 현재 디렉토리에 포함된 모듈을 불러오는 것처럼 하면 된다.

In [19]:
import wc

이제 wc가 누군지를 물으면 아래와 같이 답한다.

In [20]:
wc
Out[20]:
<module 'wc' from '/tf/GitHub/ProgInPython/notebooks/codes/wc.py'>

즉, wc는 모듈이라는 정보와 wc.py 파일이 저장된 위치 정보를 보여준다.

__name__ 속성과 __main__ 함수

__name__ 속성

파이썬에서 함수, 클래스, 모듈 등은 __name__이라는 특별한 속성을 가지며, 항상 자기 자신을 가리킨다.

예를 들어, 아래 함수를 살펴보자.

In [21]:
def myName():
    pass

이제 myName 함수의 __name__ 속성을 확인해보자. 속성 확인은 자료형의 메서드를 호출하는 방식과 비슷하다. 다만, 속성은 함수가 아니기에 괄호를 사용하지 않는다.

In [22]:
myName.__name__
Out[22]:
'myName'

모듈도 __name__ 속성을 갖는다. wc.py 모듈의 __name__ 속성을 확인해보자.

In [23]:
wc.__name__
Out[23]:
'wc'

앞으로 좀 더 구체적으로 배우게 될 클래스 역시 __name__ 속성을 갖는다. 사실 지금까지 살펴본 모든 자료형 역시 클래스이다. 예를 들어, 사전 자료형의 __name__ 속성은 아래와 같다.

In [24]:
dict.__name__
Out[24]:
'dict'

__main__ 함수

wc.py 모듈을 임포트할 때 앞서 ossys 모듈을 임포트할 때와는 달리 숫자 7을 출력한다. 이유는 모듈을 임포트할 때 모듈 안에 포함된 코드가 실행되기 때문인데, wc.py 파일의 경우에는 마지막 줄에 있는 아래 명령어가 실행되기 때문이다.

print(linecount('./codes/wc.py'))

linecount 함수는 인자로 지정된 파일에 포함된 내용의 줄 수(line number)를 계산해서 내준다. 따라서 위 명령문은 wc.py 파일에 포함된 내용이 몇 줄인가를 확인해준다.

그런데 사실 print(linecount('./codes/wc.py')) 명령문은 linecount 함수가 제대로 작동하는가를 확인하는 용도로 작성된 코드이며, 모듈을 불러와서 활용하기 위해서는 굳이 실행할 필요가 없다. 이런 경우에 모듈의 __name__ 속성을 이용하여 아래와 같이 작성하면 모듈을 임포트할 때 굳이 실행할 필요가 없는 코드를 모듈에 포함시킬 수 있다.

if __name__ == '__main__':
    print(linecount('./codes/wc.py'))

위와 같이 작성하면 import wc를 실행해도 if __name__ == '__main__':에 포함된 코드는 실행되지 않는다.

반면에 터미널을 이용해서 현재 작업 디렉토리에서 python codes/wc.py 형태로 wc.py 코드를 직접 실행하면 if __name__ == '__main__' 조건문의 본체가 실행된다.

__main__ 함수의 이런 기능은 C, Java 등에서 의무적으로 사용되는 main 함수와 유사한 기능을 수행한다. Repl.it 사이트에서 main 모듈이 기본적으로 실행되는 이유가 이런 전통에서 유래한다.

모듈 다시 불러오기

한 번 불러온 모듈을 다시 불러오면 모듈 내용이 또 실행되지는 않는다.

In [25]:
import wc

불러온 모듈 활용하기

어떤 종류의 모듈이든 한 번 불러온 모듈을 활용하는 방법은 동일하다. 우리가 작성하고 불러온 wc 모듈에 포함된 linecount 함수를 활용하는 방법도 동일하다. 예를 들어, wc.py 파일에 포함된 코드의 줄의 수를 알고자 하면 아래와 같이 실행한다.

In [26]:
wc.linecount('./codes/wc.py')
Out[26]:
8

반면에 codes 디렉토리에 포함된 __init__.py 파일은 비어있음을 아래와 같이 확인할 수 있다.

In [27]:
wc.linecount('./codes/__init__.py')
Out[27]:
0

별칭 사용

패키지나 모듈 또한 종류에 상관없이 별칭을 사용할 수 있다. 예를 들어, wc 모듈을 wordCount 라고 별칭을 지정하면서 불러올 수 있다.

In [28]:
import wc as wordCount

이제는 wc 대신에 wordCount를 사용할 수 있다.

In [29]:
wordCount.linecount('./codes/wc.py')
Out[29]:
8
In [30]:
wordCount.linecount('./codes/__init__.py')
Out[30]:
0