8장. 모듈과 패키지

앞장까지는, 대화식 콘솔과 간단한 스크립트 수준의 코드를 살펴보았다. 이러한 것은 작은 예제에서는 잘 작동하겠지만, 프로그램의 덩치가 커져감에 따라서 필연적으로 작은 단위의 프로그램으로 나누어지게 된다. 자이썬에서 큰 프로그램을 구성하는 기본 단위는 모듈이다.

재사용을 위해 가져오기

코드를 모듈로 만드는 것은 대량의 코드를 조직화하는 데에 도움이 된다. 모듈은 서로 속하는 논리적으로 분리된 코드로서 사용될 수 있으며, 프로그램을 이해하기 쉽게 만들어준다. 모듈은 서로 다른 애플리케이션에서 공용으로 가져다 쓸 수 있는 라이브러리를 구축하는 데에 도움이 된다. 자이썬의 표준 라이브러리에는 여러분의 프로그램에서 즉시 사용할 수 있는 모듈이 많이 들어 있다.

가져오기의 기본

다음은 가져오기(import)를 설명하기 위한 매우 단순한 프로그램이다.

breakfast.py

import search.scanner as scanner
import sys
class Spam(object):

    def order(self, number):
        print "spam " * number

    def order_eggs():
        print " and eggs!"

    s = Spam()
    s.order(3)
    order_eggs()

이 프로그램에는 두 개의 함수가 정의되어 있다. 이름 공간(namespace)은 고유한 식별자들의 논리적 그룹이다. 달리 말하면, 이름 공간은 여러분의 프로그램 코드로부터 접근 가능한 이름의 집합이다. 예를 들어, 자이썬 프롬프트를 열어서 dir()을 입력하면, 번역기(interpreter)의 이름 공간에 있는 이름들이 표시될 것이다.

>>> dir()

['__doc__', '__name__']

번역기 이름 공간에 __doc__과 __name__이 있음을 알 수 있다. __doc__ 속성에는 최상위 문서화 문자열(docstring)이 들어가는데, 이 경우에는 비어있다. __name__ 속성을 잠시 살펴보자. 우선 자이썬 모듈(modules)에 대하여 이야기할 필요가 있다. 자이썬에서 모듈은 파이썬의 각종 정의 및 서술을 담는 파일로서, 이름 공간을 정의한다고도 볼 수 있다. 모듈 이름은 파일 이름에서 .py를 뺀 것과 같다. 따라서 예제 파이썬 파일 ‘breakfast.py’는 ‘breakfast’ 모듈을 정의한다.

이제 __name__ 속성에 대한 이야기를 해보자. 자이썬 breakfast.py와 같이 모듈이 직접 수행될 때에는, __name__은 ‘__main__’이 된다. 모듈이 반입되었을 경우에는, __name__은 모듈의 이름이 된다. 즉, ‘import breakfast’를 하면 결과적으로 breakfast 모듈 내에서의 __name__은 ‘breakfast’가 된다. 기본 자이썬 프롬프트로 돌아가보자.

>>> dir()
['__doc__', '__name__']
>>> __name__
'__main__'

breakfast를 가져온 결과로 어떤 일이 일어나는지 살펴보자.

>>> import breakfast
spam spam spam
and eggs!
>>> dir()
['__doc__', '__name__', 'breakfast']
>>> import breakfast
>>>

import 이후에 dir()을 해보면 breakfast가 최상위 이름 공간에 추가되었음을 알 수 있다. 가져오기는 breakfast.py의 코드를 실제로 실행시킨다는 점에 유념하기 바란다. 자이썬에서는 당연한 일이다. 모듈이 반입되었을 때, 그 모듈 내의 서술은 실제로 실행된다. 클래스 및 함수 정의에 대해서도 동일하게 적용된다. 모듈을 처음 들여올 때에만 해당된다는 점이 중요하다. 우리가 마지막에 ‘import breakfast’를 재차 입력하였지만 아무런 출력이 없었다. 대부분의 경우에 있어서는 모듈을 들여올 때 print 문을 실행할 필요는 없을 것이다. 모듈이 반입되었을 때에는 코드를 실행하지 않되, 직접적으로 호출되었을 경우에는 코드를 실행하고자 할 때, __name__ 속성을 확인하는 방법을 쓴다. __name__ 속성이 ‘__main__’일 경우, 우리는 그 모듈이 다른 모듈에서 가져다 쓴 것이 아니라 직접적으로 호출된 것임을 알 수 있다.

class Spam(object):

    def order(self, number):
        print "spam " * number

    def order_eggs():
        print " and eggs!"

if __name__ == '__main__':
s = Spam()
s.order(3)
order_eggs()

이제 우리가 breakfast를 들여오면(번역기를 닫았다가 다시 열어야 모듈을 실제로 다시 들여온다!), 다음과 같은 출력을 얻는다.

>>> import breakfast

이 경우에 __name__ 속성이 모듈의 이름인 ‘breakfast’를 가질 것이기 때문이다. 만약에 ‘jython breakfast.py’와 같이 명령행에서 breakfast.py를 호출한다면, 그러한 결과를 다시 볼 수 있는데, 그 이유는 breakfast가 __main__으로서 동작하기 때문이다.

$ jython breakfast.py
spam spam spam
and eggs!

Import 문

자바에서는, import 문은 엄밀하게는 반드시 소스 파일의 상단에 와야 하는 컴파일러 지시자이다. 자이썬에서의 import 문은 소스 파일 어는 곳에서나 사용할 수 있는 표현이며, 심지어는 조건적으로 실행할 수도 있다.

예를 들어, 무언가를 import하려고 하는데 그것이 제 위치에 없을 수도 있다면, 관용적으로 try 블록 내에서 import를 하도록 하고, except 블록에는 모듈이 있을 만한 다른 위치라든지 그외의 대처 방법을 정의할 수 있다.

>>> try:
...     from blah import foo
...     print "imported normally"
... except ImportError:
...     print "defining foo in except block"
...     def foo():
...         return "hello from backup foo"
...
defining foo in except block
>>> foo()
'hello from backup foo'
>>>

‘blah’라는 이름의 모듈이 존재한다면, 그 모듈 내에 정의된 것을 취하여 ‘imported normally’가 출력될 것이다. 그러한 모듈은 존재하지 않으므로, except 블로 내에서 foo를 정의하였고, ‘defining foo in except block’가 출력된다. 또한 foo를 호출하면, ‘hello from backup foo’ 문자열이 반환된다.

예제 프로그램

자이썬에서의 들여오기에 대한 설명을 위하여 다음과 같이 작위적이기는 하지만 단순한 프로그램 예제를 준비했다.

chapter8/
        greetings.py
        greet/
                __init__.py
                hello.py
                people.py

이 예제에는 greet라는 하나의 패키지가 있다. 이것이 패키지인 이유는 디렉토리 내에 __init__.py라는 특별한 파일이 있기 때문이다. chapter8 디렉토리 자체는 __init__.py가 없으므로 패키지가 아니다. 예제에는 greetings, greet.hello, 그리고 greet.people까지 세 개의 모듈이 있다. 이 프로그램 코드는 http://kenai.com/projects/jythonbook/sources/jython-book/show/src/chapter8에서 다운로드할 수 있다.

greetings.py

print "in greetings.py"
import greet.hello

g = greet.hello.Greeter()
g.hello_all()

greet/__init__.py

print "in greet/__init__.py"

greet/hello.py

print "in greet/hello.py"
import greet.people as people

class Greeter(object):
    def hello_all(self):
        for name in people.names:
            print "hello %s" % name

greet/people.py

print "in greet/people.py"

names = ["Josh", "Jim", "Victor", "Leo", "Frank"]

예제 시험해 보기

해당 디렉토리 내에서 greetings.py를 실행한다면 다음과 같은 출력을 얻을 것이다.

$ jython greetings.py
in greetings.py
in greet/__init__.py
in greet/hello.py
in greet/people.py
hello Josh
hello Jim
hello Victor
hello Leo
hello Frank

모듈의 실행 순서를 보여주기 위하여 각 .py 파일의 상단에 print 문을 배치했다. 수행될 때에 greetings 모듈이 적재되고 ‘in greetings.py’가 출력된다. 다음으로 greet.hello를 가져온다.

import greet.hello

이는 greet 패키지가 처음 반입되었을 때 __init__.py 내의 코드가 실행되어 ‘in greet/__init__.py’가 출력되기 때문이다. 그 다음 greet.hello 모듈이 실행되어 ‘in greet/hello.py’를 출력한다. greet.hello 모듈은 그 다음에 greet.people 모듈을 들여오고, ‘in greet/people.py’를 출력한다. 이제 들여오기가 모두 끝났으므로, greetings.py는 greet.hello.Greeter 클래스를 생성하고 hello_all 메소드를 호출할 수 있다.

Import 문의 여러 형태

Import 문은 여러 가지 형태로 사용할 수 있어서, 이름 붙은 값들을 여러분의 현재 모듈에 어떻게 들여올 것인지 섬세하게 제어할 수 있다.

import module
from module import submodule
from . import submodule

Import 문의 각 유형에 대하여 차례대로 살펴보자.

import module

이것은 가장 기본적인 import 방법으로서 모듈을 직접적으로 들여온다. 자바와는 달리, 가장 왼쪽의 모듈 이름에 바인드되므로 중첩된 모듈을 다음과 같이 들여오는 경우에는,

import greet.hello

여러분의 코드 내에서는 그냥 ‘hello’가 아니라 ‘greet.hello’로 찾아야 한다.

import greet.hello as foo

‘as foo’라는 부분은 ‘greet.hello’ 모듈을 ‘foo’라는 이름으로 부를 수 있도록 하여, 보다 편리하게 호출할 수 있다. 예제 프로그램에서는 이러한 방법으로 ‘greet.hello’에 대하여 ‘hello’라는 이름으로 바꾸었다. 코드를 읽기에는 조금 헷갈릴 수도 있겠지만, ‘hello’라는 이름이 하위 모듈의 이름과도 똑같다는 점은 그다지 문제가 되지 않는다. 여러분이 들여오고 싶은 식별자가 이미 이름 공간에서 사용 중인 경우에 이러한 기법을 활용할 수 있을 것이다. 예컨대 foo라는 변수가 이미 있으면 새로운 foo는 기존의 이름이 겹치지 않도록 bar라고 부를 수가 있는 것이다.

From Import 문

from module import name

이러한 형태의 import는 다른 모듈과 중첩된 모듈, 클래스 또는 함수를 가져올 수 있는 방법이다. 코드를 다음과 같이 들여올 수 있도록 해준다.

from greet import hello

이 경우, ‘hello’가 greet의 하위 모듈이라는 점이 중요하다. 이것은 이름을 바꿔붙인 것이 아니라 greet 이름 공간으로부터 실제로 ‘hello’라는 이름의 하위 모듈을 얻는 것이다. 어떤 모듈에 있는 모든 이름(밑줄로 시작하는 것은 제외)을 현재 모듈로 가져올 수 도 있는데, 이때는 *를 사용한다. 파이썬 커뮤니티에서는 이러한 방법을 그리 권장하지 않으며, 특히 자바 패키지를 들여올 때에 (동작하지 않는 등의) 문제의 소지가 있으므로 피하는 것이 좋다. 사용법은 다음과 같다.

from module import *

자바 패키지를 들여오는 것이 아니라면, 다른 모듈에서 모두 다 끌어올 때 이런 방법이 편리하기는 하다.

상대 Import 문

파이썬 2.5에 들어서 소개된 새로운 들여오기 방법은 명시적인 상대 import이다. 이러한 import 문은 현재 모듈로부터 얼마나 떨어져있는지를 표시하는 데에 점을 사용하며, 점 한 개가 현재 모듈을 의미한다.

from . import module
from .. import module
from .module import submodule
from ..module import submodule

이러한 새로운 스타일이 이제 막 도입된 것이기는 하지만, 사용을 권장하지는 않는다. 명시적인 상대 import는 묵시적인 상대 import의 필요에 의한 반작용으로서 나온 것이다. greet.hello로부터 Greeter 클래스를 들여옴으로써 greet.hello.Greeter 대신에 Greeter()로부터 인스턴스화하고자 한다면, 다음과 같이 import할 수 있을 것이다.

from greet.hello import Greeter

greet.people 모듈 안으로 Greeter를 들여오고자 한다면, 다음과 같이 할 수 있을 것이다.

from hello import Greeter

이것이 상대 import이다. greet.people은 greet.hello의 형제모듈이므로, ‘greet’를 생략할 수 있다. 이러한 상대적 들여오기는 금지되었으므로 사용하지 않도록 한다. 몇몇 개발자들은 모듈 구조가 바뀌어도 import가 유효하다는 이유로 이러한 스타일을 좋아하지만, 이러한 상대적 들여오기는 이름 충돌의 가능성이 있으며 그로 인하여 오류를 유발할 수 있다. 상대적 들여오기를 명시적으로 사용하는 방법을 제공하는 새로운 구문이 있다. 그 역시 권장하지는 않지만 말이다. 앞에서 본 import 문은 다음과 같이 표기할 수 있다.

from .hello import Greeter

Import 문 별칭 사용

위에서 살펴본 어떠한 들여오기에서든지 ‘as’ 절을 추가함으로써 들여오는 모듈에 새로운 이름을 부여할 수 있다.

import module as alias
from module import submodule as alias
from . import submodule as alias

이는 들여오기에 있어 상당한 유연성을 제공한다. greet.hello를 들여오는 경우에는 다음과 같이,

import greet.hello as foo

greet.hello 대신 foo로 취급할 수 있다.

모듈 이름 숨기기

일반적으로 어떤 모듈이 import될 때에는, 그 모듈 내의 모든 이름을 import하는 쪽에서 사용할 수 있다. 모듈을 들여올 때 이러한 이름을 숨기는 두 가지 방법이 있다. 밑줄(_)로 시작하는 이름은 private으로 기록된다. 이름에 대한 접근은 여전히 가능하지만, ‘from module import *’로 import할 경우에는 들여오기에서 제외된다.모듈 이름을 숨기는 두번째 방법은 __all__ 목록을 정의하는 것인데, 여기에는 노출하고 싶은 이름만 담는다. 다음의 예제는 자이썬의 OS 모듈의 __all__ 값이다.

__all__ = ["altsep", "curdir", "pardir", "sep", "pathsep",
               "linesep", "defpath", "name", "path",
               "SEEK_SET", "SEEK_CUR", "SEEK_END"]

해당 모듈의 노출되는 이름을 확장하기 위하여 모듈 내부의 __all__에 추가할 수 있다. 사실상, 자이썬에서의 os 모듈은 자이썬이 구동되는 운영체제에 기반하여 이름을 조건적으로 노출하기 위하여 이렇게 한다.

모듈 검색, 컴파일 및 적재

자이썬에서 패키지와 모듈의 위치를 찾고 컴파일 및 적재하는 과정을 이해하는 것은, 자이썬의 동작 원리를 깊이 이해하는 데에 큰 도움이 된다.

자바 Import 예제

자이썬이 시작될 때 CLASSPATH 경로에 있는 자바 클래스부터 시작하도록 하자.

package com.foo;
public class HelloWorld {
    public void hello() {
        System.out.println("Hello World!");
    }
    public void hello(String name) {
        System.out.printf("Hello %s!", name);
    }
}

자이썬 대화식 번역기에서 이 클래스를 조작해보자.

>>> from com.foo import HelloWorld
>>> h = HelloWorld()
>>> h.hello()
Hello World!
>>> h.hello("frank")
Hello frank!

HelloWorld 프로그램은 CLASSPATH에 있기 때문에, 앞서 말한 sys.path 프로세스를 거치지 않는다는 점에 유의하기 바란다. 이 경우 자바 클래스는 ClassLoader에 의하여 직접적으로 적재된다. 자바 ClassLoader에 에 대한 논의는 이 책의 범위를 벗어난다. ClassLoader에 대해서는 자바 언어 명세의 execution 절을 읽어보기 바란다.

http://java.sun.com/docs/books/jls/second_edition/html/execution.doc.html.

모듈 검색 경로 및 적재

자이썬에서의 모듈 검색 및 적재 과정은 CPython이나 자바에 비하여 복잡한데, 그것은 자이썬은 자바의 CLASSPATH와 파이썬의 path 양쪽을 다 검색할 수 있기 때문이다. 파이썬의 path와 sys.path부터 살펴보자. import를 하게 되면, sys.path에는 자이썬이 여러분이 들여오고자 하는 이름을 검색할 경로를 정의한다. sys.path 목록 내의 개체들은 자이썬이 모듈을 어디에서 찾아야하는지 일러준다. 대부분의 개체는 디렉토리를 가리키지만, 자이썬의 sys.path에는 디렉토리를 가리키는 것 이외에도 몇몇 특수한 항목이 있을 수 있다. sys.path의 어디에도 존재하지 않는(또한 CLASSPATH에서도 찾을 수 없는) 파일을 들여오려고 하면 ImportError 예외가 발생한다. 다음과 같이 명령을 입력하고 sys.path를 확인해 보자.

>>> import sys
>>> sys.path
['', '/Users/frank/jython/Lib', '__classpath__', '__pyclasspath__/',
'/Users/frank/jython/Lib/site-packages']

처음의 빈 항목(‘’)은 자이썬이 모듈이 현재 디렉토리에서 찾아봄을 의미한다. 두번째 항목은 자이썬의 핵심 모듈이 있는 Lib 디렉토리이다. 세번째와 네번째 항목은 특수한 표식인데 나중에 살펴보기로 하고, 마지막은 site-packages 디렉토리를 가리킨다. site-packages 디렉토리는 setuptools 지시자를 수행하여 설치한 라이브러리가 있는 곳이다(setuptools에 대해서는 부록 A를 참조하라).이 경로 중에서 처음 발견되는 모듈이 반입된다. 일단 모듈을 찾으면 더 이상 검색을 하지 않는다.

>>> import sys
>>> sys.path.append("/Users/frank/lib/mysql-connector-java-5.1.6.jar")
>>> import com.mysql
*sys-package-mgr*: processing new jar,
'/Users/frank/lib/mysql-connector-java-5.1.6.jar'
>>> dir(com.mysql)
['__name__', 'jdbc']

이 예제에서, mysql jar를 sys.path에 추가한 다음, com.mysql을 찾으려고 시도하여, jar를 찾아내었다. ‘com.mysql’은 mysql-connector-java-5.1.6.jar에서 찾은 자바 패키지이다.

자바 패키지 탐색

다음과 같이 ClassLoader에 알려진 모든 패키지의 목록을 자바 SDK에 요구할 수도 있다.

java.lang.ClassLoader#getPackages()

아무 응답이 없다.

java.lang.Package#getClasses()

이는 자이썬에 있어서 불행한 일이다. 자이썬 사용자는 코드에 대한 인트로스펙트(introspect)라는 강력한 방법을 활용할 수 있기를 바라기 때문이다. 예컨대, 자바 패키지에 대하여 dir()을 사용하여 무엇이 있는지 알고 싶어한다.

>>> import java.util.zip
>>> dir(java.util.zip)
['Adler32', 'CRC32', 'CheckedInputStream', 'CheckedOutputStream',
'Checksum', 'DataFormatException', 'Deflater',
'DeflaterOutputStream', 'GZIPInputStream', 'GZIPOutputStream',
'Inflater', 'InflaterInputStream', 'ZipEntry', 'ZipException',
'ZipFile', 'ZipInputStream', 'ZipOutputStream', '__name__']

자바 클래스에 대해서도 마찬가지이다.

>>> import java.util.zip
>>> dir(java.util.zip.ZipInputStream)
['__class__', '__delattr__', '__doc__', '__eq__',
'__getattribute__', '__hash__', '__init__',
'__ne__', '__new__', '__reduce__',
'__reduce_ex__', '__repr__', '__setattr__',
'__str__', 'available', 'class', 'close', 'closeEntry',
'equals', 'getClass', 'getNextEntry', 'hashCode', 'mark',
'markSupported', 'nextEntry', 'notify', 'notifyAll', 'read',
'reset', 'skip', 'toString', 'wait']

병합된 이름 공간에 대하여 이런 식으로 인트로스펙션을 가능하게 하려면 자이썬이 시작될 때(또한 jar 혹은 클래스가 자이썬의 실행 중에 path에 추가될 때) 많은 부하가 일어나게 된다. 자이썬을 새로 설치해본 적이 있다면 이 시스템이 동작하는 증거를 눈치챘을 것이다.

*sys-package-mgr*: processing new jar, '/Users/frank/jython/jython.jar'
*sys-package-mgr*: processing new jar, '/System/Library/Frameworks/JavaVM.framework/Versions/1.5.0/Classes/classes.jar'
*sys-package-mgr*: processing new jar, '/System/Library/Frameworks/JavaVM.framework/Versions/1.5.0/Classes/ui.jar'
*sys-package-mgr*: processing new jar,  '/System/Library/Frameworks/JavaVM.framework/Versions/1.5.0/Classes/laf.jar'
...
*sys-package-mgr*: processing new jar, '/System/Library/Frameworks/JavaVM.framework/Versions/1.5.0/Home/lib/ext/sunjce_provider.jar'
*sys-package-mgr*: processing new jar, '/System/Library/Frameworks/JavaVM.framework/Versions/1.5.0/Home/lib/ext/sunpkcs11.jar'

이는 여러분의 JVM에서 사용 가능한 패키지 및 클래스에 대한 내부적 표현을 구축하기 위하여자이썬이 찾을 수 있는 모든 jar 파일을 탐색하는 것이다. 이것은 자이썬이 새로 설치되고 처음 기동할 때 너무 느려지게 만드는 부작용이다.

Jar와 클래스 탐색

자이썬이 jar와 클래스를 찾기 위하여 사용하는 두 가지 속성이 있다. 이 값들은 자이썬의 명령행 혹은 레지스트리를 통하여 설정한다(부록 A 참조). 그 두 가지 속성은 다음과 같다.

python.packages.paths
python.packages.directories

이 속성들은 레지스트리 항목에 대하여 반점(,)으로 구분한 목록이며 실제로 스캐너가 목록을 구축하기 위해 사용할 값을 표함한다. 이러한 값들은 수정하지 않도록 한다. 이러한 속성들이 가리키는 속성은 보다 흥미롭다. 두 속성이 잠재적으로 조작하는 것은 다음과 같은 것들이다.

java.class.path
java.ext.dirs

java.class.path 속성에서는, 운영체제의 classpath와 같은 방식으로 항목이 구분된다(윈도우에서는 ‘;’를, 그 외의 시스템에서는 ‘:’를 구분자로 사용한다). 이러한 경로 각각은 .jar 또는 .zip에 대하여 체크되며 이러한 접미어를 갖고 있을 경우 스캔된다.

java.ext.dirs 속성에 대하여는, 항목은 java.class.path와 동일한 방식으로 구분되지만 각 항목은 디렉토리를 나타낸다. 디렉토리들은 .jar나 .zip로 끝나거나, 또는 찾을 수 있는 어떤 파일이라도 있으면 검색된다.

스캔되는 jar를 제어하기 위해서는 이러한 속성들의 값을 지정할 필요가 있다. 이러한 속성 값을 지정하는 여러 가지 방법에 대해서는 부록 A를 참조하기 바란다.

전체 클래스 들여오기만 사용한다면, 패키지 탐색을 건너뛸 수 있다. 시스템 속성 python.cachedir.skip을 참으로 하거나 (다시) 여러분의 postProperties에서 pass하여 끌 수 있다.

컴파일

자이썬은 ‘번역(interpret)되는 것이지, 컴파일되지는 않는다’는 것이 일반적이 믿음이지만, 모든 자이썬 코드는 실행 전에 자바 바이트 코드로 변환된다. 이러한 자바 바이트 코드는 항상 디스크에 저장되는 것은 아니지만, 자이썬이 그 어떤 코드라도, 심지어는 eval이나 exec 같은 것도, JVM에 바이트 코드가 올라간다는 것은 확실하다. 유일한 예외는 나중에 sys.meta_path에 대한 절에 나오는 실험적인 pycimport 모듈인데, 이것은 자바 바이트 코드를 만들어내는 대신에 CPython 바이트 코드를 해석한다.

파이썬 모듈 및 패키지 vs. 자바 패키지

파이썬의 모듈과 패키지를 들여오는 기본적인 의미에 대비하여 자바 패키지를 자이썬으로 들여오는 것은 항상 마음에 새겨야 할 중요한 차이점이 있다.

sys.path

자이썬에서 모듈을 들여오고자 할 때, 앞 절에서 설명한 것과 같이 모듈을 찾을 때까지 sys.path를 탐색한다. 찾은 것이 파이썬 모듈이나 패키지일 경우엔는, 이러한 들여오기는 ‘승자독식’이 된다. 이는, 처음 import되는 파이썬 모듈 또는 패키지가 그 이후에 찾을 수도 있을 다른 모듈이나 패키지를 막아버린다는 것이다. bar를 갖고 있는 foo 모듈이 sys.path의 앞쪽에 있고, 뒤쪽에 baz를 갖고 있는 foo 모듈이 또 있다면, ‘import foo’의 결과는 foo.baz가 아니라 foo.bar만 얻게 됨을 의미한다.

이는 자이썬에서 자바 패키지를 들여오는 경우와는 다르다. 만약에 bar를 포함하는 자바 패키지 org.foo가 있는데, 나중에 그 경로에 baz를 갖는 자바 패키지 org.foo가 있다면, ‘import org.foo’를 실행하게 되면 두 이름공간을 병합하는 결과를 초래하여 org.foo.bar와 org.foo.baz 둘 다를 갖게 될 것이다.

염두에 두어야 할 중요한 점은, 만약 여러분의 경로에 있는 특정한 이름의 파이썬 모듈 또는 패키지가 여러분의 경로에 있는 자바 패키지와 충돌을 일으킨다면 승자독식과 같은 효과가 발생한다는 것이다. 자바 패키지가 경로에서 처음 나온다면, 그 이름이 병합된 자바 패키지에 바인드될 것이다. 만약에 파이썬 모듈 또는 패키지가 이긴다면, 더 이상 탐색이 이루어지지 않고, 이름이 충돌하는 그 자바 패키지는 결코 찾을 수 없을 것이다.

파이썬 모듈 및 패키지 이름 짓기

자바 쪽에서 온 개발자들은 자이썬 패키지 구조를 자바 패키지와 동일하게 구성하려는 실수를 하곤 한다. 그렇게 해서는 안된다. 자바의 url 역행 관습은 훌륭한 것이며, 자바에 있어서는 훌륭한 규정이라고 할 만하다. 이러한 이름 공간이 병합되는 자바 세계에서는 실제로 아주 잘 작동한다. 그렇지만 모듈과 패키지가 승자독식의 의미를 표시하는 파이썬의 세계에서는, 코드 관리에 좋은 방법이 못 된다.

그런 스타일을 파이썬에 적용한다면, 예컨대 ‘acme.com’을 사용하려고 하면 패키지 구조를 ‘com.acme’와 같이 만들어야 할 것이다. 만약 xyz라는 회사의 라이브러리를 쓰기 위해 ‘com.xyz’와 같이 구성한다면, 경로에서 처음 나오는 것이 ‘com’ 이름 공간을 가져버리므로 다른 패키지들은 볼 수가 없게 된다.

파이썬에 적합한 명명법

파이썬의 관례는 모듈이나 패키지에 대하여, 가능한 이름 공간을 얇게 가져가면서, 최상위 이름 공간은 고유성을 충분히 갖도록 하는 것이다. acme와 xyz 회사의 경우에, 전체 코드 기반을 하나의 이름 공간으로 구성하기를 원한다면 패키지 구조를 ‘acme’와 ‘xyz’로 시작하면 될 것이다. (꼭 그렇게 할 필요는 없다. 일반적으로는 조직을 기준으로 하기보다는 제품을 기준으로 하는 것이 낫다.)

Note

모듈 또는 패키지를 명명하는 데에 있어서 특히 나쁜 두 가지 경우가 있다. 첫번째로, 최상위 도메인에 org, com, net, us, name 같은 것을 사용하는 경우이다. 두번째는 자바 언어에서 최상위 이름 공간으로 예약하고 있는 java, javax 등이다.

고급 Import 조작

이 절에서는 들여오기의 내부적인 작동 원리를 이용한 몇 가지 고급 도구를 설명하려고 한다. 이러한 것들은 상당히 고급 기술에 해당하며 필요할 일이 거의 없지만, 반드시 필요한 경우가 있을 수도 있다.

Import 훅

자이썬이 자바 클래스를 들여오는 방식을 이해하기 위해서는 파이썬의 들여오기 규정에 대한 이해가 선행되어야 한다. 여기서 세세하게 다루지는 않으니 PEP 302 http://www.python.org/dev/peps/pep-0302/를 참조하기 바란다.

요약해서, sys.meta_path에 등록된 맞춤 importer들을 먼저 시도한다. 그것들 중에서 필요한 모듈을 가져올 능력이 되는 것이 있는 경우에는 그렇게 하도록 한다. 다음으로는 sys.path에 있는 항목들을 시도해본다. 이들 각각에 대하여, 경록 항목을 처리할 수 있는, sys.path_hooks에 등록된 첫번째 훅(hook)을 찾는다. 만약에 import 훅을 찾아서 모듈 들여오기에 성공하면 거기서 멈춘다. 그렇지 못할 경우에는 내장된 들여오기 로직을 시도한다. 그 또한 실패한다면 ImportError를 던진다. 자이썬의 path_hooks를 살펴보자.

sys.path_hooks

>>> import sys
>>> sys.path_hooks
[<type 'org.python.core.JavaImporter'>, <type 'zipimport.zipimporter'>,
<type 'ClasspathPyImporter'>]

이들 path_hooks 항목 각각은 특수한 파일의 들여오기를 시도하기 위한 path_hook을 기술한다. JavaImporter는, 그 이름에서 알 수 있듯이, 실행시점에 자바 패키지과 클래스를 동적으로 적재할 수 있도록 해준다. 예를 들어, 실행시점에 jar를 포함시키고 싶다면 다음과 같은 코드를 실행할 수 있다.

>>> import sys
>>> sys.path.append("mysql-connector-java-5.1.6.jar")
>>> import com.mysql
*sys-package-mgr*: processing new jar, 'mysqlconnector-java-5.1.6.jar'
>>> dir(com.mysql)
['__name__', 'jdbc']

‘com.mysql’이 import되면 패키지 탐색이 시작되는데, 이는 sys-package-mgr로 시작하는 행을 보면 알 수 있다. import 도중에, JavaImporter가 새로운 jar를 탐색하여 성공적으로 들여올 수 있도록 하였다.

sys.meta_path

sys.meta_path에 항목을 추가함으로써, import를 할 때 어떤 일이 일어나도록 습성을 추가할 수 있다. 이는 기본 내장 import에 대해서도 그러하다. 이는 매우 강력한 도구로서 온갖 흥미로운 일들이 가능하게 해준다. 예를 들기 위하여, 자이썬 2.5에 탑재된 시험적인 모듈에 대한 이야기를 하여야겠다. pycimport라는 모듈이다. 자이썬을 기동하여 다음과 같은 명령을 내리면,

>>> import pycimport

자이썬은 경로에서 .pyc 파일을 찾기 시작하며, 뭔가 찾아내면 그 .pyc 파일을 module.pyc 파일에 적재한다. 이 파일들은 CPython이 파이썬 소스 코드를 컴파일하면 만들어지는 것이다. 따라서, pycimport을 들여오고 (which adds a hook to sys.meta_path) 다음 명령을 내리면,

>>> import foo

자이썬은 foo.pyc라는 이름의 파일을 탐색하고, 찾은 경우에는 CPython 바이트 코드를 사용하여 foo 모듈을 들여오게 된다. 이것은 pyc 파일을 어떻게 적재할 것인지 기술하는 find_module 메소드를 정의하는 특수한 클래스에 의하여 이루어진다. 그런 다음 이 클래스는 sys.meta_path.insert 메소드와 함께 메타 검색 경로에 추가된다. find_module 메소드는 pycimport의 다른 부분으로 호출되어 .pyc 파일을 찾는다. .pyc 파일을 찾으면, 어떻게 구문분석을 하여 실행시킬 것인지을 알고 있어서 실행시점에 관련 모듈을 추가한다. 제법 쿨하지 않은가?

요약

이번 장에서는, 조직화와 재사용을 위하여 코드를 모듈로 나누는 방법을 배웠다. 모듈과 패키지를 작성하는 방법과, 자이썬 시스템이 자바 클래스 및 패키지와 어떻게 상호작용하는지를 배웠다. 이것으로 1부를 마치도록 하겠다. 자이썬 언어의 기본을 다루었으므로 이제 자이썬을 어떻게 사용하는지 배울 것이다.