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

Python 개선 제안 한국어 번역

PEP 497 – 하위 호환성을 위한 표준 메커니즘

Author:
Ed Schofield <ed at pythoncharmers.com>
PEP-Delegate:
Brett Cannon <brett at python.org>
Status:
Rejected
Type:
Process
Created:
04-Aug-2015

Table of Contents

번역·라이선스 안내

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

거부 공지

운영 위원회는 이 제안의 __past__ 측면이 잠재적 이점에 비해 너무 복잡하다고 결정했습니다. 하위 호환성 요구 사항을 강화하자는 다른 측면은 PEP 387에서 다루어야 합니다.

범위

이 PEP는 PEP 5, 236, 387을 보완하며, 비슷한 목표를 공유합니다.

이 PEP는 PEP 5 “언어 발전 지침(Guidelines for Language Evolution)”을 뒷받침하기 위해 추가적인 호환성 메커니즘이 필요한 이유를 설명합니다. PEP 236 “__future__로 돌아가기(Back to the __future__)”는 PEP 5를 뒷받침하는 상위 호환성 메커니즘을 도입했지만, 하위 호환성을 위한 새 메커니즘은 해당 PEP의 범위를 벗어난다고 언급했습니다. 관련 PEP(진행 중)가 이러한 하위 호환성 메커니즘을 도입합니다.

PEP 5 “언어 발전 지침”은 “이 PEP [PEP 5]는 하위 호환 파서의 동적 로딩과 같은 다른 호환성 전략을 대체하거나 배제하지 않습니다.”라고 언급합니다.

배경

PEP 236에서 인용합니다: “Python은 때때로 핵심 언어 구성 요소의 공표된 의미론을 호환되지 않게 변경하거나, 그 우연적인(구현에 따라 달라지는) 동작을 어떤 식으로든 변경합니다. 이는 결코 변덕으로 이루어지지 않으며 언제나 장기적으로 언어를 개선하려는 목적으로 이루어지지만, 단기적으로는 논란을 일으키고 혼란을 줍니다. PEP 5 언어 발전 지침은 그 고통을 덜어 줄 방법을 제안하며, 이 PEP [PEP 236]는 이를 뒷받침하는 몇 가지 장치를 도입합니다.”

역시 PEP 236에서 인용합니다: “future_statement의 목적은 최신 릴리스를 제때 따라가는 사람들의 삶을 더 편하게 만드는 것입니다. 그렇게 하지 않는다고 해서 여러분을 미워하지는 않지만, 여러분의 문제는 훨씬 더 해결하기 어려우며, 그런 문제를 겪는 누군가가 이를 다루는 PEP를 작성해야 할 것입니다. future_statement는 다른 사용자층을 대상으로 합니다.”

현재 상황

핵심 언어 문법이나 의미론에 호환되지 않는 변경이 이루어질 때, Python은 현재 새 문법이나 의미론을 강제하는 릴리스가 나올 때까지 상위 호환성을 제공하는 future_statement 메커니즘을 제공하지만, 그 릴리스 이후에 하위 호환성을 제공하는 그에 상응하는 표준 메커니즘은 제공하지 않습니다.

문제

이러한 비대칭의 결과로, 호환성을 깨는 변경에 관해서는 이전(변경 전) 버전의 Python 인터프리터가 새로운(변경 후) 버전보다 더 많은 일을 할 수 있습니다. 이전 인터프리터는 변경 전에 설계된 코드와 새 코드를 모두 사용할 수 있는 반면, 새 인터프리터는 변경된 기능을 지원하도록 업그레이드된 코드만 사용할 수 있습니다.

예를 들어, PEP 236이 future_statement 메커니즘을 도입한 직후인 2001년에 PEP 238에서 도입된 나눗셈 연산자 변경을 살펴봅시다. PEP 238은 Python 2.x 시리즈에서 “진짜 나눗셈(true division)”을 위한 유용한 상위 호환성 메커니즘 일체를 제시하지만, Python 3.0에서 “진짜 나눗셈”이 처음 강제된 이후를 위한 하위 호환성 메커니즘은 전혀 포함하지 않았습니다. 3.0 이후의 Python 버전은 예전의 “고전적 나눗셈(classic division)” 의미론을 기대하는 코드를 위해 from __past__ import division 같은 하위 호환성 메커니즘을 제공하지 않는 반면, 3.0 이전의 Python 버전은 “고전적 나눗셈” 코드를 지원할 뿐 아니라 “진짜 나눗셈”을 기대하는 코드에 대한 상위 호환성도 지원합니다. 이로 인한 또 다른 결과로, 실제로 쓰이는 다양한 나눗셈 관련 Python 코드에 대해 “가장 호환성이 높은” 인터프리터는 호환성을 깨는 변경이 처음 강제되기 전 버전인 Python 2.7입니다.

“내리막 업그레이드”를 가능하게 하는 하위 호환성

이러한 상황과 대조적으로, 오피스 제품군과 같은 애플리케이션 소프트웨어의 새 버전은 여러 버전의 데이터 파일 형식을 불러오는 기능 면에서 이전 버전보다 더 뛰어난 경향이 있습니다. 일반적인 패턴은 새 애플리케이션 버전이 새 데이터 형식과 이전 데이터 형식 어느 쪽에서든 데이터를 투명하게 불러올 수 있고, 새 버전은 기본적으로 새 형식으로 데이터를 저장한다는 것입니다. 새 애플리케이션 소프트웨어 버전은 기본적으로 하위 호환되는 경향이 있습니다. 상위 호환성은 비교적 드뭅니다.

이 정책은 새 애플리케이션 소프트웨어 사용자를 이전 소프트웨어 사용자보다 유리한 위치에 놓습니다. 이전 소프트웨어는 대개 새 형식의 데이터를 불러오지 못하기 때문입니다. 때로는 새 소프트웨어 애플리케이션 버전의 사용자가 이 옵션을 명시적으로 선택하여 데이터를 이전 버전 형식으로 내보낼 수 있습니다. 이런 경우 이를 통해 얻는 상위 호환성은 완벽할 수도 있고 그렇지 않을 수도 있습니다. 일부 기능이 빠지거나 결과가 다른 면에서 최적이 아닐 수 있습니다. 따라서 업그레이드는 쉬운 반면 다운그레이드는 더 어렵습니다.

매력적인 새 기능과 하위 호환성 기능을 함께 제공하는 이러한 정책이 많은 사용자에게서 창발적으로 만들어 내는 현상은, 개별 사용자 각자에게 자신의 애플리케이션 버전을 업그레이드하라는 자연스러운 압력이 쌓인다는 것이며, 한 개인이 데이터 파일을 주고받는 다른 사용자가 많을수록 이 압력은 더 강해집니다.

제안 - 1부

이 PEP는 서로 관련된 두 가지 구체적인 제안을 합니다. 첫 번째 제안은 다음과 같습니다:

PEP 5의 “하위 호환성을 깨는 기능 도입 절차(Steps for Introducing Backwards-Incompatible Features)” 절에 여섯 번째 단계를 추가해야 합니다. 이 단계에는 다음과 같은 Python-dev의 정책을 명시합니다. 핵심 언어 문법이나 의미론에 호환되지 않는 변경을 적용할 때는 새로운 future_statement 같은 상위 호환성 메커니즘을 제안하는 데 그치지 않고, 가능한 한 하위 호환성 메커니즘도 검토해야 합니다. 이 하위 호환성 메커니즘은 호환성을 깨는 변경이 기본 동작으로 채택된 이후의 Python 버전에서 제공되어야 하며, Python-dev는 이런 방식을 선호하고 또 기대합니다. 또한 PEP 387 “하위 호환성 정책(Backwards Compatibility Policy)”이 채택된다면 이 PEP에도 같은 여섯 번째 단계를 추가합니다.

예시

이 PEP가 어떻게 적용되는지 예를 들어 보겠습니다. “진정한 나눗셈(true division)” PEP(238)의 최신 개정판이 오늘 제안된다면 불완전한 제안으로 간주될 것입니다. PEP 238은 이 제안이 일으키는 “심각한 하위 호환성 문제”를 언급하고, 초록(Abstract)과 API 변경(API Changes) 절에서 상위 호환성을 위한 여러 조치를 설명합니다. 또한 c.l.py에서 나온 몇 가지 하위 호환성 아이디어도 소개합니다. “모듈에서 고전적 나눗셈 의미론을 사용하려면 from __past__ import division을 사용하십시오”라는 아이디어도 그중 하나입니다. 하지만 이 PEP는 하위 호환성 계획을 제안의 일부로 내놓지는 않습니다.

이 PEP가 채택된다면 PEP 238 같은 제안은 호환성에 광범위한 영향을 미치므로 하위 호환성 계획도 함께 제시할 것으로 기대됩니다. 이 계획을 통해 호환성을 깨는 변경이 적용된 이후의 Python 버전 사용자도 자신의 코드에서 고전적 나눗셈 동작을 쉽게 다시 활성화할 수 있어야 합니다.

제안 - 두 번째

두 번째 제안은 다음과 같습니다:

Python은 상위 호환성을 위한 __future__ 모듈 메커니즘과 나란히 표준 하위 호환성 메커니즘을 제공해야 합니다.

편의상 이 문서에서는 이후 이를 “__past__” 메커니즘이라고 부릅니다. 다만 이 메커니즘이 __future__ 모듈과 future_statement 메커니즘의 특성을 모두 갖출 필요는 없습니다.

__past__ 메커니즘의 구체적인 형태와 구현은 별도의 PEP(작성 중)에서 다룹니다. 하지만 이 PEP는 __past__ 메커니즘을 설계할 때 PEP 296이 __future__에 대해 제시한 기준과 비슷한 기준을 충족하도록 권고합니다. 구체적으로는 다음과 같습니다:

a. 개별 모듈이 다시 활성화할 이전 Python 버전의 폐기된 동작을 모듈 단위로 지정할 수 있어야 합니다.

b. __past__ 메커니즘을 호출하는 사용자 모듈에 이전 Python 문법이나 의미론과의 하위 호환성을 다시 도입할 수 있어야 합니다. 이 메커니즘은 Python 3.6 이상에서도, 이전 버전의 포인트 릴리스에서도 이렇게 할 수 있을 만큼 유연해야 합니다.

c. __past__ 동작을 호출하도록 보완한 오래된 코드를 2.x 같은 이전 Python 버전에서도 실행할 수 있어야 합니다. 이런 버전은 호출된 특정 __past__ 기능을 알지 못하며, 하위 호환성을 위한 __past__ 메커니즘이 있다는 사실조차 알지 못합니다.

반례

이 기준을 위반하는 __past__ 메커니즘 구현의 예는 다음과 같습니다:

a. 임포트 훅. 임포트 훅은 대개 모듈 단위로 동작하지 못하고, 한 모듈 안에서 새로 임포트되는 모든 모듈에 재귀적으로 적용됩니다.

b. 이전 버전과 호환되지 않는 Python 3.6의 새 문법이나 새 의미론.

c. 이전 Python 버전에 같은 이름으로 존재하는 Python 표준 라이브러리 모듈에 Python 3.6에서 추가한 함수.

이점

Python-dev가 이 제안을 채택해서 얻는 이점은, 앞으로 하위 호환성을 깨는 변경마다 대응하는 __past__ 기능이 구현되어 있고 향후 Python 버전 사용자가 이를 쉽게 호출할 수 있다면 그러한 변경이 일으키는 혼란이 줄어들 수 있다는 것입니다. 이는 설계상의 실수를 바로잡기 위해 언어가 더 빠르고 효과적으로 발전하는 데 도움이 될 수 있습니다.

보수적인 사용자가 얻는 이점은 분명합니다. 각 모듈에 __past__ 주문(아마도 한 줄)을 추가하는 것만으로 호환성을 깨는 번쩍이는 최신 Python 버전 지원을 자신의 코드에 추가할 수 있으며, 이 작업은 자동화할 수도 있습니다. 그런 다음 인터프리터를 최신 버전으로 업그레이드하고 번쩍이는 최신 Python 기능을 사용할 수 있습니다.

커뮤니티가 얻는 이점은, 만 명의 사용자가 XYZ 패키지에 의존하고 XYZ 패키지가 최신 Python 버전 지원을 손쉽게 추가할 수 있다면, 그 만 명의 사용자도 XYZ 패키지가 이 작업을 하기를 기다리며 발이 묶이지 않고 최신 Python 버전으로 빠르게 업그레이드할 수 있다는 것입니다.

질문과 답변

Q1: 이 PEP는 Python이 하위 호환성을 깨는 각 기능에 대해 가능한 두 가지 의미 체계를 영원히 유지하도록 요구합니까?

A1: 절대 그렇지 않습니다. 레거시 기능은 적절한 시점, 즉 사용자층의 대다수가 새 Python 버전으로 옮겨 갔을 때 여전히 단계적으로 제거할 수 있습니다. 이 PEP는 호환성을 위한 개발 노력의 중점을 100% 상위 호환성에서 적어도 50%는 하위 호환성 쪽으로 옮기자고 제안할 뿐입니다. 사용자층이 최신 Python 인터프리터 버전을 채택하도록 하는 데는 두 개념 중 하위 호환성이 더 강력합니다.

대부분의 사용자가 이미 Python 2.1을 여유 있게 넘어섰기 때문에, 중첩되지 않은 스코프의 하위 호환성에 대부분의 사용자가 관심을 두지 않게 된 지 오래되었다는 점에 주목하십시오.

Q2: 하지만 Python-dev는 이미 일이 넘쳐서 추가되는 복잡성을 구현하고 유지 보수할 여력이 없습니다!

A2: Python-dev는 개발자 커뮤니티에 나서서 자신들이 중요하게 여기는 레거시 언어 기능의 하위 호환성을 Python에서 유지 보수해 달라고 요청할 수 있습니다. 커뮤니티가 특정한 낡은 동작에 더 이상 관심을 두지 않게 되면, Python-dev도 관심을 끊을 수 있습니다.

코어 개발자의 부담을 줄이기 위해, __past__ 메커니즘을 커뮤니티가 확장할 수 있도록, 예를 들어 표준이면서 “공인된” PyPI 패키지로 설계할 수도 있습니다.

Q3: 하위 호환성 기능 때문에 Python에 쓸모없는 코드와 비대함과 짐이 잔뜩 쌓이지 않겠습니까?

A3: 꼭 그렇지는 않습니다. 첫째, Python에서 호환성을 깨는 새 기능에 대한 제안을 평가할 때, 관련 __past__ 기능 구현이 얼마나 단순하고 유지 보수하기 쉬운지를 기준의 일부로 삼아 미리 평가할 수 있습니다.

둘째, 일부 오래된 기능은 하위 호환성을 제공하기가 간단합니다. Python 3.0 이전의 “고전적 나눗셈” 동작을 생각해 보십시오. python-future 프로젝트에는 future.utils.old_div 함수에 고전적 나눗셈의 호환 구현이 들어 있습니다:

def old_div(a, b):
    """
    Equivalent to ``a / b`` on Python 2 without ``from __future__ import
    division``.
    """
    if isinstance(a, numbers.Integral) and isinstance(b, numbers.Integral):
        return a // b
    else:
        return a / b

이러한 함수를 Python 3.x 버전에 함께 포함하고, 적절한 __past__ 호출 뒤에 a / b가 나타날 때마다 이 함수를 호출하는 간단한 메커니즘을 함께 제공하는 일이 부담스러울 필요는 없습니다.

Q4: 성능은 어떻습니까? 레거시 기능의 무게 때문에 새 Python 버전의 성능이 떨어지지 않겠습니까?

A4: 이는 사례별로 평가할 수 있습니다. 주로 우려할 만한 점은 레거시 옵션이 존재한다는 이유로 새 기본 동작의 성능이 과도하게 떨어지지 않아야 한다는 것입니다. __past__ 호출이 적용된 상태에서의 성능은 부차적인 문제입니다.