Following system colour scheme Selected dark colour scheme Selected light colour scheme

Python 개선 제안 한국어 번역

PEP 328 – 임포트: 여러 줄 임포트와 절대/상대 임포트

Author:
Aahz <aahz at pythoncraft.com>
Status:
Final
Type:
Standards Track
Created:
21-Dec-2003
Python-Version:
2.4, 2.5, 2.6
Post-History:
08-Mar-2004

Table of Contents

번역·라이선스 안내

이 비공식 한국어 번역은 원문 Copyright 절의 Public Domain 조건에 따라 제공합니다. 원저자와 공식 원문은 그대로 표시합니다. 수정되지 않은 기준 원문 · 공식 최신판

초록

import 문에는 두 가지 문제가 있습니다:

  • 긴 import 문은 작성하기 어려울 수 있으며, 파이썬다운 스타일 지침에 맞추려면 여러 가지 억지스러운 방법을 써야 합니다.
  • 패키지가 있으면 임포트가 모호해질 수 있습니다. 패키지 안에서 import foo가 패키지 내부의 모듈을 가리키는지 패키지 외부의 어떤 모듈을 가리키는지 분명하지 않습니다. (더 정확히 말하면, 로컬 모듈이나 패키지가 sys.path 바로 아래에 있는 다른 모듈이나 패키지를 가릴 수 있습니다.)

첫 번째 문제에 대해서는 괄호로 여러 이름을 감쌀 수 있도록 허용하여, 여러 줄에 걸친 값에 적용되는 Python의 표준 메커니즘을 사용할 수 있게 하자고 제안합니다. 두 번째 문제에 대해서는 모든 import 문을 기본적으로 절대 임포트(sys.path만 검색)로 만들고, 패키지 상대 임포트에 접근하기 위한 특별한 문법(앞에 붙는 점)을 두자고 제안합니다.

타임라인

Python 2.5에서는 다음을 사용하여 새로운 절대 임포트 동작을 활성화해야 합니다

from __future__ import absolute_import

상대 임포트는 자유롭게 사용할 수 있습니다. Python 2.6에서는 결과적으로 패키지 내부 임포트가 되는 모든 import 문이 DeprecationWarning을 발생시킵니다(상대 임포트 문법을 사용하지 않는 from <> import에도 적용됩니다).

괄호에 대한 근거

현재는 모듈이나 패키지에서 많은 이름을 임포트하려면 달갑지 않은 몇 가지 방법 중 하나를 골라야 합니다:

  • 백슬래시 줄 이음으로 긴 줄을 작성합니다:
    from Tkinter import Tk, Frame, Button, Entry, Canvas, Text, \
        LEFT, DISABLED, NORMAL, RIDGE, END
    
  • import 문을 여러 개 작성합니다:
    from Tkinter import Tk, Frame, Button, Entry, Canvas, Text
    from Tkinter import LEFT, DISABLED, NORMAL, RIDGE, END
    

(import * 방식은 선택지가 아닙니다 ;-)

그 대신, Python의 표준 그룹화 메커니즘(괄호)을 사용하여 import 문을 작성할 수 있어야 합니다:

from Tkinter import (Tk, Frame, Button, Entry, Canvas, Text,
    LEFT, DISABLED, NORMAL, RIDGE, END)

제안의 이 부분은 처음부터 BDFL의 승인을 받았습니다.

괄호 지원은 Python 2.4에 추가되었습니다.

절대 임포트에 대한 근거

Python 2.4 이하에서는 패키지 안에 있는 모듈을 읽을 때, 다음 문장이

import foo

최상위 모듈을 가리키는지 패키지 내부의 다른 모듈을 가리키는지 분명하지 않습니다. Python의 라이브러리가 확장됨에 따라, 점점 더 많은 기존 패키지 내부 모듈이 뜻하지 않게 갑자기 표준 라이브러리 모듈을 가리게 됩니다. 어떤 모듈을 의도한 것인지 지정할 방법이 없기 때문에 패키지 내부에서는 특히 어려운 문제입니다. 이 모호함을 해결하기 위해 foo는 항상 sys.path에서 도달할 수 있는 모듈이나 패키지가 되도록 하자고 제안합니다. 이를 절대 임포트라고 합니다.

python-dev 커뮤니티는 절대 임포트를 기본값으로 선택했습니다. 절대 임포트가 더 흔한 사용 사례이고, 상대(패키지 내부) 임포트의 모든 기능을 제공할 수 있기 때문입니다 – 다만 계층 구조에서 더 위에 있는 패키지 구성 요소의 이름을 바꾸거나 한 패키지를 다른 패키지 안으로 옮길 때 어려움이 따른다는 대가가 있습니다.

이는 의미 체계의 변경이므로, Python 2.5와 2.6에서 절대 임포트는 다음을 사용하여 선택적으로 적용됩니다

from __future__ import absolute_import

제안의 이 부분은 처음부터 BDFL의 승인을 받았습니다.

상대 임포트에 대한 근거

절대 임포트로 전환하면서 상대 임포트를 애초에 허용해야 하는지에 대한 의문이 제기되었습니다. 여러 사용 사례가 제시되었는데, 그중 가장 중요한 것은 하위 패키지를 수정하지 않고도 대규모 패키지의 구조를 재배치할 수 있다는 점입니다. 또한 패키지 내부의 모듈은 상대 임포트 없이는 자기 자신을 쉽게 임포트할 수 없습니다.

Guido는 상대 임포트라는 아이디어를 승인했지만, 표기법(문법)에 대해서는 많은 이견이 있었습니다. 상대 임포트에서는 임포트할 특정 이름을 나열해야 한다는 점(즉, 단독으로 쓰인 import foo는 항상 절대 임포트가 된다는 점)에는 합의가 이루어진 것으로 보입니다.

후보는 다음과 같습니다:

  • Guido가 제안한 것 하나:
    from .foo import bar
    

    그리고

    from ...foo import bar
    

    이 두 형식에는 몇 가지 서로 다른 의미가 제안되었습니다. 한 가지 의미는 각 점이 한 단계를 나타내도록 하는 것입니다. 점의 개수를 세기 어렵다는 불만이 많았습니다. 또 다른 방안은 상대 임포트를 한 단계만 허용하는 것입니다. 이 방안은 많은 기능을 놓치게 되며, 사람들은 점 하나짜리 형식에서도 점을 빠뜨리기 쉽다는 불만을 여전히 제기했습니다. 마지막 방안은 상대 모듈과 패키지를 찾는 알고리즘을 정의하는 것입니다. 이에 대한 반론은 “명시적인 것이 암묵적인 것보다 낫다”는 것입니다. (제안된 알고리즘은 “현재 패키지 디렉터리에서 시작하여 최상위 부모 패키지에 도달할 때까지 위로 검색한다”는 것입니다.)

    일부 사람들은 “-“나 “^” 같은 다른 문장 부호를 구분자로 사용하자고 제안했습니다.

    일부 사람들은 “*”를 사용하자고 제안했습니다:

    from *.foo import bar
    
  • 다음 방안들은 여러 게시자의 제안을 합친 것입니다:
    from __pkg__.__pkg__ import
    

    그리고

    from .__parent__.__parent__ import
    

    많은 사람들(Guido 포함)이 이것들이 보기 흉하다고 생각하지만, 이것들은 분명히 명확하고 명시적입니다. 전반적으로 더 많은 사람들이 더 짧은 방안인 __pkg__를 선호합니다.

  • 한 가지 제안은 형제 참조만 허용하자는 것이었습니다. 다시 말해, 상대 임포트를 사용하여 패키지 트리에서 더 상위에 있는 모듈을 참조할 수는 없게 됩니다. 그러면 다음과 같이 하거나
    from .spam import eggs
    

    또는

    import .spam.eggs
    
  • 일부 사람들은 인덱스로 부모를 지정할 수 있도록 허용하는 방식을 선호합니다:
    from -2.spam import eggs
    

    이 시나리오에서 현재 디렉터리로부터 임포트하는 것은 다음과 같이 간단합니다

    from .spam import eggs
    
  • 마지막으로, 일부 사람들은 패키지 내부를 파고들고 싶을 때 import를 from ... import로 바꿔야 하는 방식을 싫어합니다. 이들은 import 문법을 완전히 새로 작성하자고 제안합니다:
    from MODULE import NAMES as RENAME searching HOW
    

    또는

    import NAMES as RENAME from MODULE searching HOW
        [from NAMES] [in WHERE] import ...
    

    그러나 이는 Python 2.5에서는 구현될 수 없을 가능성이 매우 높으며(변경이 너무 큼), 상대 임포트를 허용하는 것은 충분히 중요하므로 지금 당장 무언가가 필요합니다(표준 import가 절대 임포트로 바뀔 것이기 때문입니다). 그뿐만 아니라, 이 제안된 문법에는 아직 해결되지 않은 몇 가지 문제가 있습니다:

    • 제안된 정확한 문법은 무엇입니까? (어떤 상황에서 어떤 절이 선택 사항입니까?)
    • searching 절은 얼마나 강하게 결합합니까? 다시 말해, 다음과 같이 작성합니까:
      import foo as bar searching XXX, spam as ham searching XXX
      

      아니면:

      import foo as bar, spam as ham searching XXX
      

Guido의 결정

Guido는 상대 임포트에 앞에 붙는 점을 사용하기로 공표했습니다 [1]. 선행 점이 하나이면 현재 패키지에서 시작하는 상대 임포트를 나타냅니다. 선행 점이 둘 이상이면 현재 패키지의 상위 패키지를 기준으로 하는 상대 임포트가 되며, 첫 번째 점 다음부터 점 하나마다 한 단계씩 올라갑니다. 다음은 패키지 구성의 예입니다:

package/
    __init__.py
    subpackage1/
        __init__.py
        moduleX.py
        moduleY.py
    subpackage2/
        __init__.py
        moduleZ.py
    moduleA.py

현재 파일이 moduleX.py 또는 subpackage1/__init__.py라고 가정하면, 다음은 새 문법의 올바른 사용법입니다:

from .moduleY import spam
from .moduleY import spam as ham
from . import moduleY
from ..subpackage1 import moduleY
from ..subpackage2.moduleZ import eggs
from ..moduleA import foo
from ...package import bar
from ...sys import path

마지막 경우는 합법이지만, 확실히 권장되지 않는다는 점에 유의하십시오(Guido가 사용한 단어는 “insane”이었습니다).

상대 임포트는 항상 from <> import를 사용해야 하며, import <>는 항상 절대 임포트입니다. 물론 절대 임포트도 선행 점을 생략하면 from <> import를 사용할 수 있습니다. import .foo를 금지하는 이유는 다음을 실행한 뒤에

import XXX.YYY.ZZZ

그러면

XXX.YYY.ZZZ

표현식에서 사용할 수 있기 때문입니다. 하지만

.moduleY

표현식에서 사용할 수 없습니다.

상대 임포트와 __name__

상대 임포트는 모듈의 __name__ 속성을 사용하여 패키지 계층 구조에서 그 모듈의 위치를 결정합니다. 모듈 이름에 패키지 정보가 전혀 없으면(예를 들어 ‘__main__’으로 설정된 경우), 모듈이 실제로 파일 시스템의 어디에 있는지와 관계없이 상대 임포트는 그 모듈이 최상위 모듈인 것처럼 해석됩니다.

상대 임포트와 sys.modules의 간접 참조 항목

패키지가 도입되면서 sys.modules의 간접 참조 항목이라는 개념이 생겼습니다 [2]. 패키지 안의 모듈에 해당하는 sys.modules 항목의 값이 None이면, 그 모듈이 실제로는 최상위 모듈을 가리킨다는 뜻이었습니다. 예를 들어, sys.modules에서 ‘Sound.Effects.string’의 값이 None일 수 있었습니다. 이는 그 이름으로 해석되는 모든 임포트가 실제로는 최상위 ‘string’ 문자열 모듈을 임포트하는 것임을 의미했습니다.

이 방식은 상대 임포트가 절대 임포트로 해석되어야 하는 경우를 위한 최적화를 도입한 것이었습니다. 하지만 이 PEP는 절대 임포트와 상대 임포트를 매우 명확하게 구분하므로 이 최적화는 더 이상 필요하지 않습니다. 절대/상대 임포트가 유일하게 사용 가능한 임포트 의미 체계가 되면 sys.modules의 간접 참조 항목은 더 이상 지원되지 않을 것입니다.

참고 문헌

더 자세한 배경은 다음 python-dev 스레드를 참조하십시오: