PEP 502 – 문자열 보간 - 확장 논의
- Author:
- Mike G. Miller
- Status:
- Rejected
- Type:
- Informational
- Created:
- 10-Aug-2015
- Python-Version:
- 3.6
번역·라이선스 안내
이 비공식 한국어 번역은 원문 Copyright 절의 Public Domain 조건에 따라 제공합니다. 원저자와 공식 원문은 그대로 표시합니다. 수정되지 않은 기준 원문 · 공식 최신판
초록
“형식 문자열”을 제안한 PEP 498: Literal String Interpolation을은 2015년 9월 9일에 승인되었습니다. 설계 단계에서 제시된 추가 배경과 근거는 아래에 자세히 설명되어 있습니다.
해당 PEP를 요약하면, 문자열을 렌더링할 템플릿으로 표시하는 문자열 접두사가 도입되었습니다. 이러한 형식 문자열에는 str.format()에 기존 the existing syntax 을기반으로 작성된 하나 이상의 표현식이 포함될 수 있습니다. [10] [11] 형식 문자열은 컴파일 시점에 기존 문자열 형식 지정 연산으로 확장되며, 텍스트에 포함된 주어진 표현식을 추출하여 대신 위치 인자로 전달합니다.
런타임에는 결과 표현식을 평가하여 주어진 사양에 맞는 문자열을 렌더링합니다.:
>>> location = 'World'
>>> f'Hello, {location} !' # new prefix: f''
'Hello, World !' # interpolated result
형식 문자열은 기존의 str.format() 호출을 단순화하기 위한 구문 설탕에 불과하다고 생각할 수 있습니다.
PEP 상태
이 PEP는 사실에 근거한 어조가 아니라 의견에 근거한 어조를 사용한다는 이유로 거부되었습니다. 또한 PEP 498이 이미 작성되어 있었고 설계 결정의 세부 사항을 담을 곳이 되어야 하므로, 이 PEP는 중요하지 않은 것으로 판단되었습니다.
동기
Python에는 문자열 형식 지정 및 조작 기능이 풍부하지만, 한 가지 부족한 영역은 편리한 문자열 보간 구문이 없다는 점입니다. 유사한 사용 사례를 가진 다른 동적 스크립팅 언어와 비교하면, 비슷한 문자열을 작성하는 데 필요한 코드의 양이 상당히 많으며, 장황함과 조밀한 구문 또는 식별자 중복으로 인해 때로는 가독성도 떨어집니다.
이러한 어려움은 눈덩이처럼 커져 PEP 498이 된 작업을 시작한 원래 post to python-ideas 에서어느 정도 자세히 설명되어 있습니다. [1]
또한 Python 3의 보다 일관된 print 함수(PEP 3105)로 print 문을 대체하면서 입력하고 읽어야 하는 괄호 한 쌍이라는 사소한 부담이 하나 더 생겼습니다. 현재 문자열 형식 지정 방식의 장황함까지 더해지면서, 이는 다른 언어에 비해 본래 단순한 언어인 Python을 불리한 위치에 놓습니다.:
echo "Hello, user: $user, id: $id, on host: $hostname" # bash
say "Hello, user: $user, id: $id, on host: $hostname"; # perl
puts "Hello, user: #{user}, id: #{id}, on host: #{hostname}\n" # ruby
# 80 ch -->|
# Python 3, str.format with named parameters
print('Hello, user: {user}, id: {id}, on host: {hostname}'.format(**locals()))
# Python 3, worst case
print('Hello, user: {user}, id: {id}, on host: {hostname}'.format(user=user,
id=id,
hostname=
hostname))
Python에서는 표준 너비의 코드 한 줄에서 여러 변수가 포함된 문자열을 형식 지정하고 출력하는 일이 눈에 띄게 더 어렵고 장황하며, 들여쓰기가 이 문제를 더욱 악화시킵니다.
메시지 형식 지정의 복잡성이 아직 캡슐화되지 않은 소규모 프로젝트, 시스템 프로그래밍, 셸 스크립트 대체, 심지어 한 줄짜리 코드와 같은 사용 사례에서는 이러한 장황함 때문에 상당수의 개발자와 관리자가 수년에 걸쳐 다른 언어를 선택했을 가능성이 큽니다.
근거
목표
형식 문자열의 설계 목표는 다음과 같습니다.
제한 사항
Unix와 셸에서 설계상의 영감을 얻은 다른 언어들과 달리, 그리고 Javascript와 마찬가지로 Python은 문자열을 둘러싸기 위해 단일 (') 및 이중 (") ASCII 인용 문자를 모두 지정했습니다. 지금 둘 중 하나를 보간을 활성화하는 데 선택하고 다른 하나는 보간되지 않는 문자열에 남겨 두는 것은 합리적이지 않습니다. “Backtick”(또는 grave accent `)과 같은 다른 문자들도 역사적 제약(constrained by history)을 받으며, repr()의 단축 표기로 사용됩니다.
따라서 이러한 기능을 설계할 때 고려할 수 있는 몇 가지 선택지가 남습니다:
%를 통한 printf 스타일 문자열 포맷팅에서와 같은 연산자입니다.string.Template()와 같은 클래스입니다.str.format()와 같은 메서드 또는 함수입니다.- 새로운 구문 또는
- 잘 알려진
r''또는u''와 같은 새로운 문자열 접두사 표식입니다.
위의 처음 세 가지 옵션은 성숙한 방식입니다. 각각 구체적인 사용 사례와 단점이 있지만, 앞서 언급한 장황함과 시각적 잡음도 겪습니다. 모든 옵션은 다음 절에서 논의합니다.
배경
포맷된 문자열은 여러 기존 기법과 제안, 그리고 우리가 이를 통해 함께 배운 내용을 기반으로 합니다. 가독성과 오류 방지라는 설계 목표에 맞추어, 다음 예제에서는 위치 인자가 아닌 명명된 인자를 사용합니다.
다음 딕셔너리가 있고 최종 사용자에게 정보를 제공하는 문자열로 그 항목을 출력하고자 한다고 가정합시다:
>>> params = {'user': 'nobody', 'id': 9, 'hostname': 'darkstar'}
연산자를 통한 printf 스타일 포맷팅
이 venerable technique은 바이트 기반 프로토콜, 간단한 경우의 단순성, 많은 프로그래머에게 익숙하다는 점 등에서 여전히 사용됩니다:
>>> 'Hello, user: %(user)s, id: %(id)s, on host: %(hostname)s' % params
'Hello, user: nobody, id: 9, on host: darkstar'
이 형식에서는 필요한 딕셔너리 생성을 고려할 때 이 기법은 장황하고 약간 번잡하지만 비교적 읽기 쉽습니다. 추가적인 문제는 연산자가 원래 문자열 외에 인자를 하나만 받을 수 있다는 점이며, 따라서 여러 매개변수는 튜플이나 딕셔너리로 전달해야 합니다. 또한 전달한 인자의 개수나 예상된 타입에서 오류를 내거나, 키를 누락하거나, 뒤따르는 타입을 잊어버리기 쉽습니다. 예를 들어 (s 또는 d)입니다.
string.Template 클래스
string.Template class from PEP 292(단순한 문자열 치환)는 익숙한 셸 보간 구문과 safe-substitution feature를 사용하는 의도적으로 단순화된 설계이며, 셸 및 국제화 도구에서 주로 사용됩니다:
Template('Hello, user: $user, id: ${id}, on host: $hostname').substitute(params)
또한 장황하지만 문자열 자체는 읽기 쉽습니다. 기능은 제한적이지만 요구 사항을 잘 충족합니다. 많은 경우에 충분히 강력하지 않으며, 덕분에 미숙한 사용자가 문제를 일으키는 것을 방지할 뿐 아니라 제3자의 어느 정도 신뢰할 수 있는 입력(i18n)과 관련된 문제도 피할 수 있습니다. 안타깝게도 임시 문자열 보간에 사용하기에는 코드가 충분히 많아 사용을 꺼리게 하며, flufl.i18n과 같은 convenience library로 캡슐화하지 않는 한 그렇습니다.
PEP 215 - 문자열 보간
PEP 215는 이 제안과 많은 공통점을 공유하는 이전 제안입니다. 당시에는 세계가 이를 받아들일 준비가 되지 않았던 것 같지만, 최근 여러 다른 언어에서 지원되는 점을 고려하면 이제 때가 왔을 수도 있습니다.
이 제안에 포함된 많은 달러 기호($) 문자는 Python의 숙적 Perl과 닮았다는 인상을 주었을 수 있으며, PEP가 받아들여지지 않은 데 기여했을 가능성이 큽니다. 이는 다음 제안으로 대체되었습니다.
str.format() 메서드
str.format() syntax of PEP 3101은 기존 옵션 중 가장 최신이며 현대적인 방식입니다. 또한 다른 방식보다 강력하고 대개 읽기 쉽습니다. 이전 기법의 단점과 한계 중 많은 부분을 피합니다.
그러나 필요한 함수 호출과 매개변수 전달 때문에 문자열 리터럴을 사용하는 다양한 상황에서 장황한 정도부터 매우 장황한 정도까지 이릅니다.:
>>> 'Hello, user: {user}, id: {id}, on host: {hostname}'.format(**params)
'Hello, user: nobody, id: 9, on host: darkstar'
# when using keyword args, var name shortening sometimes needed to fit :/
>>> 'Hello, user: {user}, id: {id}, on host: {host}'.format(user=user,
id=id,
host=hostname)
'Hello, user: nobody, id: 9, on host: darkstar'
메서드 기반 접근 방식의 장황함은 여기에서 설명합니다.
PEP 498 – 리터럴 문자열 형식 지정
PEP 498은 위의 Abstract에서 설명한 것처럼 형식 문자열을 정의하고 논의합니다.
또한 처음 접하는 사람들에게는 다소 논란의 여지가 있지만, 형식 문자열에 임의의 표현식 지원을 추가해야 한다는 개념을 소개합니다. 이에 대해서는 Rejected Ideas 아래의 Restricting Syntax 절에서 더 자세히 논의합니다.
PEP 501 – 번역을 지원하는 문자열 보간
상호 보완적인 PEP 501은 i 접두사 제안, ES6 (Javascript)와 호환되는 string.Template 구문 통합, 지연 렌더링 및 객체 반환값을 통해 국제화를 논의의 일급 관심사로 가져옵니다.
다른 언어의 구현
이제 문자열 보간은 여러 산업에서 사용되는 다양한 프로그래밍 언어에서 잘 지원되며, 일종의 표준으로 수렴하고 있습니다. 이는 약간씩 변형된 str.format() 스타일 구문을 중심으로 하며, 활용도를 높이기 위해 임의의 표현식을 추가합니다.
Motivation 절에서는 Bash, Perl 및 Ruby에 얼마나 편리한 보간 구문이 존재하는지 보여 주었습니다. 이들의 표현식 지원을 살펴봅시다.
Bash
Bash는 문자열 내부에서 임의의, 심지어 재귀적인 구성도 여러 가지 지원합니다.:
> echo "user: $USER, id: $((id + 6)) on host: $(echo is $(hostname))"
user: nobody, id: 15 on host: is darkstar
Perl
Perl에도 임의의 표현식 구성이 있지만, 아마 잘 알려져 있지는 않을 것입니다.:
say "I have @{[$id + 6]} guanacos."; # lists
say "I have ${\($id + 6)} guanacos."; # scalars
say "Hello { @names.join(', ') } how are you?"; # Perl 6 version
Ruby
Ruby는 보간된 문자열에서 임의의 표현식을 사용할 수 있도록 합니다.:
puts "One plus one is two: #{1 + 1}\n"
기타
최근 문자열 보간을 구현한, 서로 덜 유사한 현대 언어들을 살펴봅시다.
Scala
Scala interpolation은 문자열 접두사를 통해 처리됩니다. 각 접두사는 서로 다른 결과를 생성합니다.:
s"Hello, $name ${1 + 1}" # arbitrary
f"$name%s is $height%2.2f meters tall" # printf-style
raw"a\nb" # raw, like r''
이러한 접두사는 Scala의 StringContext 클래스를 확장하여 사용자가 구현할 수도 있습니다.
- 리터럴 접두사를 사용한 큰따옴표 내부의 명시적 보간입니다.
- 사용자가 구현한 접두사를 지원합니다.
- 임의의 식을 지원합니다.
ES6 (Javascript)
Template strings를 설계한 사람들도 작은따옴표와 큰따옴표가 이미 사용 중이었던 Python과 동일한 문제에 직면했습니다. 그러나 Python과 달리 “백틱”은 사용 중이지 않았습니다. their issues에도 불구하고, 백틱은 ECMAScript 2015 (ES6) 표준의 일부로 선택되었습니다.:
console.log(`Fifteen is ${a + b} and\nnot ${2 * a + b}.`);
태그와 동일한 이름의 함수를 구현하여 사용자 지정 접두사도 지원합니다.:
function tag(strings, ...values) {
console.log(strings.raw[0]); // raw string is also available
return "Bazinga!";
}
tag`Hello ${ a + b } world ${ a * b}`;
- 백틱 내부의 명시적 보간입니다.
- 사용자가 구현한 접두사를 지원합니다.
- 임의의 식을 지원합니다.
C#, 버전 6
C#에도 유용한 새로운 interpolation feature가 있으며, IFormattable 인터페이스를 통해 customize interpolation할 수 있는 기능도 일부 제공합니다.:
$"{person.Name, 20} is {person.Age:D3} year{(p.Age == 1 ? "" : "s")} old.";
- 큰따옴표와
$접두사를 사용한 명시적 보간입니다. - 사용자 지정 보간을 사용할 수 있습니다.
- 임의의 식을 지원합니다.
Apple의 Swift
모든 문자열에서 임의의 interpolation under Swift를 사용할 수 있습니다.:
let multiplier = 3
let message = "\(multiplier) times 2.5 is \(Double(multiplier) * 2.5)"
// message is "3 times 2.5 is 7.5"
- 큰따옴표를 사용한 암시적 보간입니다.
- 임의의 식을 지원합니다.
- CR/LF를 포함할 수 없습니다.
추가 예제
문자열 보간의 여러 추가 예제를 found at Wikipedia에서 찾을 수 있습니다.
이제 배경과 역사를 살펴보았으므로, 해결책을 계속 살펴보겠습니다.
새로운 구문
이는 최후의 수단으로 선택해야 합니다. 새로운 구문 기능은 그것이 자리 잡는 뇌의 공간이라는 측면에서 모두 비용이 발생하기 때문입니다. 그러나 가능성 목록에는 아직 하나의 대안이 남아 있으며, 다음과 같습니다.
새로운 문자열 접두사
파이썬의 문자열 포매팅 및 하위 호환성의 역사, 다른 언어의 구현, 필요하지 않은 한 새로운 구문을 피한다는 점을 고려하면, 독창적인 통찰보다는 제거를 통해 수용 가능한 설계에 도달합니다. 따라서 보간된 문자열 리터럴을 문자열 접두사로 표시하도록 선택합니다.
또한 기존 선택지 중 가장 강력한 것을 재사용하고 그 위에 구축하는 표현식 구문을 선택하며, 기능을 더욱 중복 구현하지 않도록 str.format()를 활용합니다.:
>>> location = 'World'
>>> f'Hello, {location} !' # new prefix: f''
'Hello, World !' # interpolated result
PEP 498 – 리터럴 문자열 포매팅은 이 설계의 메커니즘과 구현을 자세히 다룹니다.
추가 주제
안전성
이 절에서는 포맷 문자열을 지원하기 위한 안전성 현황과 취한 예방 조치를 설명합니다.
- 포맷 문자열에는 입력으로 받거나 전달할 변수가 아니라 문자열 리터럴만 고려되므로, 외부 공격을 수행하기가 어렵습니다.
str.format()및 대안은 이 사용 사례를 already handle이미 처리합니다. - 변환 중에는
locals()도globals()도 필요하지 않으며 사용되지도 않으므로, 정보가 유출되지 않습니다. - 복잡성과 재귀 깊이로 인한
RuntimeError(s)를 제거하기 위해 재귀적 보간은 지원하지 않습니다.
그러나 문자열 리터럴 내부의 실수나 악성 코드를 놓칠 수 있습니다. 일반적인 코드에도 그렇게 말할 수 있지만, 이러한 표현식이 문자열 내부에 있다는 것은 조금 더 쉽게 가려질 수 있음을 의미합니다.
도구를 통한 완화
pyflakes, pylint 또는 Pycharm과 같은 도구나 린터가 표현식이 포함된 문자열 내부를 검사하고 적절히 마크업할 수 있다는 발상입니다. 이는 오늘날 프로그래밍 언어에서 흔한 작업이므로, 다중 언어 도구는 이 기능을 Python에만 구현할 필요가 없어 구현까지 걸리는 시간을 크게 단축할 수 있습니다.
앞으로는 프로젝트의 안전성 정책을 초과하는 구문이 있는지도 문자열을 검사할 수 있습니다.
스타일 가이드/예방 조치
임의의 표현식은 Python 표현식이 수행할 수 있는 것은 무엇이든 수행할 수 있으므로, 부작용을 일으킬 수 있는 구문을 포맷 문자열 내부에서 사용하지 않는 것이 매우 권장됩니다.
사용 패턴과 실제 문제가 파악되면 추가 지침을 작성할 수 있습니다.
참조 구현
say module on PyPI은 문자열 보간을 여기에서 설명한 방식으로 구현하며, 호출 가능 객체 인터페이스라는 작은 부담이 있습니다.:
> pip install say
from say import say
nums = list(range(4))
say("Nums has {len(nums)} items: {nums}")
Ruby 보간의 Python 구현도 is also available 있습니다. 이 구현은 codecs 모듈을 사용하여 작업을 수행합니다.:
> pip install interpy
# coding: interpy
location = 'World'
print("Hello #{location}.")
하위 호환성
기존 구문을 사용하고 현재 또는 과거의 기능을 피함으로써, 포맷 문자열은 기존 코드와 간섭하지 않도록 설계되었으며 어떠한 문제도 일으키지 않을 것으로 예상됩니다.
연기된 아이디어
국제화
국제화 지원을 통합하는 것이 매우 바람직했지만(PEP 501 참조), 세부 사항이 거의 모든 지점에서 달라 공통된 해결책은 어려울 것으로 보입니다: [15]
- 사용 사례가 다릅니다.
- 컴파일 작업과 런타임 작업의 차이
- 보간 구문의 필요 사항
- 대상 독자
- 보안 정책
거부된 아이디어
str.format()만으로 구문 제한하기
임의의 식 지원에 반대하는 일반적인 arguments against는 다음과 같습니다:
- YAGNI, “필요하지 않을 것입니다.”
- 이 기능은 역사적인 Python의 보수주의와 맞지 않습니다.
- 연기하십시오. 필요성이 입증되면 향후 버전에서 구현할 수 있습니다.
그러나 str.format() 구문만 지원하는 것은 이 문제에 대한 충분한 해결책이 아니라고 판단되었습니다. 예를 들어, 출력하기 전에 객체의 간단한 길이나 증가량을 구해야 하는 경우가 많습니다.
Implementations in Other Languages 절에서 볼 수 있듯이, 개발자 커뮤니티 전반은 대체로 동의하는 경향이 있습니다. 임의 표현식을 사용한 문자열 보간은 그 유용성으로 인해 현대 언어에서 업계 표준이 되어 가고 있습니다.
추가/사용자 지정 문자열 접두사
Implementations in Other Languages 절에서 보았듯이, 많은 현대 언어에는 공통 인터페이스를 갖춘 확장 가능한 문자열 접두사가 있습니다. 이는 일반적인 상황에서 코드를 일반화하고 줄이는 방법이 될 수 있습니다. ES6(Javascript), Scala, Nim, C#(다소 제한적인 형태)에서 그 예를 찾아볼 수 있습니다. 이는 BDFL에 의해 거부되었습니다. [14]
입력 변수의 자동 이스케이프
일부 경우에는 유용하지만, 문자열 표현식을 언제 어디에서 안전하게 사용할 수 있는지에 대해 지나치게 많은 불확실성을 초래할 것으로 여겨졌습니다. 또한 이 개념은 다른 사람들에게 설명하기도 어려웠습니다. [12]
개발자가 명시적으로 이스케이프하지 않은 한, 형식 문자열 변수를 항상 이스케이프되지 않은 것으로 간주하십시오.
환경 접근 및 명령 대체
시스템 프로그래밍과 셸 스크립트 대체를 위해서는 표현식 문자열에서 환경 변수를 처리하고 명령의 출력을 직접 캡처할 수 있으면 유용할 것입니다. 이는 충분히 중요하지 않고 bash/perl과 지나치게 비슷해 나쁜 습관을 조장할 수 있다는 이유로 거부되었습니다. [13]
감사의 말
- Eric V. Smith가 PEP 498의 작성 및 구현을 담당한 데 감사드립니다.
- 발생한 여러 가지 엉뚱한 아이디어를 거부하고 최종 설계에 집중할 수 있도록 도와준 python-ideas 메일링 리스트의 모든 분께 감사드립니다.
참고 자료
Copyright
This document has been placed in the public domain.