python 2

모듈

본 토픽은 현재 준비중입니다.공동공부에 참여하시면 완성 되었을 때 알려드립니다.

프로그램은 작고 단순한 것에서 크고 복잡한 것으로 진화한다. 그 과정에서 코드의 재활용성을 높이고, 유지보수를 쉽게 할 수 있는 다양한 기법들이 사용된다. 그 중의 하나가 코드를 여러개의 파일로 분리하는 것이다. 이를 통해서 얻을 수 있는 효과는 아래와 같다.

  • 자주 사용되는 코드를 별도의 파일로 만들어서 필요할 때마다 재활용할 수 있다.
  • 코드를 개선하면 이를 사용하고 있는 모든 애플리케이션의 동작이 개선된다.
  • 코드 수정 시에 필요한 로직을 빠르게 찾을 수 있다.
  • 필요한 로직만을 로드해서 메모리의 낭비를 줄일 수 있다.

모듈이란

파이썬에서는 필요에 따라서 로드 할 수 있도록 만들어진 파일을 모듈(module)이라고 부른다. 모듈을 만들고 모듈을 로드하는 방법을 알아보자.

모듈이 없다면

우선 모듈이 없는 애플리케이션을 하나 만들어보자. 이 코드의 파일명은 main.py 다.

def welcome():
    return 'Hello world'

print welcome();

위의 코드는 아무런 문제가 없다. 하지만 welcome 함수가 자주 사용되는 것이라고 가정해보자. 이런 경우 이것이 필요할 때마다 이 함수를 정의해서 사용하는 것은 유지보수도 어렵고 낭비가 될 것이다. 이럴 때 모듈이 필요하다. 함수 welcome을 모듈로 만들어보자.

모듈의 사용

새로운 파일을 만든다. 이름은 greeting.py다.

greeting.py

#greeting.py
def welcome():
    return 'Hello world'

main.py의 내용을 다음과 같이 변경한다.

main.py

#main.py
import greeting
print greeting.welcome()

이전 예제와 비교했을 때 결과는 같다. 하지만 함수 welcome을 main.py의 외부 파일로 분리했다. 다음은 위의 코드에 대한 분석이다.

import greeting

import는 모듈을 로드할 때 사용하는 명령이다. greeting은 불러오려고 하는 모듈의 이름인데, 모듈의 이름은 확장자를 제외한 파일명을 사용한다. import greeting은 greeting.py라는 이름의 파일을 찾아서 로드한다.

greeting.welcome()

greeting은 모듈의 이름이다. 즉 함수 welcome 은 로드(import) 후에 바로 사용할 수 있는 것이 아니라 greeting 라는 모듈의 이름을 통해서 사용할 수 있게 되는 것이다. 이러한 개념을 네임스페이스(namespace)라고 한다.

네임스페이스

네임스페이스가 무엇인가를 정의하기에 앞서서 파일을 생각해보자. 파일은 데이터를 보관하고 있는 일종의 컨테이너다. 그리고 이 컨텐이너는 파일명으로 식별이 된다. 파일의 수가 많아지면서 파일을 관리하는 것이 점점 어려워진다. 그래서 고안된 것이 바로 디렉토리다. 디렉토리를 이용하면 같은 이름의 파일이 하나의 컴퓨터에 존재할 수 있다. 파일명의 충돌을 회피 할 수 있게 된 것이다. 네임스페이스란 간단하게 디렉토리와 같은 것이라고 생각하자. 하나의 에플리케이션에는 다양한 모듈을 사용하게 된다. 그런데 모듈이 서로 다른 개발자에 의해서 만들어지기 때문에 같은 이름을 쓰는 경우가 생길 수 있다. 이런 경우 먼저 로드된 모듈은 나중에 로드된 모듈에 의해서 덮어쓰기 되기 때문에 이에 대한 대책이 필요하다. 네임스페이스가 필요해지게 되는 것이다. 

만약 환영인사를 언어별로 다르게 하고 싶다면 어떻게 해야 할까? 3개의 파일을 만든다.

greeting_en.py

def welcome():
    return 'Hello world'

greeting_ko.py

# -*- coding:utf-8 -*-
def welcome():
    return '안녕세계'

main.py

import greeting_ko
import greeting_en
print welcome();
print welcome();

위의 코드는 무언가 이상하다. 함수 welcome은 greeting_en.py에도 있고 greeting_ko.py에도 있다. 위의 코드에서 호출하고 있는 welcome이 누구의 welcome인지 불분명하다. 실행시켜보면 실제로 에러가 발생한다. 아래와 같이하면 welcome이 어디에 속한 함수인지 분명해진다.

main.py

import greeting_en
import greeting_ko
print greeting_en.welcome();
print greeting_ko.welcome();

여기서 greeting_en, greeting_ko가 네임스페이스다. geeting_en과 greeting_ko라는 네임스페이스가 있기 때문에 같은 이름인 메소드 welcome이 어떤 메소드인지를 분명하게 명시할 수 있다.

from

그럼 welcome 앞에 네임스페이스(greeting)을 명시하고 싶지 않다면 어떻게 해야 할까? 모듈을 로드하는 방식을 달리하면 된다. 아래와 같이 from 뒤에 모듈의 이름을 붙이고, import 뒤에 함수의 이름을 붙이면 welcome 함수는 greeting_ko 모듈의 함수로 동작하게 된다.

main.py

from greeting_ko import welcome
print welcome();

또는 import 뒤에 *를 붙이면 모듈 greeting_ko 의 모든 함수를 네임스페이스 없이 사용할 수 있게 된다.

main.py

from greeting_ko import *
print welcome();

패키지

모듈의 수가 많아지면 모듈을 사용하기 좋은 형태로 잘 조직화하는 어려움이 생긴다. 이런 경우에 도입 할 수 있는 기능이 패키지(package)다. 패키지를 사용하면 모듈을 디렉토리 별로 분류할 수 있다.

아래와 같은 구조로 모듈을 구조화시키고 싶다고 하자. greeting이라는 패키지를 만들고 그 안에 작별인사와 Helloworld라는 이름의 인사를 언어별로 구분해서 정리하는 것이 목표다.

위와 같은 디렉토리 구조의 패키지를 만들었을 때 main.py로 en.py의 로직을 실행하는 방법을 알아보자.

en.py (그림 중 2번 파일)

def welcome():
    return 'Hello world'

main.py (3번)

아래의 3개 예제들은 패키지를 사용하는 3가지 방법을 보여준다. 코드를 주의깊게 보고 import와 from의 관계를 파악하자.

import greeting.helloworld.en
print greeting.helloworld.en.welcome()
from greeting.helloworld.en import welcome
print welcome()
from greeting.helloworld.en import *
print welcome()

위의 코드는 문제가 없이 모듈 en의 함수 welcome을 실행할 것이다. 하지만 아래 코드는 에러를 발생시킨다.

from greeting.helloworld import *
print en.welcome()

 에러의 내용은 아래와 같다.

Traceback (most recent call last):
  File "D:\dev\language\fundamental\python\greeting.py", line 2, in <module>
    print en.welcome()
NameError: name 'en' is not defined

en이 존재하지 않는다는 의미다. 

이 때 helloworld 디렉토리에 __init__.py(그림에서 1번에 해당 함) 파일을 만들고 아래와 같이 코드를 작성한다.

__all__ = ['en']

__init__.py 파일은 패키지의 정보를 담고 있는 특수한 파일이다. 이 파일에 배열 __all__ 의 값으로 모듈의 이름을 위와 같이 적어주면 from greeting.helloworld import *를 했을 때 helloworld 디렉토리에 있는 모듈 중 자동으로 로드할 파일을 명시 할 수 있는 것이다.

모듈 기본 디렉토리 지정

아래 내용은 중급 주제에 속하기 때문에 프로그래밍 초심자라면 모듈 기본 디렉토리의 개념이 무엇인지만 파악하고 이러한 기능이 필요할 때 이곳을 다시 찾아와서 꼼꼼하게 살펴보는 것을 권한다.

지금까지 import 를 이용해서 모듈을 로드하는 방법을 알아봤다. import를 이용할 때는 기본적으로 같은 디렉토리에 파일이 존재해야 한다. 이것은 서로 다른 애플리케이션이 똑같은 모듈을 각자 가지고 있어야 한다는 것을 의미한다. 이 또한 낭비라고 할 수 있다. 그럼 모든 파이썬 애플리케이션이 접근할 수 있는 모듈을 만들려면 어떻게 하면 될까? 모듈 기본 디렉토리를 사용하면 된다. 이것의 사용법을 알아보자.

python 콘솔에서 아래의 명령을 입력한다.

import sys
print sys.path

실행결과는 아래와 같다.

import sys

sys 모듈은 파이썬에서 기본적으로 제공하는 모듈이다. 즉 sys 모듈은 여러분이 만든 것이 아니라, python에서 제공하는 모듈이라는 것이다. 기본 제공 모듈에 대해서는 표준 라이브러리 수업에서 자세하게 알아볼 것이다. sys 모듈은 파이썬의 인터프리터와 관련된 다양한 정보와 제어방법을 제공하는 모듈이다.

print sys.path

sys.path는 특정 모듈을 로드(import) 할 때 경로를 검색해보는 리스트다. 아래는 위의 결과를 보기 좋게 정리해본 것이다. 예를들면 greeting.py라는 모듈을 로드 할 때 만약 실행 파일인 main.py와 모듈인 greeting.py가 같은 디렉토리에 존재하지 않는다면 파이썬은 /usr/lib/python2.6/greeting.py 파일을 찾는다. 이 파일도 존재하지 않는다면 이번엔 /usr/lib/python2.6/plat-linux2/greeting.py 파일을 찾는다. 이 파일이 존재한다면 로드하고, 존재하지 않는다면 4, 5, 6...10까지 순차적으로 파일의 존재 여부를 확인하게 되는 것이다. 아래에 열거된 디렉토리에 파이썬 모듈을 위치시키면 모든 파이썬 애플리케이션에서 사용할 수 있게 되는 것이다.

아래의 내용은 파이썬이 설치된 환경에 따라서 결과가 다를 것이다.
  1. ''
  2. '/usr/lib/python2.6'
  3. '/usr/lib/python2.6/plat-linux2'
  4. '/usr/lib/python2.6/lib-tk'
  5. '/usr/lib/python2.6/lib-old'
  6. '/usr/lib/python2.6/lib-dynload'
  7. '/usr/lib/python2.6/dist-packages'
  8. '/usr/lib/pymodules/python2.6'
  9. '/usr/lib/pymodules/python2.6/gtk-2.0'
  10. '/usr/local/lib/python2.6/dist-packages'

모듈 로드의 우선순위

모듈을 현재 디렉토리와 위의 디렉토리 중의 하나에 위치시켜보자. 필자는 위의 디렉토리 중에서 2번째에 해당하는 /usr/lib/python2.6에 파일을 위치시키겠다. 아래에서 첫번째 예제인 greeting.py와 세번째 예제인 main.py는 같은 디렉토리에 위치해야 한다.

필자와 똑같이 하지 말고 print sys.path를 통해서 출력된 내용을 보고 따라해야 한다.

greeting.py

def welcome():
        return 'hello'

/usr/lib/python2.6/greeting.py

def welcome():
        return 'world'

main.py

import greeting
print greeting.welcome()

main.py 를 실행한 결과는 hello가 출력된다. 첫 번째 파일인 greeting.py 을 삭제한 후에 다시 시도해보자. world가 출력될 것이다.(greeting.pyc 파일이 있다면 이 파일도 삭제해야 한다.) 첫 번째 파일이 사라졌기 때문에 파이썬은 두 번째 파일인 /usr/lib/python2.6/greeting.py 모듈을 사용한 것이다.

사용자 모듈 디렉토리 추가

위에서 sys.path를 통해서 나열한 디렉토리는 파이썬이 기본적으로 사용하는 경로들이다. 여기에 자기만의 경로를 추가하고 싶다면 PYTHONPATH라는 환경변수를 추가하면 된다. 이것은 운영체제의 PATH나 자바의 CLASSPATH와 비슷한 역할을 한다. 아래와 같은 방법으로 사용자 디렉토리를 추가하면 실행파일과 같은 디렉토리의 모듈보다는 우선순위가 낮지만, 모듈 기본 디렉토리 보다는 우선순위가 높다.

리눅스 & OSX & UNIX

유닉스 계열의 운영체제에서는 아래와 같은 방법으로 환경변수 PYTHONPATH를 설정할 수 있다. 파이썬 인터프리터는 이 변수를 참고해서 사용자의 기본 디렉토리를 찾아낸다. 아래 예제는 /home/egoing/python/lib을 파이썬의 모듈 디렉토리로 추가하는 예다. 아래의 내용을 콘솔에 입력한다.

export PYTHONPATH=$PYTHONPATH:/home/egoing/python/lib

위와 같이 환경변수를 설정하면 터미널을 닫았을 때 환경변수가 사라진다. 아래와 같이 .bash_profile에 위의 내용을 추가하면 사용자가 로그인 할 때마다 위의 구문이 실행되면서 환경변수가 자동으로 셋팅된다. 파일을 수정하기 위해서 nano를 이용해서 파일을 수정한다면 아래와 같이 입력한다. 그리고 위의 내용을 .bash_profile의 내용에 추가한다.

nano ~/.bash_profile

윈도우

윈도우 계열에서는 환경변수를 설졍할 때 set과 setx 명령을 사용한다. set은 현재 명령 프롬프트에서만 사용되는 환경변수를 설정하는 것이고, setx는 영구적으로 환경변수를 설정하는 것이다. 환경변수를 설정하기 위해서는 우선 윈도우키+r을 이용해서 '실행' 창을 구동하고 거기에 cmd라고 입력한다.

C:\Python27\userlib을 모듈 기본 디렉토리로 추가하고 싶다면 명령 프롬프트에 아래의 명령을 차례로 입력한다.

set PYTHONPATH=%PYTHONPATH%;C:\Python27\userlib
setx PYTHONPATH %PYTHONPATH%;C:\Python27\userlib

참고

  • 봤어요 0명

댓글

댓글 본문
  1. 신성훈
    혹시

    greeting 디렉토리 바로 밑에도 __init__.py 파일을 하나 만들어야 할 거 같은데요...

    import greeting.helloworld.en
    print greeting.helloworld.en.welcome()

    이거 컴파일 할 때 아래처럼 오류가 발생해서요

    Traceback (most recent call last):
    File "main.py", line 4, in <module>
    import greeting.HelloWorld.en
    ImportError: No module named HelloWorld.en

    https://wikidocs.net/1418 의 상단에 적힌 것을 보면
    가상의 game 패키지를 작성할 때
    game 디렉토리 밑에도 __init__.py 파일을 만들었어요.


    game/__init__.py //<--- 요기처럼

    game/sound/__init__.py
    game/sound/echo.py
    game/sound/wav.py

    game/graphic/__init__.py
    game/graphic/screen.py
    game/graphic/render.py

    game/play/__init__.py
    game/play/run.py
    game/play/test.py
  2. 신성훈
    패키지 를 실습하려는 데

    main.py 에
    import greeting.helloworld.en 라고 적는 부분아 나오는데...

    greeting 를 적는 이유는 main.py 파일을 포함하는 부모디렉토리는 적어야 하기 때문인가요?
  3. 신성훈
    패키지를 연습할 때 디렉토리 구조가 어떻게 되요?

    greeting 디랙토리 만든 후
    그 안에 main.py 파일, bye 디렉토리 , Helloworld 디렉토리를 만드나요?
  4. 룰루
    세미콜론은 원래 다 붙어야 하는데 생략해도 알아서 매번 컴퓨터가 세미콜론 넣어 줬던 거예요
    대화보기
    • 김태훈
      print welcome();
      여기에 세미콜론은 왜 붙는건가요?
    • Ryan Cho
      감사합니다