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
- 동기
- 의도
- 문법
- 의미론
- 예제
- 표준 모듈 __future__.py
- 해결된 문제: 런타임 컴파일
- 해결된 문제: 네이티브 대화형 셸
- 해결된 문제: 시뮬레이션된 대화형 셸
- 질문과 답변
- 옛 동작을 되돌리기 위한 “from __past__” 버전은 어떻습니까?
- 파이썬 가상 머신 변경으로 인한 비호환성은 어떻습니까?
- 파이썬 C API 변경으로 인한 비호환성은 어떻습니까?
- future_statements를 try/except 블록으로 감싸서 실행 중인 파이썬 버전에 따라 다른 코드를 사용하고 싶습니다. 왜 안 될까요?
- nested_scopes 예제로 돌아가서, 릴리스 2.2가 나왔는데 제가 아직 코드를 바꾸지 않았다면 어떻게 될까요? 그럴 때 2.1 동작을 유지하려면 어떻게 해야 할까요?
import를 오버로딩하는 것은 별로입니다. 이를 위해 새로운 문장을 도입하지 않는 이유는 무엇일까요?
- Copyright
- References and Footnotes
번역·라이선스 안내
이 비공식 한국어 번역은 원문 Copyright 절의 Public Domain 조건에 따라 제공합니다. 원저자와 공식 원문은 그대로 표시합니다. 수정되지 않은 기준 원문 · 공식 최신판
동기
Python은 이따금 핵심 언어 구성체의 공표된 의미에 호환되지 않는 변경을 가하거나, 그 우발적인(구현에 의존하는) 동작을 어떤 식으로든 변경합니다. 이는 결코 임의로 이루어지는 것이 아니며 항상 장기적으로 언어를 개선하려는 목적으로 이루어지지만, 단기적으로는 논쟁을 일으키고 혼란을 초래합니다.
PEP 5, Guidelines for Language Evolution은 이러한 고통을 완화할 방법을 제안하며, 이 PEP는 이를 뒷받침하는 몇 가지 장치를 도입합니다.
PEP 227, Statically Nested Scopes가 첫 번째 적용 사례이며, 여기서는 예시로 사용하겠습니다.
의도
[참고: 이는 정책이므로 결국 PEP 5로 옮겨져야 합니다]
핵심 언어의 구문이나 의미에 호환되지 않는 변경이 이루어질 경우, 다음과 같습니다:
- 이 변경을 도입하는 릴리스 C는 기본적으로 구문이나 의미를 변경하지 않습니다.
- 새 구문이나 의미가 강제될 미래 릴리스 R이 지정됩니다.
- 경고 프레임워크인 PEP 230에서 설명한 메커니즘은 가능한 경우, 릴리스 R에서 의미가 [1] 변경될 수 있는 구문이나 연산에 대한 경고를 생성하는 데 사용됩니다.
- 새로운 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에서는, 그리고 2.1에서도 scope.py의 맨 위에 from __future__ import nested_scopes가 포함되어 있는 경우에는, 다음과 같이 출력됩니다.:
x is 666
표준 모듈 __future__.py
Lib/__future__.py는 실제 모듈이며, 세 가지 목적을 수행합니다:
- import 문을 분석하여 임포트하려는 모듈을 찾으려는 기존 도구들에 혼란을 주지 않기 위함입니다.
- future_statement가 2.1 이전 릴리스에서 실행되면 최소한 런타임 예외를 발생시키도록 보장하기 위함입니다(2.1 이전에는 그런 이름의 모듈이 없었기 때문에
__future__의 임포트는 실패합니다). - 호환되지 않는 변경이 언제 도입되었는지, 그리고 그것이 언제 필수가 될 것인지(또는 되었는지)를 문서화하기 위함입니다. 이것은 실행 가능한 문서의 한 형태이며,
__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
허용된 릴리스를 기록합니다.
아직 발생하지 않은 MandatoryRelease의 경우, 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 기능은 모듈의 런타임 중에 코드를 컴파일할 수 있습니다:
exec문.execfile()함수.compile()함수.eval()함수.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는 표현식이 아니기 때문입니다.
해결된 문제: 네이티브 대화형 셸
대화형 셸을 얻는 방법에는 두 가지가 있습니다:
- 스크립트 인자 없이 명령줄에서 Python을 호출하는 방법입니다.
- 스크립트 인자와 함께
-i스위치를 사용하여 명령줄에서 Python을 호출하는 방법입니다.
대화형 셸은 런타임 컴파일(위 참조)의 극단적인 경우로 볼 수 있습니다: 실제로 대화형 셸 프롬프트에 입력된 각 문장은 exec, compile() 또는 execfile()의 새 인스턴스를 실행합니다. 대화형 셸에 입력된 future_statement는, 마치 모듈 맨 위에 등장한 것처럼, 셸 세션의 남은 기간 동안 적용됩니다.
해결된 문제: 시뮬레이션된 대화형 셸
“직접 만든”(IDLE이나 Emacs Python-mode 같은 도구로 만든) 대화형 셸은 네이티브 대화형 셸(위 참조)처럼 동작해야 합니다. 그러나 네이티브 대화형 셸이 내부적으로 사용하는 메커니즘은 노출되어 있지 않으며, 자체 대화형 셸을 만드는 도구가 원하는 동작을 달성할 수 있는 명확한 방법도 없습니다.
참고: PEP 264는 표준 codeop.py에 지능을 추가함으로써 이 문제를 나중에 해결했습니다. 표준 라이브러리 셸 헬퍼를 사용하지 않는 시뮬레이션된 셸은 PEP 264가 추가한 compile()의 새로운 선택적 인자를 활용하여 비슷한 효과를 얻을 수 있습니다.
질문과 답변
옛 동작을 되돌리기 위한 “from __past__” 버전은 어떻습니까?
이 PEP의 범위를 벗어납니다. 다만 저자에게는 그럴 가능성이 낮아 보입니다. 추진하고 싶다면 PEP를 작성하십시오.
파이썬 가상 머신 변경으로 인한 비호환성은 어떻습니까?
이 PEP의 범위를 벗어나지만, PEP 5도 마찬가지로 유예 기간을 제안하며, future_statement도 그곳에서 역할을 할 수 있습니다.
파이썬 C API 변경으로 인한 비호환성은 어떻습니까?
이 PEP의 범위를 벗어납니다.
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% 하위 호환성을 가지고 있습니다.
Copyright
This document has been placed in the public domain.