f-strings (Literal String Interpolation)
1. f-strings 소개
f-strings는 2015년 PEP-498에서 처음 제안되어 Python3.6부터 제공되기 시작한 기능입니다. 실제 제안 문서에서는 Literal String Interpolation이라는 이름으로 소개하고 있지만 일반적으로 f-string(formatted string의 줄임말)로 표현합니다.
f-strings는 문자열 포맷팅을 위해 사용되는 문법으로 자바스크립트의 Template Literal과 유사한 모습을 가지고 있습니다. 기존 Python2에서 사용하던 % 또는 Python3의 str.format 을 이용한 포맷팅에 비해 빠른 처리속도와 높은 코드 가독성을 제공한다는 점에서 현시점에서 문자열 포맷팅을 위한 가장 좋은 문법이 아닐까 생각됩니다.
간단한 사용 예시는 다음과 같습니다.
name = 'Alice'
age = 20
print(f'Hello my name is {name}. I am {age} years old')
[실행결과]
Hello my name is Alice. I am 20 years old
위의 예시에서 볼 수 있듯이 {} 문자 사이에 유효한 expression을 끼워넣어 간결하게 문자열 포맷팅을 할 수 있다는 것이 가장 큰 장점입니다. 이어서 f-strings의 다양한 활용을 살펴보도록 하겠습니다.
2. Expression Evaluation
f-string의 {} 문자 사이에는 유효한 expression이 들어갈 수 있다고 했습니다. 다시 말해 Python 코드를 삽입(embed)할 수 있다고 봐도 무방합니다. 삽입된 expression은 해당 식을 포함하고 있는 f-string과 같은 context를 가지므로 local 및 global 변수에 접근할 수 있습니다.
2-1. 계산식
계산식은 가장 일반적인 형태의 표현식(expression)으로 주로 간단한 덧셈 또는 곱셈의 결과를 출력하기 위해 많이 사용됩니다.
# 상수끼리의 덧셈을 수행합니다
print(f'10 + 20 = {10 + 20}')
# 변수를 활용할 수도 있습니다
a, b = 10, 20
print(f'{a} + {b} = {a + b}')
[실행결과]
10 + 20 = 30
10 + 20 = 30
2-2. 함수의 반환값
단순히 상수 또는 변수를 활용하는 것을 넘어 함수를 직접 호출하여 그 반환값을 출력하는 것도 가능합니다. 이것 또한 단순히 하나의 표현식이므로 당연한 것이지만 f-string을 처음 접하는 분들께 일반적으로 많이 사용되는 예시를 보여주기 위해 이를 간단히 기술하였습니다.
def mod(a, b):
return a % b
a, b = 10, 3
print(f'{a} is divided by {b} with a remainder of {mod(a, b)}')
[실행결과]
10 is divided by 3 with a remainder of 1
3. Format Specifier
{} 안의 expression 뒤에 선택적으로 : 문자를 입력한 뒤 원하는 서식지정자(Format Specifier)를 선택할 수 있습니다. 이제 f-string의 형태를 다음과 같이 정리할 수 있습니다.
f'<text> { <expression> <: format_specifier> } <text>'
사용 예시를 살펴봄으로써 조금 더 쉽게 이해가능합니다.
a, b = 10, 3
print(f'{a} divided by {b} is {(a / b):.4f}')
[실행결과]
10 divided by 3 is approximately 3.3333
위의 예시는 float 자료형의 데이터를 소수점 아래 유효 4자리까지 출력하도록 한 것입니다. 이처럼 앞에 오는 각 데이터의 자료형에 맞는 서식형태를 임의로 사용할 수 있습니다. float와 더불어 많이 사용되는 것 중 datetime 자료형이 있습니다. 다음 예시는 현재 년월일을 출력하는 코드를 보여줍니다.
from datetime import datetime
if __name__ == '__main__':
print(f'Today is {datetime.now():%Y.%m.%d}')
[실행 결과]
Today is 2022.01.05
한가지 알아두면 재미있는 사실은 서식지정자 자체도 {}를 이용한 evaluation을 통해 생성할 수 있다는 점입니다. 다음 예시는 float 자료형의 서식형태 중 width와 precision을 표현식 형태로 전달하는 것을 보입니다.
width, precision = 8, 4
print(f'10 divided by 3 : {(10 / 3):{width}.{precision}}')
print(f'10 divided by 7 : {(10 / 7):{width}.{precision}}')
[실행 결과]
10 divided by 3 : 3.333
10 divided by 7 : 1.429
{} 내부에 {}를 중첩하여 사용함으로써 서식지정자 자체를 표현식을 통해 생성할 수 있음을 보였습니다.
4. Self-Documenting Expressions
Self-Documenting 이란 f-string의 표현식에 = 문자를 붙여 표현식 자체와 그 evaluation 값을 모두 출력해주는 확장기능으로써 Python3.8에서 이 기능이 새롭게 추가되었습니다. 이 기능은 주로 디버깅을 하는 상황에서 유용하게 사용할 수 있습니다.
name, age = 'Alice', 20
print(f'{name=}, {age=}')
[실행 결과]
name='Alice', age=20
표현식에 해당하는 텍스트와 값이 모두 출력되는 모습을 확인할 수 있습니다. = 문자 이후 format specifier를 추가하여 사용하는 것또한 가능합니다. 참고로 = 주변에 공백문자를 입력할 경우 출력결과에서도 해당 공백이 그대로 반영되는 것을 확인할 수 있습니다.
num = 10 / 3
print(f'{num = :.2f}')
[실행 결과]
num = 3.33
다음과 같이 함수를 호출하여 그 반환값을 출력하고자 하는 경우에도 유용하게 사용할 수 있습니다.
def add(a, b):
return a + b
print(f'{add(10, 20) = }')
[실행 결과]
add(10, 20) = 30
이와 같은 코드는 가독성의 측면에서 직관적인 형태의 문법은 아닐 수 있지만 익숙해진다면 빠르게 특정 expression의 결과값을 확인하기에 좋은 수단으로 사용할 수 있습니다.
5. Padding & Alignment
f-strings의 기능중 잘 사용하지는 않지만 출력 결과를 정렬하거나 특정 길이의 padding 문자열을 임의로 붙일 수 있는 기능이 존재합니다. 엄밀히 말하면 이것또한 서식 지정자(format specifier)의 한 종류입니다. 아래의 예시를 통해 해당 기능을 쉽게 이해할 수 있습니다.
x = 'hello'
print(f'{x:>11}') # 우측정렬 (width : 11)
print(f'{x:&>11}') # 우측정렬 + 패딩 문자(&) 추가
print(f'{x:*<11}') # 좌측정렬 + 패딩 문자(*) 추가
print(f'{x:=^11}') # 가운데 정렬 + 패딩 문자(=) 추가
[실행 결과]
hello
&&&&&&hello
hello******
===hello===
패딩 문자를 임의로 지정해주지 않은 경우 공백(' ')문자로 설정되는 모습을 확인할 수 있습니다. 특정 상황에 따라 잘 활용할 수 있을 것으로 생각되지만 역시 가독성 측면에서 아쉬운 점이 있는 것 같습니다.
6. Conversions
Conversion은 f-string 내의 expression의 결과값을 포맷팅하기 전 어떤 형식으로 제공할 것인지를 결정하는 역할을 담당합니다. expression 이후 ! 문자를 추가한 뒤 s를 입력한 경우 표현식의 결과 값의 str()를, r은 repr()을, a는 ascii()를 적용한 결과를 반환합니다. 아래 예시를 통해 조금 더 수월하게 이해가 가능합니다.
x = '😃'
print(f'{x!r}') # repr() 호출
print(f'{x!s}') # str() 호출
print(f'{x!a}') # ascii() 호출
[실행 결과]
'😃'
😃
'\U0001f603'
7. Escaping
Escaping은 실제로 f-string을 사용하면서 가장 많이 겪게 되는 문제 중 하나입니다. 이 글에서는 대표적인 예시 몇 가지를 설명하고자 합니다.
7-1. 따옴표(quote) 출력
따옴표 문자로 둘러싸인 문자열(quoted string)을 f-string을 이용해 출력하는 경우 크게 두 가지 방법을 사용할 수 있습니다. expression 부(部)가 아닌 text 영역에서 \(backslash) 문자를 사용하거나, f-string 자체를 감싸고 있는 따옴표와 다른 quote 문자(single quote인 경우 double, 반대의 경우 single)를 사용할 수 있습니다.
x = 'quoted_string'
print(f'\'{x}\'') # \(backslash) 문자를 사용하여 단일 따옴표 escape
print(f'"{x}"') # 단일 따옴표로 싸여진 f-string내 쌍따옴표를 사용
[실행 결과]
'quoted_string'
"quoted_string"
7-2. 중괄호 문자('{') 출력
중괄호 문자 자체를 출력해야 하는 경우도 자주 발생합니다. 이런 경우 중괄호 문자를 중복하여 입력하면 됩니다.
print(f'{{Hello World!}}')
[실행 결과]
{Hello World!}
7-3. dict 객체의 멤버 출력
dict 객체의 멤버를 출력하기 위해 따옴표 문자를 사용해야 합니다. 이 경우 앞에서와 유사하게 f-string을 감싸고 있는 따옴표와 다른 종류의 따옴표 문자를 사용함으로써 escaping 할 수 있습니다.
price_of = {
'Apple': 3000,
'Banana': 4000,
'Kiwi': 2500
}
print(f'{price_of["Kiwi"] = }')
[실행 결과]
price_of["Kiwi"] = 2500
마치며
이 글에서 미처 설명하지 못한 f-string의 디테일한 기능이 더 많이 있습니다. 해당 내용은 PEP-498 또는 이 문서를 참고하시면 좋을 것 같습니다.
Python의 기존 문법에 익숙하신 분들이 f-string 대신 기존의 % 또는 format 메소드를 사용하는 경우가 아직 많은 것 같습니다. 그럼에도 불구하고 현시점에서 f-string은 높은 가독성과 좋은 성능(빠른 속도)을 제공하기 때문에 사용하지 않을 이유가 크게 없다고 생각합니다. 익숙해진다면 상당히 유용하게 활용하게 되는 기능이므로 만약 아직 f-string과 친하지 않다면 조금씩 사용해보면서 감을 익히는 것을 권장합니다 :)