문자열 포매팅은 값을 문자열로 변환해서 문자열 템플릿에 넣는 과정이에요. The Zen of Python은 "Python에서 어떤 일을 하는 명백한 방법은 하나여야 한다"고 말해요. 그런데 문자열 포매팅에서는 사정이 조금... 덜 선(zen)해요.
Python에서 문자열 포매팅을 하는 주요 방법이 네 가지나 된다는 사실은 놀라울 수 있어요. 각각 다른 상황을 위한 방법이거든요.
이는 일부는 Python의 긴 역사 때문이고, 일부는 국제화나 입력 정제 같은 고려 사항 때문이에요.
문자열 포매팅 도구 상자에 가장 최근에 추가된 것부터 시작해서, "옛 스타일" 또는 "printf() 스타일" 문자열 포매팅으로 거슬러 올라가 볼게요.
f-string
Python 3.6에 도입된 f-string("formatted string"의 줄임말) 또는 리터럴 문자열 보간은, 따옴표 앞에 f(또는 F) 접두사를 붙여 표현식과 문자열을 str 타입으로 빠르고 효율적으로 평가(하고 포매팅)하는 방법이에요(이렇게요: f'{object}').
f-string은 한 줄(' 또는 ")과 여러 줄(''' 또는 """)을 비롯한 모든 따옴표 종류와 함께 쓸 수 있어요.
{} 안에 넣은 변수, 표현식 또는 다른 타입은 먼저 평가되고, 그다음 str로 변환되며, 마지막으로 중괄호 밖의 str과 이어 붙여져요.
이 예제에서는 문장에 두 변수 값(str 하나와 float 하나)을 넣어요:
>>> name = 'eighth'
>>> value = 1/8
# The f-string, using the two values.
# The .2f format code truncates, so the value displays as 0.12.
>>> f'An {name} is approximately {value:.2f}.'
'An eighth is approximately 0.12.'
평가할 수 있는 표현식은 거의 무엇이든 될 수 있어요.
평가할 수 있는 (아주 다양한) 가능성 중 일부는 str, 숫자, 변수, 산술 표현식, 조건 표현식, 내장 타입, 슬라이스, 함수, 람다, 컴프리헨션, 또는 __str__이나 __repr__ 메서드가 정의된 모든 객체예요.
간단한 것부터 복잡한 것까지 살펴볼게요:
변수 넣기: f-string의 가장 간단한 사용법은 변수를 문자열에 바로 넣는 거예요.
# Assigning a variable.
>>> name = "World"
# Inserting that variable.
>>> f'Hello, {name}!'
'Hello, World!'
{} 안의 표현식: 중괄호 안에서는 유효한 Python 표현식이라면 무엇이든 평가할 수 있어요.
작은따옴표로 묶은 f-string 안에서 큰따옴표를 쓰면(또는 그 반대로) 이스케이프 시퀀스가 필요 없다는 점을 기억해 두세요:
# A dictionary of key:value pairs.
>>> waves = {'water': 1, 'light': 3, 'sound': 5}
# Inserting the whole dict.
>>> f'Wave ranks: {waves}'
"Wave ranks: {'water': 1, 'light': 3, 'sound': 5}"
# An expression can be evaluated inline:
>>> f"Tenfold the value of 'light' is {waves['light'] * 10}."
"Tenfold the value of 'light' is 30."
# A method call can also be evaluated inline:
>>> f'{"hello world!".title()} is a classic greeting.'
'Hello World! is a classic greeting.'
# An f-string can be nested inside another f-string:
>>> f"{f'hello world!'.title()} is a classic greeting."
'Hello World! is a classic greeting.'
출력 포매팅: 형식 지정 미니 언어로 정렬, 숫자 정밀도 등을 제어할 수 있어요.
형식 지정은 값 뒤에 :로 구분해서 써요.
# Right-align a value to ten characters, and round it to 3 decimal places.
>>> value = 1 / 7
>>> f'One seventh is {value:10.3f}.'
'One seventh is 0.143.'
# A format specification can be set using variables as well.
>>> padding = 10
>>> precision = 3
>>> f'One seventh is {value:{padding}.{precision}f}.'
'One seventh is 0.143.'
전부 합치기: 변수, 표현식, 함수 호출, 출력 포매팅:
>>> precision = 3
>>> f"{30e8 * 111_000:6.{precision}e}"
'3.330e+14'
>>> verb = 'meet'
>>> the_end = ['end', 'of', 'transmission']
>>> f'"Have a {"NICE".lower()} day, I will {verb} you after {30e8 * 111_000:6.{precision}e} light-years."{the_end}'
'"Have a nice day, I will meet you after 3.330e+14 light-years."[\'end\', \'of\', \'transmission\']'
# Did you notice the escaped single-quotes in the previous example?
# Using double quotes instead of single quotes for the f-string means the list's single-quoted strings print cleanly.
>>> f"Have a nice day. {the_end}"
"Have a nice day. ['end', 'of', 'transmission']"
알아 둘 두 가지 주요 제약이 있어요.
f-string 표현식은 비워 둘 수 없어요.
또한 Python 3.12 이전에는 주석을 포함할 수 없었어요.
>>> f"An empty expression will error: {}"
SyntaxError: f-string: empty expression not allowed
>>> word = 'word'
>>> f"""A comment in a triple quoted f-string: {
word # I chose a nice variable
}"""
'A comment in a triple quoted f-string: word'
문자열 보간은 국제화(I18N)와 지역화(L10N)를 위한 GNU gettext API와 함께 쓸 수 없어요. 그래서 "문자열을 감싸는" 번역 상황에서는 f-string 대신 아래에서 설명하는 string.Template 클래스나 str.format() 메서드를 쓰는 것이 권장돼요.
또한 f-string의 중괄호 {} 안에서 표현식을 쓰는 것은 eval()이나 exec()을 쓰는 것과 비슷하다는 점도 기억해 두세요. 그리 안전하지 않아서 사용자 입력과 함께 써서는 절대 안 돼요.
str.format() 메서드str.format() 메서드는 문자열 안의 자리 표시자를 함수에 인자로 넘긴 값으로 바꿔요.
자리 표시자는 이름({price}), 숫자({0} 또는 인덱스), 또는 빈(위치 기반) 자리 표시자 {}로 지정해요.
예를 들어:
# A named placeholder and a positional placeholder.
>>> 'My text: {placeholder_1} and {}.'.format(12, placeholder_1='named placeholder')
'My text: named placeholder and 12.'
f-string과 마찬가지로, Python의 str.format()은 텍스트를 정렬하고 실수를 자르는 등 다양한 미니 언어 형식 지정자를 지원해요.
전체 형식 지정자 패턴은 {[<name>][!<conversion>][:<format_specifier>]}예요:
<name>은 이름이 있는 자리 표시자, 숫자, 또는 빈 값일 수 있어요.!<conversion>은 선택 사항이며, 다음 세 변환 중 하나여야 해요: str()은 !s, repr()은 !r, ascii()은 !a예요.
기본값으로는 str()이 쓰여요.:<format_specifier>는 선택 사항이며 값이 어떻게 표시될지 제어해요.
가능한 옵션에 대한 자세한 내용은 여기에서 볼 수 있어요.분음 부호가 있는 문자의 변환 예제:
# Fills in the object at index zero, converted to a string.
>>> 'An e with an umlaut: {0!s}'.format('ë')
'An e with an umlaut: ë'
# Fills in the object at index zero, converted to a repr.
>>> 'An e with an umlaut (object representation): {0!r}'.format('ë')
"An e with an umlaut (object representation): 'ë'"
# Fills in the object at index zero, converted to ASCII.
>>> 'An e with an umlaut converted into ASCII: {0!a}'.format('ë')
"An e with an umlaut converted into ASCII: '\xeb'"
# Fills in the object in the first position,
# then fills in the object in the second position formatted as a repr.
>>> 'She said her name is not {} but {!r}.'.format('Chloe', 'Zoë')
"She said her name is not Chloe but 'Zoë'."
자주 쓰이는 형식 지정자의 예:
# Integer and binary/hex representations of the same number.
>>> my_num = 42
>>> f"{my_num} in binary is {my_num:b}. In hex, it is {my_num:x}"
"42 in binary is 101010. In hex, it is 2a"
# Alignment: left (<), right (>), and center (^) using up to ten characters total.
>>> f"[{"left":<10}] [{"right":>10}] [{"center":^10}]"
"[left ] [ right] [ center ]"
# Float precision and scientific notation (up to three decimal places).
>>> pi = 3.141592653589793
>>> f"Fixed: {pi:.3} Scientific: {pi:.3e}"
"Fixed: 3.142 Scientific: 3.142e+00"
# Thousands separator and percentage.
>>> balance = 1000
>>> rate = 0.0225
>>> f"Balance: ${balance:,.0f} Interest rate: {rate:.1%}"
"Balance: $1,000 Interest rate: 2.2%"
# Putting it all together:
>>> items = [("Widget", 1250, 9.991), ("Gadget", 37, 24.503), ("Doohickey", 4, 149.002)]
>>> header = f"{"Item":<12} {"Qty":>6} {"Price":>9}"
>>> print(header)
Item Qty Price
>>> for name, qty, price in items:
... print(f"{name:<12} {qty:>6} {price:>9.2f}")
Widget 1250 9.99
Gadget 37 24.50
Doohickey 4 149.00
더 많은 예제는 이 문서 끝부분에 나와 있어요.
% 포매팅, 또는 printf() 스타일 포매팅포매팅에 % 연산자를 사용하는 것은 Python에서 가장 오래된 문자열 포매팅 방법이에요.
C 언어에서 왔으며, 위치 기반 인자를 사용해 str을 만들 수 있어요.
이 방법은 f-string과 str.format()에 밀려났고, 그래서 % 포매팅의 별명이 _"옛 스타일"_이에요.
여전히 Python 2나 레거시 코드에서 찾아볼 수 있어요.
이 방법은 Python 3.x에서도 동작하지만, 오류가 나기 쉽고 효율이 떨어지며 사용할 수 있는 옵션도 적고 자리 표시자와 인자가 맞지 않으면 예외가 발생할 수 있어서 보통은 피해요.
% 연산자를 쓰는 것은 printf()와 비슷해서 _printf 포매팅_이라고도 불려요.
# Assigning a variable.
>>> name = "Anna-conda"
# Building a string using %.
>>> "The snake's name is %s." % name
"The snake's name is Anna-conda."
위 예제에서 % 연산자는 런타임에 자리 표시자 %s를 변수 name으로 바꿔요.
문자열에 여러 변수를 넣고 싶다면, % 뒤에 자리 표시자마다 객체 하나씩 담은 튜플을 넘겨야 해요:
# Assigning variables.
>>> name = "Billy the Kid"
>>> fruit = "grapes"
# Building a string using %.
>>> "Surprisingly, %s's favorite snack was %s." %(name, fruit)
"Surprisingly, Billy the Kid's favorite snack was grapes."
string.Template()는 (Python 3.14의 t-strings와 혼동하면 안 돼요) string 모듈(내장 str 타입과는 달라요)의 클래스로, Python 표준 라이브러리에 속하지만 사용하려면 임포트해야 해요.
템플릿 문자열은 $ 기반 치환을 지원하며, 여기서 언급한 다른 방법들보다 훨씬 단순하고 기능도 적어요.
하지만 복잡한 국제화나 외부 입력 정제에는 아주 유용할 수 있어요.
string.Template은 임의의 표현식을 평가하거나 객체 속성에 접근하는 것을 막아서 형식 문자열 주입 공격을 완화하므로, 신뢰할 수 없는 사용자 입력에 더 안전한 것으로 여겨져요.
>>> from string import Template
>>> name = "Anna-Conda"
# Creating a Template with placeholder text.
>>> template_string = Template("The snake called `$snake_name` has escaped!")
# Calling .substitute() to replace the placeholder with a value.
>>> template_string.substitute(snake_name=name)
'The snake called `Anna-Conda` has escaped!'
string.Template에 대한 더 자세한 내용은 Python 문서에서 볼 수 있어요.
이렇게 많은 옵션과 미니 언어가 있는데, Python 문자열을 포매팅할 때 무엇을 골라야 할까요? 빠르게 참고할 몇 가지 지침이에요:
f-string은 가장 최신이고 읽기 가장 쉬워요.
국제화가 필요 없다면 Python 3.6 이상에서는 이것을 우선적으로 쓰는 게 좋아요.
str.format()은 다재다능하고 강력하며, gnu gettext와 대부분의 Python 버전 모두와 호환돼요.
단순함, 안전성, 또는 많은 국제화가 필요하다면, 사용자 입력을 다뤄야 할 때 위험을 줄이고 번역 문자열을 감싸는 데 string.Template()을 쓸 수 있어요.
% 연산자는 최신 Python에서도 동작하지만, 새 코드에서는 보통 사용을 지양해요.
주로 오래된 코드베이스와의 호환성을 위해 써요.
% 포매팅은 다른 방법보다 오류가 나기 쉽고(기능도 적으며), ASCII가 아닌 문자를 표시할 때 문제가 생길 수 있어요.
사용할 생각이라면 지원 세부 사항을 해당 Python 배포판에서 확인해 보세요.
더 나아가고 싶다면, 포매팅에 관한 모든 것과 Python 문자열 포매팅 모범 사례가 좋은 출발점이에요.