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

Python 개선 제안 한국어 번역

PEP 236 – __future__로 돌아가기

Author:
Tim Peters <tim.peters at gmail.com>
Status:
Final
Type:
Standards Track
Created:
26-Feb-2001
Python-Version:
2.1
Post-History:
26-Feb-2001

Table of Contents

번역·라이선스 안내

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

동기

Python은 때때로 핵심 언어 구성 요소에 대해 공표된 의미를 호환되지 않게 변경하거나, 그 구성 요소의 우발적인(구현에 따라 달라지는) 동작을 어떤 식으로든 변경합니다. 이러한 변경은 결코 변덕스럽게 이루어지지 않으며 언제나 장기적으로 언어를 개선하려는 목적으로 이루어지지만, 단기적으로는 논란을 일으키고 혼란을 초래합니다.

PEP 5, 언어 발전 지침은 이러한 고통을 덜어 줄 방법을 제안하며, 이 PEP는 이를 뒷받침하는 몇 가지 장치를 도입합니다.

PEP 227, 정적으로 중첩된 스코프가 첫 번째 적용 사례이며, 여기에서 예시로 사용합니다.

의도

[참고: 이것은 정책이므로 결국 PEP 5로 옮겨져야 합니다]

핵심 언어 문법이나 의미에 호환되지 않는 변경이 이루어질 때에는 다음과 같이 합니다.

  1. 변경을 도입하는 릴리스 C는 기본적으로 문법이나 의미를 변경하지 않습니다.
  2. 새 문법이나 의미가 강제될 향후 릴리스 R을 지정합니다.
  3. PEP 230, 경고 프레임워크에 설명된 메커니즘을 사용하여, 릴리스 R에서 의미가 바뀔 수 있는 [1] 구성 요소나 연산에 대해 가능한 한 경고를 생성합니다.
  4. 새로운 future_statement(아래 참조)를 모듈 M에 명시적으로 포함하여, 모듈 M의 코드가 현재 릴리스 C에서 새 문법이나 의미를 사용하도록 요청할 수 있습니다.

따라서 기존 코드는 새로운 경고 메시지를 생성하기 시작할 수는 있지만, 적어도 한 릴리스 동안은 기본적으로 계속 동작합니다. 그 기간 동안 새 문법이나 의미로의 이전을 진행할 수 있으며, future_statement를 사용하면 이를 포함한 모듈이 마치 새 문법이나 의미가 이미 강제되고 있는 것처럼 동작하게 됩니다.

기존 코드를 깨뜨릴 수 있는 경우가 아니라면 새 기능에 future_statement 장치를 끌어들일 필요가 없다는 점에 유의하십시오. 하위 호환성을 완전히 유지하는 추가 사항은 대응하는 future_statement 없이 도입할 수 있으며, 또 그렇게 도입해야 합니다.

문법

future_statement는 예약된 모듈 이름 __future__를 사용하는 from/import 문일 뿐입니다:

future_statement: "from" "__future__" "import" feature ["as" name]
                  (","feature ["as" name])*

feature: identifier
name: identifier

또한 모든 future_statement는 모듈의 맨 위 가까이에 나타나야 합니다. future_statement 앞에 올 수 있는 줄은 다음뿐입니다.

  • 모듈 독스트링(있는 경우).
  • 주석.
  • 빈 줄.
  • 다른 future_statement.

예시:

"""This is a module docstring."""

# This is a comment, preceded by a blank line and followed by
# a future_statement.
from __future__ import nested_scopes

from math import sin
from __future__ import alabaster_weenoblobs  # compile-time error!
# That was an error because preceded by a non-future_statement.

의미론

future_statement는 컴파일 시간에 인식되어 특별하게 처리됩니다. 핵심 구성 요소의 의미 변경은 흔히 다른 코드를 생성하는 방식으로 구현되기 때문입니다. 심지어 새 기능이 호환되지 않는 새 문법(예를 들어 새 예약어)을 도입하는 경우도 있을 수 있으며, 이 경우 컴파일러는 모듈을 다르게 구문 분석해야 할 수도 있습니다. 이러한 결정은 런타임까지 미룰 수 없습니다.

각 릴리스마다 컴파일러는 어떤 기능 이름이 정의되어 있는지 알고 있으며, future_statement에 자신이 알지 못하는 기능이 들어 있으면 컴파일 시간 오류를 발생시킵니다 [2].

직접적인 런타임 의미론은 다른 import 문과 같습니다. 뒤에서 설명할 표준 모듈 __future__.py가 있으며, 이 모듈은 future_statement가 실행될 때 일반적인 방식으로 임포트됩니다.

흥미로운 런타임 의미론은 모듈에 나타나는 future_statement(들)가 “임포트”하는 특정 기능(들)에 따라 달라집니다.

다음 문에는 특별한 점이 없다는 것에 유의하십시오.:

import __future__ [as name]

이것은 future_statement가 아닙니다. 특별한 의미나 문법 제약이 없는 평범한 import 문입니다.

예시

scope.py 파일에 있는 다음 코드를 살펴보십시오.:

x = 42
def f():
    x = 666
    def g():
        print "x is", x
    g()
f()

2.0에서는 다음을 출력합니다.:

x is 42

중첩 스코프(PEP 227)가 2.1에서 도입됩니다. 하지만 2.1에서도 여전히 다음을 출력하며:

x is 42

경고도 함께 발생시킵니다.

2.2에서, 그리고 scope.py 맨 위에 from __future__ import nested_scopes가 포함되어 있는 경우에는 2.1에서도 다음을 출력합니다.:

x is 666

표준 모듈 __future__.py

Lib/__future__.py는 실제 모듈이며, 세 가지 목적을 수행합니다:

  1. import 문을 분석하면서 임포트하는 모듈을 찾을 수 있으리라 기대하는 기존 도구들을 혼란스럽게 하지 않기 위함입니다.
  2. 2.1 이전 릴리스에서 실행되는 future_statement가 최소한 런타임 예외는 발생시키도록 보장하기 위함입니다(2.1 이전에는 그런 이름의 모듈이 없었으므로 __future__의 임포트가 실패합니다).
  3. 호환되지 않는 변경이 언제 도입되었는지, 그리고 언제 필수가 될지 또는 언제 필수가 되었는지를 문서화하기 위함입니다. 이것은 실행 가능한 문서의 한 형태이며, __future__를 임포트하고 그 내용을 살펴봄으로써 프로그램적으로 검사할 수 있습니다.

__future__.py의 각 문은 다음과 같은 형식입니다.:

FeatureName = "_Feature(" OptionalRelease "," MandatoryRelease ")"

여기서 일반적으로 OptionalRelease < MandatoryRelease이며, 둘 다 sys.version_info와 같은 형식의 5-튜플입니다.:

(PY_MAJOR_VERSION, # the 2 in 2.1.0a3; an int
 PY_MINOR_VERSION, # the 1; an int
 PY_MICRO_VERSION, # the 0; an int
 PY_RELEASE_LEVEL, # "alpha", "beta", "candidate" or "final"; string
 PY_RELEASE_SERIAL # the 3; an int )

OptionalRelease는 다음 문이:

from __future__ import FeatureName

처음으로 허용된 릴리스를 기록합니다.

아직 도래하지 않은 MandatoryReleases의 경우, MandatoryRelease는 해당 기능이 언어의 일부가 될 릴리스를 예측합니다.

그렇지 않으면 MandatoryRelease는 해당 기능이 언어의 일부가 된 시점을 기록합니다. 그 릴리스와 이후 릴리스에서 모듈은 다음 임포트 없이도:

from __future__ import FeatureName

해당 기능을 사용할 수 있지만, 그러한 임포트를 계속 사용해도 됩니다.

MandatoryRelease는 None일 수도 있으며, 이는 계획되었던 기능이 철회되었음을 의미합니다.

class _Feature의 인스턴스에는 이에 대응하는 두 메서드 .getOptionalRelease()와 .getMandatoryRelease()가 있습니다.

__future__.py에서 기능 줄이 삭제되는 일은 결코 없습니다.

예제 줄:

nested_scopes = _Feature((2, 1, 0, "beta", 1), (2, 2, 0, "final", 0))

이것은 다음 문이:

from __future__ import nested_scopes

2.1b1 이후의 모든 릴리스에서 동작하며, nested_scopes가 릴리스 2.2부터 강제될 예정임을 의미합니다.

해결된 문제: 런타임 컴파일

여러 Python 기능은 모듈의 런타임 중에 코드를 컴파일할 수 있습니다:

  1. exec 문.
  2. execfile() 함수.
  3. compile() 함수.
  4. eval() 함수.
  5. input() 함수.

기능 F를 지정하는 future_statement를 포함한 모듈 M은 F에 관해 현재 릴리스가 향후 릴리스처럼 동작하도록 명시적으로 요청하는 것이므로, M 내부에서 이들 중 하나에 전달된 텍스트로부터 동적으로 컴파일되는 코드도 아마 F와 연관된 새 문법이나 의미 체계를 사용해야 할 것입니다. 2.1 릴리스는 실제로 이렇게 동작합니다.

하지만 이것이 항상 바람직한 것은 아닙니다. 예를 들어, doctest.testmod(M)은 M 안의 문자열에서 가져온 예제를 컴파일하는데, 이 예제들은 doctest 모듈의 선택이 아니라 M의 선택을 따라야 합니다. 2.1 릴리스에서는 이것이 불가능하며, 이를 우회하기 위한 방안도 아직 제안된 바가 없습니다. 참고: 이후 PEP 264가 compile()에 선택적 인자를 추가하여 이 문제를 유연한 방식으로 해결했습니다.

어떤 경우든, exec, execfile() 또는 compile()에 의해 동적으로 컴파일되는 텍스트의 “맨 위 근처”(위의 문법 참조)에 나타나는 future_statement는 생성되는 코드 블록에 적용되지만, 그러한 exec, execfile() 또는 compile()을 실행하는 모듈에는 더 이상 영향을 미치지 않습니다. 그러나 이것을 eval()이나 input()에 영향을 주는 데 사용할 수는 없습니다. 이들은 표현식 입력만 허용하는데, future_statement는 표현식이 아니기 때문입니다.

해결된 문제: 네이티브 대화형 셸

대화형 셸을 얻는 방법은 두 가지입니다:

  1. 스크립트 인자 없이 명령줄에서 Python을 실행하는 방법입니다.
  2. 명령줄에서 -i 스위치와 스크립트 인자를 함께 지정하여 Python을 실행하는 방법입니다.

대화형 셸은 런타임 컴파일(위 참조)의 극단적인 경우로 볼 수 있습니다: 사실상 대화형 셸 프롬프트에 입력되는 각 문장은 exec, compile() 또는 execfile()의 새 인스턴스를 실행합니다. 대화형 셸에 입력된 future_statement는 마치 그 future_statement가 모듈의 맨 위에 나타난 것처럼 셸 세션의 남은 기간 동안 적용됩니다.

해결된 문제: 시뮬레이션된 대화형 셸

(IDLE이나 Emacs Python-mode 같은 도구가) “직접 만든” 대화형 셸은 네이티브 대화형 셸(위 참조)처럼 동작해야 합니다. 그러나 네이티브 대화형 셸이 내부적으로 사용하는 메커니즘은 공개되어 있지 않으며, 자체 대화형 셸을 만드는 도구가 원하는 동작을 달성할 명확한 방법도 없습니다.

참고: 이후 PEP 264가 표준 codeop.py에 지능을 추가하여 이 문제를 해결했습니다. 표준 라이브러리의 셸 도우미를 사용하지 않는 시뮬레이션된 셸은 PEP 264에서 추가된 compile()의 새로운 선택적 인자를 활용하여 비슷한 효과를 얻을 수 있습니다.

질문과 답변

옛 동작을 되돌리기 위한 “from __past__” 버전은 어떻습니까?

이 PEP의 범위를 벗어납니다. 다만 저자가 보기에는 가능성이 낮아 보입니다. 추진하고 싶다면 PEP를 작성하십시오.

Python 가상 머신의 변경으로 인한 비호환성은 어떻습니까?

이 PEP의 범위를 벗어납니다. 다만 PEP 5가 그 경우에도 유예 기간을 제안하고 있으며, future_statement도 거기에서 역할을 할 수 있습니다.

Python C API의 변경으로 인한 비호환성은 어떻습니까?

이 PEP의 범위를 벗어납니다.

실행 중인 Python 버전에 따라 다른 코드를 사용할 수 있도록 future_statements를 try/except 블록으로 감싸고 싶습니다. 왜 그렇게 할 수 없습니까?

죄송합니다! try/except는 런타임 기능이지만 future_statements는 주로 컴파일 타임의 장치이며, 여러분의 try/except는 컴파일러가 작업을 마치고 한참 뒤에야 실행됩니다. 즉, try/except를 실행하는 시점에는 모듈에 적용되는 의미론이 이미 확정된 상태입니다. try/except는 해낼 것처럼 보이는 일을 실제로는 해내지 못하므로, 아예 허용하지 않습니다. 또한 저희는 이러한 특수 문장을 아주 쉽게 찾고 알아볼 수 있도록 유지하고 싶습니다.

다만 __future__를 직접 임포트하고 그 안의 정보를 sys.version_info와 함께 사용하여, 현재 실행 중인 릴리스가 특정 기능의 상태와 비교해 어디쯤에 있는지 알아내는 것은 가능하다는 점에 유의하십시오.

nested_scopes 예제로 돌아가서, 릴리스 2.2가 나왔는데 제가 아직 코드를 바꾸지 않았다면 어떻게 됩니까? 그때는 어떻게 2.1의 동작을 유지할 수 있습니까?

2.1을 계속 사용하고, 코드를 실제로 바꿀 때까지 2.2로 옮겨 가지 않으면 됩니다. future_statement의 목적은 최신 릴리스를 제때 따라가는 사람들이 더 편하게 지낼 수 있도록 하는 것입니다. 그렇게 하지 않는다고 해서 저희가 여러분을 미워하지는 않지만, 여러분의 문제는 훨씬 더 풀기 어려우며, 그런 문제를 가진 누군가가 이를 다루는 PEP를 작성해야 할 것입니다. future_statement는 다른 사용자층을 대상으로 합니다.

import를 중복 정의하는 것은 형편없습니다. 이를 위해 새 문장을 도입하면 되지 않습니까?

예를 들어 lambda lambda nested_scopes 같은 것 말입니까? 즉, 새 키워드를 도입하지 않는 한 완전히 새로운 문장을 도입할 수는 없습니다. 하지만 새 키워드를 도입하면, 그 자체로 기존 코드가 깨집니다. 그것은 견디기 힘들 만큼 아이러니한 일일 것입니다. 네, import를 중복 정의하는 것은 분명 형편없지만, 다른 대안들만큼 심하지는 않습니다 – 현재 방식대로라면 future_statements는 100% 하위 호환성을 유지합니다.

References and Footnotes