PEP 414 – Python 3.3을 위한 명시적 유니코드 리터럴
- Author:
- Armin Ronacher <armin.ronacher at active-4.com>, Alyssa Coghlan <ncoghlan at gmail.com>
- Status:
- Final
- Type:
- Standards Track
- Created:
- 15-Feb-2012
- Python-Version:
- 3.3
- Post-History:
- 28-Feb-2012, 04-Mar-2012
- Resolution:
- Python-Dev message
번역·라이선스 안내
이 비공식 한국어 번역은 원문 Copyright 절의 Public Domain 조건에 따라 제공합니다. 원저자와 공식 원문은 그대로 표시합니다. 수정되지 않은 기준 원문 · 공식 최신판
초록
이 문서는 유니코드를 인식하는 Python 2 애플리케이션을 Python 3으로 포팅할 때 필요한 변경량을 줄이기 위해, Python 2.x의 명시적 유니코드 리터럴을 Python 3.x 언어 사양에 다시 통합할 것을 제안합니다.
BDFL 선언
이 PEP는 Python 3.3에 공식적으로 승인되었습니다:
이 PEP를 승인합니다. 이 정도로 무해한 제안은 드뭅니다. 그렇게 하십시오.
제안
이 PEP는 Python 2의 유니코드 리터럴 구문을 Python 3.3에서 복원하여, 유니코드를 인식하는 애플리케이션의 기존 Python 2 코드 중 수정 없이 Python 3에서 실행되는 코드의 행 수를 크게 늘릴 것을 제안합니다.
구체적으로, Python 3의 문자열 리터럴 접두사 정의를 다음을 허용하도록 확장합니다.:
"u" | "U"
현재 지원되는 다음 항목에 더하여:
"r" | "R"
다음 항목은 모두 일반적인 Python 3 문자열을 나타냅니다.:
'text'
"text"
'''text'''
"""text"""
u'text'
u"text"
u'''text'''
u"""text"""
U'text'
U"text"
U'''text'''
U"""text"""
Python 3의 실제 유니코드 처리 방식은 변경하지 않고, 문자열 리터럴에 허용되는 형식만 변경합니다.
“Raw” 유니코드 리터럴의 제외
Python 2는 일반적인 raw 문자열의 정의에는 부합하지 않는 “raw” 유니코드 리터럴 개념을 지원합니다. 즉, \uXXXX 및 \UXXXXXXXX 이스케이프 시퀀스는 관련 유니코드 객체를 생성할 때에도 컴파일러에 의해 처리되어 적절한 유니코드 코드 포인트로 변환됩니다.
Python 3에는 이에 대응하는 개념이 없습니다. 컴파일러는 raw 문자열 리터럴 내용에 대해 어떠한 전처리도 수행하지 않습니다. 이는 Python 2의 8비트 raw 문자열 리터럴 동작과 일치합니다.
이러한 문자열은 거의 사용되지 않으며 허용할 경우 Python 3에서 다르게 해석되므로, 이를 완전히 제외하는 것이 더 나은 선택이라고 결정했습니다. 따라서 이를 사용하는 코드는 잠재적으로 다른 출력을 생성하는 대신 Python 3에서 여전히 즉시 실패합니다(Syntax Error와 함께).
Python 2와 Python 3 모두에서 실행되는 동등한 동작을 얻으려면, 일반 유니코드 리터럴을 사용하거나(문자열 내부에 적절한 추가 이스케이프를 적용해야 함), 문자열 연결 또는 문자열 서식을 사용하여 문자열의 raw 부분과 유니코드 이스케이프 시퀀스를 사용해야 하는 부분을 결합할 수 있습니다.
Python 2에서 from __future__ import unicode_literals를 사용할 때 명목상 “raw”인 유니코드 문자열 리터럴은 “raw Unicode” 접두사로 명시적으로 표시된 Python 2 문자열과 마찬가지로 \uXXXX 및 \UXXXXXXXX 이스케이프 시퀀스를 처리한다는 점에 유의하십시오.
작성자 주
이 PEP는 원래 Armin Ronacher가 작성했으며, Guido의 승인은 해당 버전을 바탕으로 이루어졌습니다.
현재 공개된 버전은 Guido가 결정을 내릴 때 고려했지만 Armin의 PEP 버전에는 명시적으로 문서화되지 않았던 추가적인 역사적 세부 사항과 근거를 포함하도록 Alyssa Coghlan이 다시 작성했습니다.
독자는 이 PEP의 많은 논거가 기술적인 것이아님을 인지해야 합니다. 대신, 이는 소프트웨어 개발의 사회적 및 개인적 측면과 깊이 관련됩니다.
근거
Python 3.2를 위한 Python 3 호환 Web Services Gateway Interface(WSGI) 사양(PEP 3333)이 출시되면서, Python 웹 생태계의 많은 부분은 기존 개발자 및 사용자 커뮤니티에 부정적인 영향을 주지 않으면서 Python 3을 지원하기 위해 공동으로 노력해 왔습니다.
해당 커뮤니티의 주요 개발자인 Chris McDonough(WebOb, Pyramid), Armin Ronacher(Flask, Werkzeug), Jacob Kaplan-Moss(Django), Kenneth Reitz(requests) 등이 제공한 주요 피드백 중 하나는, 애플리케이션의 모든 유니코드 리터럴 표기를 변경해야 한다는 요구 사항이(이를 어떤 방식으로 수행하든) 포팅 작업의 주요 걸림돌이라는 점입니다.
특히 다른 많은 Python 3 변경 사항과 달리, 이 변경은 프레임워크 및 라이브러리 작성자가 사용자를 대신하여 쉽게 처리할 수 있는 사항이 아닙니다. 그러한 사용자 대부분은 Python 언어 사양의 “순수성”에는 전혀 관심이 없으며, 단지 자신의 웹사이트와 애플리케이션이 가능한 한 잘 작동하기를 원합니다.
이 우려를 가장 목소리 높여 지적해 온 곳은 Python 웹 커뮤니티이지만, 다른 유니코드 지원에 매우 적극적인 분야(예를 들어 GUI 개발)도 해당 분야와 커뮤니티가 Python 3 지원을 위해 본격적으로 노력하기 시작하면 비슷한 문제에 부딪힐 수 있을 것으로 예상됩니다.
일반적인 반론
불만: 이 PEP가 Python 3.2의 채택을 저해할 수 있습니다
이 불만은 흥미롭습니다. 이 불만에는 이 PEP가 유니코드를 인식하는 Python 2 애플리케이션을 Python 3으로 포팅하는 일을 분명히 더 쉽게 만들어 줄 것이라는 암묵적인 인정이 담겨 있기 때문입니다.
기존 포팅 도구 모음이 부과하는 제약을 감수할 준비가 되어 있거나, 문제가 최소화되도록 Python 2 코드베이스를 충분히 업데이트할 준비가 된 Python 커뮤니티는 많습니다.
이 PEP는 그러한 커뮤니티를 위한 것이 아닙니다. 대신, 이러한 어려움을 원하지 않는 사람들을 돕기 위해 특별히 설계되었습니다.
그러나 이 제안은 의미 변경 없이 언어 구문을 비교적 조금 수정하는 것이므로, 서드 파티 임포트 훅으로 지원하는 것이 가능합니다. 이러한 임포트 훅은 임포트 시간에 약간의 오버헤드를 발생시키고, 훅을 설치해야 하는 각 애플리케이션에 추가 단계가 필요하지만, 유니코드 리터럴 접두사를 사용하기 때문에 그렇지 않으면 Python 3.3 이상에서만 실행될 라이브러리와 프레임워크를 Python 3.2를 대상으로 하는 애플리케이션에서도 사용할 수 있게 합니다.
이러한 임포트 훅 프로젝트 중 하나가 Vinay Sajip의 uprefix [4]입니다.
임포트 시점에 즉시 변환하기보다 코드를 미리 변환하는 것을 선호하는 사람들을 위해, Armin Ronacher는 임포트 중이 아니라 설치 시점에 실행되는 훅을 개발하고 있습니다 [5].
물론 두 접근 방식을 결합하는 것도 가능합니다. 예를 들어, 로컬 개발 중에는 빠른 편집-테스트 주기를 위해 임포트 훅을 사용하고, 지속적 통합 작업과 Python 3.2 배포에는 설치 훅을 사용할 수 있습니다.
이 절에서 설명한 접근 방식은 예를 들어 Python 2.7과 3.2를 공식적으로 지원되는 Python 버전으로 함께 제공할 Ubuntu 12.04 LTS 릴리스에서 Python 3을 대상으로 하려는 애플리케이션에 유용할 수 있습니다.
불만: Python 2에서의 포팅을 지원하기 위해 Python 3을 악화해서는 안 됩니다
이는 실제로 Python 3의 핵심 설계 원칙 중 하나입니다. 그러나 Python 전체의 핵심 설계 원칙 중 하나는 “실용성이 순수성보다 중요합니다”라는 것입니다. 서드 파티 개발자에게 상당한 부담을 지우려 한다면, 그렇게 하는 데 대한 확실한 근거가 있어야 합니다.
대부분의 경우, 하위 호환성이 깨지는 Python 3 변경의 근거는 코드의 정확성을 향상하거나(예를 들어 바이너리 데이터와 텍스트 데이터를 기본적으로 더 엄격하게 분리하고, 필요한 경우 정수 나눗셈의 결과를 부동 소수점으로 변환하는 것), 일반적인 메모리 사용량을 줄이거나(예를 들어 구체적인 리스트 대신 이터레이터와 뷰를 더 많이 사용하는 것), 표현력을 높이지 않으면서 Python 코드를 읽기 어렵게 만드는 산만한 성가신 요소를 제거하는 것(예를 들어 잡힌 예외의 이름을 지정하는 쉼표 기반 구문)입니다. 그러한 근거에 기반한 변경은 Python 3으로 전환하려는 Python 2 개발자들의 반대가 있더라도 절대 되돌려지지 않습니다.
많은 경우 Python 2는 역사적인 이유로 동일한 작업을 수행하는 두 가지 방법을 제공했습니다. 예를 들어 부등식은 !=와 <>를 모두 사용하여 검사할 수 있었고, 정수 리터럴은 선택적인 L 접미사를 사용하여 지정할 수 있었습니다. 이러한 중복 요소는 Python 3에서 제거되었으며, 그 결과 언어 전체의 규모가 줄어들고 개발자 간의 일관성이 향상되었습니다.
최초의 Python 3 설계에서는(Python 3.2까지 포함하여) 유니코드 리터럴에 대한 명시적 접두사 구문이 Python 3에서는 완전히 불필요하므로 이 범주에 속하는 것으로 간주되었습니다. 그러나 이러한 다른 사례와 유니코드 리터럴의 차이점은 Python 2 코드에서 유니코드 리터럴 접두사가 중복되지 않는다는 점입니다. 이는 정보 손실을 방지하기 위해 어떤 방식으로든 보존해야 하는 프로그램상 중요한 구별입니다.
전환을 돕기 위해 포팅 도구가 만들어졌지만(다음 절 참조), 그럼에도 Python 2에서 유니코드 문자열을 많이 사용하는 사용자에게 추가 부담을 줍니다. 이는 오로지 Python 3을 배우는 미래의 개발자들에게 “역사적인 이유로 문자열 리터럴에는 선택적인 u 또는 U 접두사가 올 수 있습니다. 이를 직접 사용하지 마십시오. 이전 버전의 언어에서 포팅하는 일을 돕기 위해 존재할 뿐입니다.”라고 말할 필요가 없도록 하기 위한 것입니다.
Python 2를 배우던 많은 학생이 문자열 예외에 관해 이와 비슷한 경고를 들었지만, Python 개발자로 성장하는 과정에서 혼란을 겪거나 회복할 수 없을 정도로 방해받지는 않았습니다. 이 기능도 마찬가지일 것입니다.
이 점은 Python 3이 바이트 리터럴과 원시 바이트 및 문자열 리터럴에 사용되는 B 및 R 접두사의 대문자 변형을 여전히 허용한다는 사실로 더욱 뒷받침됩니다. 문자열 접두사 변형으로 인해 혼란이 발생할 가능성이 그토록 크다면, Python 3에서 제거된 다른 모든 중복 요소와 함께 이러한 중복 접두사도 제거하라고 요구하는 항의의 목소리는 어디에 있었습니까?
일반적인 사용 중단 절차를 통해 Python 2에서 문자열 예외에 대한 지원이 제거된 것과 마찬가지로, 현재 이 PEP가 받아들여지는지와 관계없이 중복 문자열 접두 문자(구체적으로 B, R, u, U)에 대한 지원도 결국 Python 3에서 제거될 수 있습니다. 그러나 이러한 변경은 Python 2.7을 지원하는 서드 파티 라이브러리가 오늘날 Python 2.2 또는 2.3을 지원하는 라이브러리만큼 흔해진 이후에야 이루어질 가능성이 큽니다.
문제 제기: WSGI의 “네이티브 문자열” 개념은 보기 흉한 임시방편입니다
유니코드 리터럴의 제거가 웹 개발 커뮤니티에서 그러한 우려를 불러일으킨 한 가지 이유는, 업데이트된 WSGI 사양이 WSGI 호환 인터페이스를 제공하는 기존 웹 서버에 대한 혼란을 최소화하기 위해 몇 가지 타협을 해야 했기 때문입니다(업데이트된 표준이 웹 애플리케이션 작성자와 웹 프레임워크 개발자에게 실행 가능한 목표가 되도록 하려면 이것이 필요하다고 판단되었습니다).
그러한 타협 중 하나가 “네이티브 문자열”이라는 개념입니다. WSGI는 세 가지 서로 다른 종류의 문자열을 정의합니다:
- 텍스트 문자열: Python 2에서는
unicode로, Python 3에서는str로 처리됩니다 - 네이티브 문자열: Python 2와 Python 3 모두에서
str로 처리됩니다 - 바이너리 데이터: Python 2에서는
str로, Python 3에서는bytes로 처리됩니다
일부 개발자는 WSGI의 “네이티브 문자열”을 보기 흉한 임시방편으로 간주합니다. 실제 기저 데이터의 인코딩과 관계없이, latin-1로 디코딩된 “텍스트”에만 사용된다고 명시적으로 문서화되어 있기 때문입니다. 이러한 접근 방식은 텍스트 인코딩을 올바르게 처리하도록 장려하기 위해 설계된 Python 3 데이터 모델의 많은 업데이트를 우회합니다. 그러나 문제 영역의 구체적인 특성으로 인해 일반적으로 제대로 작동합니다. 웹 서버 및 웹 프레임워크 개발자는 HTTP와 관련 프로토콜을 다룰 때 바이너리 데이터와 텍스트 사이의 경계가 얼마나 모호해질 수 있는지, 그리고 인코딩된 텍스트 데이터를 조작할 때 사용 중인 인코딩의 영향을 이해하는 것이 얼마나 중요한지를 가장 잘 인식하는 사람들 중 일부이기 때문입니다. 애플리케이션 수준에서는 이러한 세부 사항 대부분이 웹 프레임워크와 지원 라이브러리에 의해 개발자에게서 숨겨집니다(Python 2 및 Python 3 모두에서).
실제로 네이티브 문자열은 유용한 개념입니다. 표준 라이브러리와 서드 파티 프레임워크 및 패키지 모두에 str과 주로 함께 작동하도록 설계된 일부 API가 있으며, 인터프리터 내부 세부 사항 중에도 그러한 것들이 있기 때문입니다. 이러한 구성 요소는 Python 2에서 unicode를 지원하지 않거나 Python 3에서 bytes를 지원하지 않는 경우가 많습니다. 지원하더라도 추가적인 인코딩 세부 사항을 요구하거나 str 변형에는 적용되지 않는 제약을 부과합니다.
실제 str인스턴스를 사용하여 가장 잘 처리되는 인터페이스의 몇 가지 예는 다음과 같습니다:
- Python 식별자(속성, 딕셔너리 키, 클래스 이름, 모듈 이름, 임포트 참조 등)
- 대부분의 URL과 urllib/http 서버의 HTTP 헤더
- WSGI 환경 키와 CGI에서 상속된 값
- 동적 컴파일 및 AST 해킹을 위한 Python 소스 코드
- 예외 메시지
__repr__반환 값- 선호되는 파일 시스템 경로
- 선호되는 OS 환경
Python 2.6 및 2.7에서는 이러한 구분을 다음과 같이 표현하는 것이 가장 자연스럽습니다:
u"": 텍스트 문자열(unicode)"": 네이티브 문자열(str)b"": 바이너리 데이터(str,bytes로도 별칭 지정됨)
Python 3에서는 latin-1로 디코딩된 네이티브 문자열이 다른 텍스트 문자열과 구별되지 않습니다:
"": 텍스트 문자열(str)"": 네이티브 문자열(str)b"": 바이너리 데이터(bytes)
from __future__ import unicode_literals을 사용하여 Python 2의 동작을 수정하는 경우, 적절한 n()의 정의와 함께 다음과 같이 구분을 표현할 수 있습니다:
"": 텍스트 문자열n(""): 네이티브 문자열b"": 바이너리 데이터
(n=str은 단순한 경우에 작동하지만, ASCII가 아닌 소스 인코딩으로 인해 때때로 문제가 발생할 수 있습니다)
Python 2와 Python 3의 공통 부분 집합에서는 (소스 인코딩을 적절히 지정하고 u()및 b()헬퍼 함수를 정의하면) 다음과 같이 표현할 수 있습니다:
u(""): 텍스트 문자열"": 네이티브 문자열b(""): 바이너리 데이터
이 마지막 접근법은 Python 2.5 및 그 이전 버전을 지원하는 유일한 변형입니다.
모든 대안 가운데 Python 2.6 및 2.7에서 현재 지원되는 형식이 원하는 세 가지 동작 유형을 명확히 구분하는 가장 깔끔한 접근법입니다. 이 PEP를 통해 해당 형식은 Python 3.3 이상에서도 지원됩니다. 또한 가져오기 및 설치 훅을 사용하면 Python 3.1 및 3.2에서도 지원됩니다. 가능성은 훨씬 낮지만, Python 2.5에서 b접두사를 사용할 수 있도록 훅을 조정하는 것도 생각해 볼 수 있습니다.
불만: 기존 도구는 모든 사람에게 충분히 좋아야 합니다
이미 Python 3으로 애플리케이션을 성공적으로 포팅한 개발자들이 흔히 표현하는 의견은 “어렵다고 생각한다면 잘못하고 있는 것입니다” 또는 “그렇게 어렵지 않으니 그냥 시도해 보십시오!”와 같은 말입니다. 의도한 바는 분명 아니겠지만, 이러한 응답은 현재 포팅 도구 모음의 부족한 점을 지적하는 사람들에게 결국 “포팅 도구에는 아무런 문제가 없으며, 제대로 사용하는 방법을 모를 뿐 아니라 여러분이 서툰 것입니다”라고 말하는 효과를 냅니다.
이러한 응답은 사람들이 무엇을 불평하는지에 대한 핵심을 완전히 놓친 사례입니다. 이 PEP로 이어진 피드백은 포팅이 불가능하다는 사람들의 불평에서 나온 것이 아닙니다. 오히려 이 피드백은 포팅을 성공적으로 완료한 사람들이 자신들이 포팅해야 했던 애플리케이션 유형(구체적으로는 유니코드를 인식하는 웹 프레임워크와 지원 라이브러리)에서 그 경험이 매우 불쾌했다고 이의를 제기하면서 나온 것입니다.
이는 주관적인 평가이며, Python 3 포팅 도구 생태계가 “이를 수행하는 하나의 명백한 방법”이라는 철학이 단호하게 적용되지 않는 사례인 이유입니다. 원래는 “Python 2에서 개발하고, 2to3로 변환한 다음, 양쪽을 테스트한다”가 두 버전을 동시에 개발하는 표준 방식이 되도록 의도했지만, 실제로는 서로 다른 프로젝트와 개발자 커뮤니티의 요구가 충분히 다양하다는 점이 입증되어 여러 접근법이 고안되었으며, 각 그룹이 자신의 요구에 가장 잘 맞는 접근법을 선택할 수 있게 되었습니다.
Lennart Regebro는 사용 가능한 마이그레이션 전략에 대한 훌륭한 개요를 [2]에서 작성했으며, 공식 포팅 가이드 [3]에도 유사한 검토가 제공됩니다. (Lennart가 개요를 작성한 이후 공식 지침은 “구체적인 상황에 따라 다릅니다”라는 방향으로 완화되었다는 점에 유의하십시오.)
그러나 이 두 가이드는 관련된 모든 개발자가 Python 3 지원이라는 생각에 이미 전념하고 있다는 근본적인 가정에서 작성되었습니다. 이러한 가이드는 명확한 이점이 없다면 혼란을 특별히 잘 용납하지 않을 수 있는 사용자층과 상호작용할 때 발생하는 이러한 변경의 사회적 측면을 전혀 고려하지 않으며, Python 2에 초점을 맞춘 업스트림 개발자들에게 Python 3 하위 호환성 향상만을 목적으로 하는 패치를 받아들이도록 설득하려는 상황도 고려하지 않습니다.
현재 포팅 도구 모음을 사용하면 모든 마이그레이션 전략이 프로젝트의 모든 유니코드 리터럴을 변경하게 됩니다. 예외는 없습니다. 프로젝트가 unicode_literals가져오기를 채택하기로 결정한 경우에는 접두사가 없는 문자열 리터럴로, 그렇지 않은 경우에는 u("text")와 같은 변환기 호출로 변환됩니다.
unicode_literals가져오기 접근법을 사용하되 프로젝트 전체에서 동시에 채택하지 않는다면, 접두사가 없는 문자열 리터럴의 의미가 성가시게 모호해질 수 있습니다. 이 문제는 Django 사이트와 같은 집계된 소프트웨어에서 특히 심각할 수 있습니다. 이러한 상황에서는 일부 파일이 unicode_literals가져오기를 사용하고 다른 파일은 사용하지 않게 되어 혼란이 발생할 가능성이 분명해집니다.
이러한 문제는 기술적 차원에서는 분명히 해결할 수 있지만, 사회적 차원에서는 완전히 불필요한 방해 요소입니다. 개발자의 역량은 Python 3 전환과 관련된 실제 기술적 어려움(예를 들어 8비트 텍스트 문자열과 바이너리 데이터를 구분하는 문제)을 해결하는 데 사용해야 합니다. Python 2에서 자신의 유니코드 문자열을 이미 명시적으로 식별해 두었다는 이유만으로 추가 코드 변경(자동화된 변경도 포함)을 강요해서는 안 됩니다.
Armin Ronacher는 크로스 버전 호환성 six 라이브러리의 지원을 받아 Python 2.7 이상에서 실행되는 범위까지만 Python 코드를 현대화하는 2to3용 실험적 확장을 만들었습니다. 이 도구는 python-modernize [1]로 제공됩니다. 현재 이 도구가 생성하는 변경 사항은 변환된 소스의 모든 유니코드 리터럴에 영향을 줍니다. 이로 인해 그러한 변경 사항을 수용하도록 요청받은 상류 개발자들과 애플리케이션을 변경하도록 요청받은 프레임워크 사용자 사이에서 정당한 우려가 발생할 것입니다.
그러나 유니코드 리터럴 구문 변경으로 인한 잡음을 제거하면, 많은 프로젝트가 python-modernize를 실행하고 권장된 변경 사항을 적용하는 것만으로 Python 3.3+와 깔끔하고 (비교적) 논란 없이 순방향 호환되도록 만들 수 있습니다.
참고 문헌
Copyright
This document has been placed in the public domain.