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

Python 개선 제안 한국어 번역

PEP 498 – 리터럴 문자열 보간

Author:
Eric V. Smith <eric at trueblade.com>
Status:
Final
Type:
Standards Track
Created:
01-Aug-2015
Python-Version:
3.6
Post-History:
07-Aug-2015, 30-Aug-2015, 04-Sep-2015, 19-Sep-2015, 06-Nov-2016
Resolution:
Python-Dev message

Table of Contents

번역·라이선스 안내

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

초록

Python은 텍스트 문자열을 형식화하는 여러 방법을 지원합니다. 여기에는 %-formatting [1], str.format() [2], string.Template [3]이 포함됩니다. 이러한 각 방법에는 장점이 있지만, 실제로 사용하기 번거롭게 만드는 단점도 있습니다. 이 PEP는 새로운 문자열 형식화 메커니즘인 리터럴 문자열 보간을 추가하자고 제안합니다. 이 PEP에서는 이러한 문자열을 “f-strings”라고 부르며, 이는 그러한 문자열을 나타내는 데 사용되는 첫 문자에서 따온 이름이고 “formatted strings”를 의미합니다.

이 PEP는 기존 문자열 형식화 메커니즘을 제거하거나 더 이상 사용되지 않게 하자고 제안하지 않습니다.

F-문자열은 최소한의 구문을 사용하여 문자열 리터럴 안에 표현식을 삽입하는 방법을 제공합니다. f-문자열은 상수 값이 아니라 실제로 실행 시 평가되는 표현식이라는 점에 유의해야 합니다. Python 소스 코드에서 f-문자열은 ‘f’가 앞에 붙은 문자열 리터럴이며, 중괄호 안에 표현식을 포함합니다. 표현식은 해당 값으로 대체됩니다. 몇 가지 예시는 다음과 같습니다.:

>>> import datetime
>>> name = 'Fred'
>>> age = 50
>>> anniversary = datetime.date(1991, 10, 12)
>>> f'My name is {name}, my age next year is {age+1}, my anniversary is {anniversary:%A, %B %d, %Y}.'
'My name is Fred, my age next year is 51, my anniversary is Saturday, October 12, 1991.'
>>> f'He said his name is {name!r}.'
"He said his name is 'Fred'."

유사한 기능이 PEP 215에서 제안되었습니다. PEP 215는 Python 표현식의 일부를 지원하자고 제안했지만, PEP 3101에서 도입된 타입별 문자열 형식화(__format__() 메서드)는 지원하지 않았습니다.

근거

이 PEP는 Python에서 문자열을 더 간단하게 형식화할 방법을 원한다는 요구에서 비롯되었습니다. 기존 형식화 방법은 오류가 발생하기 쉽거나, 유연하지 않거나, 사용하기 번거롭습니다.

%-formatting은 지원하는 타입에 제한이 있습니다. int, str, double만 형식화할 수 있습니다. 다른 모든 타입은 지원되지 않거나, 형식화하기 전에 이러한 타입 중 하나로 변환됩니다. 또한 단일 값이 전달될 때 발생하는 잘 알려진 함정이 있습니다.:

>>> msg = 'disk failure'
>>> 'error: %s' % msg
'error: disk failure'

하지만 msg가 튜플인 경우에는 동일한 코드가 실패합니다.:

>>> msg = ('disk failure', 32)
>>> 'error: %s' % msg
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: not all arguments converted during string formatting

방어적으로 처리하려면 다음 코드를 사용해야 합니다.:

>>> 'error: %s' % (msg,)
"error: ('disk failure', 32)"

str.format()은 %-formatting의 이러한 문제 중 일부를 해결하기 위해 추가되었습니다. 특히 일반 함수 호출 구문을 사용하므로 여러 매개변수를 지원하며, 문자열로 변환되는 객체의 __format__()메서드를 통해 확장할 수 있습니다. 자세한 근거는 PEP 3101을 참조하십시오. 이 PEP는 기존 Python 문자열 형식화 메커니즘과의 연속성을 제공하기 위해 str.format()의 구문과 메커니즘을 상당 부분 재사용합니다.

그러나 str.format()에도 문제가 없는 것은 아닙니다. 그중 가장 큰 문제는 장황하다는 점입니다. 예를 들어 텍스트 value가 여기에서 반복됩니다.:

>>> value = 4 * 20
>>> 'The value is {value}.'.format(value=value)
'The value is 80.'

가장 단순한 형태에서도 약간의 보일러플레이트가 있으며, 플레이스홀더에 삽입되는 값이 플레이스홀더가 위치한 곳에서 때로는 매우 멀리 떨어져 있습니다.:

>>> 'The value is {}.'.format(value)
'The value is 80.'

f-문자열을 사용하면 다음과 같이 됩니다.:

>>> f'The value is {value}.'
'The value is 80.'

F-문자열은 문자열 내부에 Python 표현식의 값을 간결하고 읽기 쉬운 방식으로 포함할 수 있게 합니다.

이러한 점에서 string.Template 및 %-서식은 str.format()과 유사한 단점이 있지만, 지원하는 서식 옵션도 더 적습니다. 특히 이들은 __format__ 프로토콜을 지원하지 않으므로 특정 객체가 문자열로 변환되는 방식을 제어할 방법이 없으며, 문자열로 변환되는 방식을 제어하려는 추가 유형(예: Decimaldatetime)으로 확장할 수도 없습니다. 이 예제는 string.Template로는 가능하지 않습니다.:

>>> value = 1234
>>> f'input={value:#06x}'
'input=0x04d2'

또한 %-서식이나 string.Template은 다음과 같은 서식도 제어할 수 없습니다.:

>>> date = datetime.date(1991, 10, 12)
>>> f'{date} was on a {date:%A}'
'1991-10-12 was on a Saturday'

globals() 또는 locals() 사용 금지

python-dev [4]에서 진행된 논의에서는 locals()와 globals() 또는 그에 상응하는 기능을 사용하는 여러 해법이 제시되었습니다. 이러한 해법에는 각각 여러 문제가 있습니다. 그중에는 클로저에서 다른 방식으로는 사용되지 않는 변수를 참조하는 문제도 있습니다. 다음을 고려하십시오.:

>>> def outer(x):
...     def inner():
...         return 'x={x}'.format_map(locals())
...     return inner
...
>>> outer(42)()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 3, in inner
KeyError: 'x'

컴파일러가 클로저 내부의 x에 대한 참조를 추가하지 않았기 때문에 오류가 발생합니다. 이 기능이 작동하려면 x에 대한 참조를 수동으로 추가해야 합니다.:

>>> def outer(x):
...     def inner():
...         x
...         return 'x={x}'.format_map(locals())
...     return inner
...
>>> outer(42)()
'x=42'

또한 locals() 또는 globals()를 사용하면 정보 유출이 발생합니다. 호출자의 locals() 또는 globals()에 접근할 수 있는 호출된 루틴은 문자열 보간에 필요한 것보다 훨씬 많은 정보에 접근할 수 있습니다.

Guido는 [5]에서 더 나은 문자열 보간을 위한 어떤 해법도 구현에서 locals() 또는 globals()를 사용하지 않을 것이라고 밝혔습니다. (이는 사용자가 locals() 또는 globals()를 전달하는 것을 금지하지 않으며, 단지 이를 요구하지 않을 뿐이고, 내부적으로 이러한 함수를 사용하는 것도 허용하지 않습니다.)

사양

소스 코드에서 f-문자열은 문자 ‘f’ 또는 ‘F’가 앞에 붙은 문자열 리터럴입니다. 이 PEP에서 ‘f’를 사용하는 모든 곳에서는 ‘F’도 사용할 수 있습니다. ‘f’는 ‘r’ 또는 ‘R’과 어느 순서로든 결합하여 원시 f-문자열 리터럴을 만들 수 있습니다. ‘f’는 ‘b’와 결합할 수 없습니다. 이 PEP는 바이너리 f-문자열을 추가하는 것을 제안하지 않습니다. ‘f’는 ‘u’와 결합할 수 없습니다.

소스 파일을 토큰화할 때 f-문자열은 일반 문자열, 원시 문자열, 바이너리 문자열 및 삼중 따옴표 문자열과 동일한 규칙을 사용합니다. 즉, 문자열은 시작할 때 사용한 문자와 동일한 문자로 끝나야 합니다. 작은따옴표로 시작하면 작은따옴표로 끝나야 하는 식입니다. 이는 현재 문자열을 찾기 위해 Python 코드를 스캔하는 모든 코드를 f-문자열도 인식하도록 간단히 수정할 수 있음을 의미합니다. (물론 f-문자열 내부의 구문 분석은 별개의 문제입니다.)

토큰화가 완료되면 f-문자열은 리터럴 문자열과 표현식으로 구문 분석됩니다. 표현식은 중괄호 '{''}' 안에 나타납니다. 표현식을 찾기 위해 문자열을 스캔할 때 f-문자열의 리터럴 부분 내부에 있는 중복 중괄호 '{{' 또는 '}}'는 각각 대응하는 하나의 중괄호로 대체됩니다. 중복된 리터럴 여는 중괄호는 표현식의 시작을 나타내지 않습니다. 문자열의 리터럴 부분에 있는 하나의 닫는 중괄호 '}'는 오류입니다. 하나의 닫는 중괄호를 나타내려면 리터럴 닫는 중괄호를 '}}'로 중복해야 합니다.

중괄호 바깥에 있는 f-문자열의 부분은 리터럴 문자열입니다. 그런 다음 이러한 리터럴 부분이 디코딩됩니다. 원시 문자열이 아닌 f-문자열의 경우, 여기에는 '\n', '\"', "\'", '\xhh', '\uxxxx', '\Uxxxxxxxx'와 같은 백슬래시 이스케이프 및 명명된 유니코드 문자 '\N{name}'를 해당 유니코드 문자로 변환하는 작업이 포함됩니다 [6].

표현식 내부 어디에도 백슬래시를 사용할 수 없습니다. '#' 문자를 사용하는 주석은 표현식 내부에서 허용되지 않습니다.

각 표현식 뒤에 선택적 타입 변환을 지정할 수 있습니다. 허용되는 변환은 '!s', '!r' 또는 '!a'입니다. 이는 str.format()에서와 동일하게 처리됩니다. '!s'는 표현식에 str()를 호출하고, '!r'는 표현식에 repr()를 호출하며, '!a'는 표현식에 ascii()를 호출합니다. 이러한 변환은 format()을 호출하기 전에 적용됩니다. '!s'를 사용하는 유일한 이유는 표현식의 타입이 아니라 str에 적용되는 형식 지정자를 지정하려는 경우입니다.

F-string은 str.format과 동일한 형식 지정자 미니 언어를 사용합니다. str.format()과 마찬가지로, f-string 내부에 선택적 형식 지정자를 포함할 수 있으며, 지정된 경우 표현식 또는 타입 변환과 콜론으로 구분합니다. 형식 지정자를 제공하지 않으면 빈 문자열이 사용됩니다.

따라서 f-string은 다음과 같은 형태입니다.:

f ' <text> { <expression> <optional !s, !r, or !a> <optional : format specifier> } <text> ... '

그런 다음 형식 지정자를 인수로 사용하여 __format__프로토콜에 따라 표현식의 형식을 지정합니다. 그 결과 값은 f-string의 값을 구성할 때 사용됩니다.

각 값에 대해 __format__()을 직접 호출하는 것은 아니라는 점에 유의하십시오. 실제 코드는 type(value).__format__(value, format_spec) 또는 format(value, format_spec)과 동등한 방식을 사용합니다. 자세한 내용은 내장 format()함수의 문서를 참조하십시오.

표현식에는 문자열이나 괄호, 대괄호 또는 중괄호 바깥에서 ':'이나 '!'를 포함할 수 없습니다. 단, '!='연산자는 특수한 경우로 허용됩니다.

이스케이프 시퀀스

f-string의 표현식 부분에는 백슬래시가 나타날 수 없으므로, 예를 들어 f-string 내부의 따옴표를 이스케이프하는 데 백슬래시를 사용할 수 없습니다.:

>>> f'{\'quoted string\'}'
  File "<stdin>", line 1
SyntaxError: f-string expression part cannot include a backslash

표현식 내부에서는 다른 종류의 따옴표를 사용할 수 있습니다.:

>>> f'{"quoted string"}'
'quoted string'

백슬래시 이스케이프는 f-string의 문자열 부분 내부에 나타날 수 있습니다.

결과 문자열 값에 리터럴 중괄호를 표시하는 올바른 방법은 중괄호를 두 번 쓰는 것임에 유의하십시오.:

>>> f'{{ {4*10} }}'
'{ 40 }'
>>> f'{{{4*10}}}'
'{40}'

Python의 모든 원시 문자열과 마찬가지로 원시 f-string에서는 이스케이프 처리가 수행되지 않습니다.:

>>> fr'x={4*10}\n'
'x=40\\n'

Python의 문자열 토큰화 규칙으로 인해 f-string f'abc {a['x']} def'는 유효하지 않습니다. 토크나이저는 이를 3개의 토큰인 f'abc {a[', x']} def'로 구문 분석합니다. 일반 문자열과 마찬가지로 이는 원시 문자열을 사용해도 수정할 수 없습니다. 이 f-string을 작성하는 올바른 방법은 여러 가지가 있으며, 그중 하나는 다른 따옴표 문자를 사용하는 것입니다.:

f"abc {a['x']} def"

또는 삼중 따옴표를 사용할 수 있습니다.:

f'''abc {a['x']} def'''

코드 동등성

f-string을 구현하는 데 사용되는 정확한 코드는 지정되어 있지 않습니다. 그러나 문자열로 변환되는 모든 내장 값은 해당 값의 __format__메서드를 사용한다는 점이 보장됩니다. 이는 str.format()이 값을 문자열로 변환할 때 사용하는 것과 동일한 메커니즘입니다.

예를 들어 다음 코드는:

f'abc{expr1:spec1}{expr2!r:spec2}def{expr3}ghi'

다음으로 평가될 수 있습니다:

'abc' + format(expr1, spec1) + format(repr(expr2), spec2) + 'def' + format(expr3) + 'ghi'

표현식 평가

문자열에서 추출된 표현식은 f-문자열이 나타난 컨텍스트에서 평가됩니다. 이는 표현식이 지역 변수와 전역 변수에 완전히 액세스할 수 있음을 의미합니다. 함수 및 메서드 호출을 포함하여 유효한 모든 Python 표현식을 사용할 수 있습니다.

f-문자열은 소스 코드에서 문자열이 나타나는 위치에서 평가되므로, f-문자열을 사용한다고 해서 추가적인 표현력이 제공되지는 않습니다. 추가적인 보안 문제도 없습니다. f-문자열 안에 넣지 않고 동일한 표현식을 그대로 작성할 수도 있었기 때문입니다.:

>>> def foo():
...   return 20
...
>>> f'result={foo()}'
'result=20'

다음과 동등합니다:

>>> 'result=' + str(foo())
'result=20'

표현식은 ast.parse('(' + expression + ')', '<fstring>', 'eval') [7]과 동등한 방식으로 구문 분석됩니다.

평가하기 전에 표현식이 암시적 괄호로 둘러싸이므로 표현식에 줄 바꿈이 포함될 수 있다는 점에 유의하십시오. 예를 들어 다음과 같습니다:

>>> x = 0
>>> f'''{x
... +1}'''
'1'

>>> d = {0: 'zero'}
>>> f'''{d[0
... ]}'''
'zero'

형식 지정자

형식 지정자에도 평가되는 표현식이 포함될 수 있습니다. 이를 통해 다음과 같은 코드를 사용할 수 있습니다:

>>> width = 10
>>> precision = 4
>>> value = decimal.Decimal('12.34567')
>>> f'result: {value:{width}.{precision}}'
'result:      12.35'

형식 지정자의 표현식이 필요한 경우 평가되고 나면, 형식 지정자는 f-문자열 평가기에 의해 해석되지 않습니다. str.format()에서와 마찬가지로 형식 지정자는 형식이 지정되는 객체의 __format__() 메서드에 단순히 전달됩니다.

문자열 연결

인접한 f-문자열과 일반 문자열은 연결됩니다. 일반 문자열은 컴파일 시간에 연결되고, f-문자열은 실행 시간에 연결됩니다. 예를 들어 표현식:

>>> x = 10
>>> y = 'hi'
>>> 'a' 'b' f'{x}' '{c}' f'str<{y:^4}>' 'd' 'e'

은 다음 값을 생성합니다:

'ab10{c}str< hi >de'

이 실행 시간 연결의 정확한 방식은 지정되어 있지 않지만, 위 코드는 다음과 같이 평가될 수 있습니다:

'ab' + format(x) + '{c}' + 'str<' + format(y, '^4') + '>de'

각 f-문자열은 인접한 f-문자열과 연결되기 전에 완전히 평가됩니다. 즉, 다음은:

>>> f'{x' f'}'

첫 번째 f-문자열에 닫는 중괄호가 포함되어 있지 않으므로 구문 오류입니다.

오류 처리

f-문자열을 처리할 때 컴파일 시간 오류 또는 실행 시간 오류가 발생할 수 있습니다. 컴파일 시간 오류는 f-문자열을 스캔할 때 감지할 수 있는 오류로 제한됩니다. 이러한 오류는 모두 SyntaxError를 발생시킵니다.

짝이 맞지 않는 중괄호:

>>> f'x={x'
  File "<stdin>", line 1
SyntaxError: f-string: expecting '}'

잘못된 표현식:

>>> f'x={!x}'
  File "<stdin>", line 1
SyntaxError: f-string: empty expression not allowed

f-string 내부의 표현식을 평가할 때 런타임 오류가 발생합니다. f-string은 여러 번 평가될 수 있으며, 어떤 때는 정상적으로 작동하고 다른 때는 오류를 발생시킬 수 있다는 점에 유의하십시오.:

>>> d = {0:10, 1:20}
>>> for i in range(3):
...     print(f'{i}:{d[i]}')
...
0:10
1:20
Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
KeyError: 2

또는:

>>> for x in (32, 100, 'fifty'):
...   print(f'x = {x:+3}')
...
'x = +32'
'x = +100'
Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
ValueError: Sign not allowed in string format specifier

표현식의 앞뒤 공백은 무시됩니다.

가독성을 높이기 위해 표현식의 앞뒤 공백은 무시됩니다. 이는 평가 전에 표현식을 괄호로 묶기 때문에 발생하는 부산물입니다.

표현식 평가 순서

f-string의 표현식은 왼쪽에서 오른쪽 순서로 평가됩니다. 이는 표현식에 부작용이 있는 경우에만 확인할 수 있습니다.:

>>> def fn(l, incr):
...    result = l[0]
...    l[0] += incr
...    return result
...
>>> lst = [0]
>>> f'{fn(lst,2)} {fn(lst,3)}'
'0 2'
>>> f'{fn(lst,2)} {fn(lst,3)}'
'5 7'
>>> lst
[10]

논의

python-ideas 논의

python-ideas [8]에서 이루어진 대부분의 논의는 세 가지 문제에 초점을 맞추었습니다:

  • f-string을 나타내는 방법,
  • f-string에서 표현식의 위치를 지정하는 방법, 그리고
  • 완전한 Python 표현식을 허용할지 여부입니다.

f-string을 나타내는 방법

보간된 문자열에 포함된 표현식을 평가하려면 컴파일러가 관여해야 하므로, 어떤 문자열을 평가해야 하는지 컴파일러에 나타내는 방법이 있어야 합니다. 이 PEP에서는 문자열 리터럴 앞에 오는 선행 'f' 문자를 선택했습니다. 이는 컴파일 시 'b''r' 접두사가 문자열 자체의 의미를 변경하는 방식과 유사합니다. 'i'와 같은 다른 접두사도 제안되었습니다. 어느 선택지도 다른 선택지보다 나아 보이지 않았으므로 'f'를 선택했습니다.

또 다른 선택지는 컴파일러가 알고 있는 Format()과 같은 특수 함수를 지원하는 것이었습니다. 이는 Python에 지나치게 많은 마법을 도입하는 것처럼 보입니다. 기존 식별자와 충돌할 가능성이 있을 뿐만 아니라, PEP 작성자는 문자열 접두사 문자로 이러한 마법을 나타내는 편이 더 낫다고 생각합니다.

f-string에서 표현식의 위치를 지정하는 방법

이 PEP에서는 문자열 내부의 대체 텍스트를 구분하기 위해 str.format()과 동일한 구문을 지원합니다. 표현식은 중괄호 안에 포함됩니다. string.Template$identifier 또는 ${expression}과 같은 다른 선택지도 제안되었습니다.

$identifier는 셸 스크립트 작성자와 일부 다른 언어 사용자에게 분명 더 익숙하겠지만, Python에서는 str.format()이 널리 사용됩니다. Python 표준 라이브러리를 간단히 검색해 보면 string.Template의 사용 사례는 소수에 불과하지만 str.format()의 사용 사례는 수백 건에 이릅니다.

또 다른 제안된 대안은 대체된 텍스트를 \{} 사이 또는 \{\} 사이에 두는 것이었습니다. 모든 문자열 리터럴이 보간을 지원한다면 이 구문이 바람직했을 수도 있지만, 이 PEP에서는 선행 'f'로 이미 표시된 문자열만 지원합니다. 따라서 이 PEP에서는 최종 사용자가 str.format()에 익숙하다는 점을 활용하기 위해 장식되지 않은 중괄호를 사용하여 대체 텍스트를 나타냅니다.

완전한 Python 표현식 지원

python-ideas 토론에 참여한 많은 사람은 단일 식별자만 지원하거나, 제한된 Python 표현식의 하위 집합(예를 들어 str.format()이 지원하는 하위 집합)을 지원하기를 원했습니다. 이 PEP는 중괄호 안에서 완전한 Python 표현식을 지원합니다. 완전한 표현식이 없으면 일부 바람직한 사용 방식이 번거로워집니다. 예를 들어:

>>> f'Column={col_idx+1}'
>>> f'number of items: {len(items)}'

다음과 같이 됩니다.:

>>> col_number = col_idx+1
>>> f'Column={col_number}'
>>> n_items = len(items)
>>> f'number of items: {n_items}'

매우 보기 흉한 표현식을 f-string에 포함할 수 있다는 점은 사실이지만, 이 PEP는 그러한 사용을 린터나 코드 검토에서 다루어야 한다고 봅니다.:

>>> f'mapping is { {a:b for (a, b) in ((1, 2), (3, 4))} }'
'mapping is {1: 2, 3: 4}'

다른 언어에서의 유사한 지원

Wikipedia에는 다른 프로그래밍 언어의 문자열 보간에 관한 좋은 논의가 있습니다 [9]. 이 기능은 다양한 구문과 제한 사항을 적용하여 많은 언어에서 구현되어 있습니다.

f-string 표현식과 str.format 표현식의 차이

str.format()에서 허용되는 제한된 표현식과 f-string 안에서 허용되는 완전한 표현식 사이에는 작은 차이가 하나 있습니다. 차이는 인덱스 조회가 수행되는 방식에 있습니다. str.format()에서는 숫자처럼 보이지 않는 인덱스 값이 문자열로 변환됩니다.:

>>> d = {'a': 10, 'b': 20}
>>> 'a={d[a]}'.format(d=d)
'a=10'

딕셔너리에서 인덱스 값을 조회할 때 해당 값이 문자열 'a'로 변환된다는 점에 주목하십시오.

그러나 f-string에서는 'a'의 값에 리터럴을 사용해야 합니다.:

>>> f'a={d["a"]}'
'a=10'

그렇지 않으면 변수를 인덱스 값으로 사용할 수 없기 때문에 이러한 차이가 필요합니다.:

>>> a = 'b'
>>> f'a={d[a]}'
'a=20'

추가 논의는 [10]를 참조하십시오. 완전한 Python 표현식을 f-string에서 지원하게 된 계기는 바로 이 관찰이었습니다.

또한 str.format()이 이해하는 제한된 표현식이 유효한 Python 표현식일 필요는 없습니다. 예를 들어:

>>> '{i[";]}'.format(i={'";':4})
'4'

이러한 이유로 str.format() “표현식 파서”는 f-string을 구현할 때 사용하기에 적합하지 않습니다.

삼중 따옴표로 묶은 f-string

삼중 따옴표로 묶은 f-string을 사용할 수 있습니다. 이러한 문자열은 일반적인 삼중 따옴표 문자열과 동일하게 구문 분석됩니다. 구문 분석 및 디코딩이 끝나면 일반적인 f-string 로직이 적용되고, 각 값에 대해 __format__()이 호출됩니다.

원시 f-string

원시 문자열과 f-string을 결합할 수 있습니다. 예를 들어 정규 표현식을 구성하는 데 사용할 수 있습니다.:

>>> header = 'Subject'
>>> fr'{header}:\s+'
'Subject:\\s+'

또한 원시 f-string을 삼중 따옴표 문자열과 결합할 수 있습니다.

바이너리 f-string은 지원하지 않음

bytes.format()을 지원하지 않는 것과 같은 이유로, 'f''b' 문자열 리터럴을 결합할 수 없습니다. 주요 문제는 객체의 __format__()메서드가 바이트 문자열과 호환되지 않는 유니코드 데이터를 반환할 수 있다는 점입니다.

이진 f-문자열은 먼저 bytes.format()에 대한 해결책이 필요합니다. 이 아이디어는 과거에도 제안되었으며, 가장 최근에는 PEP 461에서 제안되었습니다. 이러한 기능에 대한 논의에서는 보통 다음 중 하나를 제안합니다.

  • 객체가 바이트로 변환되는 방식을 제어할 수 있도록 __bformat__()과 같은 메서드를 추가하는 방법 또는
  • bytes.format()str.format()만큼 범용적이거나 확장 가능하지 않게 하는 방법입니다.

이러한 기능이 필요하다면, 이 두 가지 모두 향후 선택지로 남아 있습니다.

!s, !r, 및 !a는 중복됩니다.

!s, !r, 및 !a변환은 엄밀히 말해 필요하지 않습니다. f-문자열 내부에서는 임의의 표현식이 허용되므로, 다음 코드는:

>>> a = 'some string'
>>> f'{a!r}'
"'some string'"

다음과 동일합니다.:

>>> f'{repr(a)}'
"'some string'"

마찬가지로 !sstr()호출로, !aascii()호출로 대체할 수 있습니다.

그러나 이 PEP에서는 str.format()과의 차이를 최소화하기 위해 !s, !r, 및 !a를 지원합니다. str.format()에서는 임의의 표현식 실행을 허용하지 않으므로 !s, !r, 및 !a가 필요합니다.

표현식 내부의 람다

람다는 ':'문자를 사용하므로 표현식에서 괄호 밖에 나타날 수 없습니다. 콜론은 형식 지정자의 시작으로 해석되므로, 람다 표현식의 시작이 인식되어 구문상 유효하지 않게 됩니다. f-문자열 표현식에서 일반 람다를 사용할 실용적인 용도가 없으므로, 이는 그다지 큰 제한으로 여겨지지 않습니다.

람다를 반드시 사용해야 한다고 생각한다면, 괄호 안에서 사용할 수 있습니다.:

>>> f'{(lambda x: x*2)(3)}'
'6'

‘u’와 결합할 수 없음

‘u’ 접두사는 Python 2.7과의 소스 호환성을 쉽게 하기 위한 수단으로 PEP 414에 따라 Python 3.3에 추가되었습니다. Python 2.7은 f-문자열을 결코 지원하지 않으므로, ‘f’ 접두사를 ‘u’와 결합할 수 있어도 얻는 이점은 없습니다.

Python 소스 코드의 예

다음은 현재 str.format()을 사용하는 Python 소스 코드의 몇 가지 예와, 이를 f-문자열로 작성하면 어떻게 보이는지를 보여 줍니다. 이 PEP에서는 f-문자열로 일괄 변환할 것을 권장하지 않습니다. 이는 str.format()의 실제 사용 사례와 이를 처음부터 f-문자열을 사용해 작성했다면 어떻게 보이는지를 보여 주는 예일 뿐입니다.

Lib/asyncio/locks.py:

extra = '{},waiters:{}'.format(extra, len(self._waiters))
extra = f'{extra},waiters:{len(self._waiters)}'

Lib/configparser.py:

message.append(" [line {0:2d}]".format(lineno))
message.append(f" [line {lineno:2d}]")

Tools/clinic/clinic.py:

methoddef_name = "{}_METHODDEF".format(c_basename.upper())
methoddef_name = f"{c_basename.upper()}_METHODDEF"

python-config.py:

print("Usage: {0} [{1}]".format(sys.argv[0], '|'.join('--'+opt for opt in valid_opts)), file=sys.stderr)
print(f"Usage: {sys.argv[0]} [{'|'.join('--'+opt for opt in valid_opts)}]", file=sys.stderr)

참고 자료