형식화(Formatting)는 미리 정의된 문자열에 데이터 값을 끼워 넣어서 완성된 문자열을 만드는 것!
name = '아이펠'
print('안녕하세요! 저는 %s입니다.' % name)
# 안녕하세요! 저는 아이펠입니다.
'저는 000입니다.'에서 000에 들어가는 문자를 따로 %형식자를 이용해 완성된 문자열을 만들게 된다. 하지만 % 연산자를 사용하게 되면 다양한 단점이 발생한다.
key = 'my_var'
value = 1.234
print('%s = %f' %(key, value))
# key와 value의 순서를 바꾸면?
print('%s = %f' %(value, key))
>>>
TypeError: must be real number, not str # value는 float, key는 str
pantry = [
('아보카도', 1.25),
('바나나', 2.5),
('체리', 15),
]
for i, (item, count) in enumerate(pantry):
print('#%d: %-10s = %.2f' % (i, item, count))
#위의 for문 안의 튜플에는 짧아서 괜찮지만 값을 조절하기 위해
#수식을 추가할 경우 tuple의 길이가 너무 길어져서 여러 줄에 나눠 써야하고
#이로 인해 가독성이 나빠진다.
for i, (item, count) in enumerate(pantry):
print('#%d: %-10s = %d' % (
i + 1,
item.title(),
round(count)))
>>>
#0: 아보카도 = 1.25
#1: 바나나 = 2.50
#2: 체리 = 15.00
#1: 아보카도 = 1
#2: 바나나 = 2
#3: 체리 = 15
template = '%s는 음식을 좋아해. %s가 요리하는 모습을 봐요.'
name = '철수'
formatted = template % (name, name) #이렇게 name을 여러번 반복해야 한다.
print(formatted)
name = 'bruno'
formatted = template % (name.title(), name.title())
#이 때, title 메서드 호출을 잊어버릴 수도 있다.
print(formatted)
>>>
철수는 음식을 좋아해. 철수가 요리하는 모습을 봐
for i, (item, count) in enumerate(pantry):
before = '#%d: %-10s = %d' % (
i + 1,
item.title(),
round(count))
after = '#%(loop)d: %(item)-10s = %(count)d' % {
'loop': i + 1,
'item': item.title(),
'count': round(count),
}
# 각 키를 최소 두 번(형식지정자, 딕셔너리 키, [변수 이름]) 이상 사용하게 된다.
assert before == after
key = 'my_var'
value = 1.234
old_way = '%-10s = %.2f' % (key, value)
new_way = '%(key)-10s = %(value).2f' % {
'key' : key, 'value' : value}
→ 문제점 1, 문제점 3 은 해결할 수 있지만 문제점 4를 유발한다.
key = 'my_var'
value = 1.234
formatted = ' {} = {}'.format(key,value)
#format 메서드에 전달된 인자 중 순서상 같은 위치에 있는 위치 지정자에 들어간다.
print(formatted)
formatted = '{1} = {0}'.format(key, value)
print(formatted)
>>>
1.234 = my_var
→ format에 넘기는 인자의 순서를 바꾸지 않아도 형식화 문자열을 사용해 형식화한 값의 출력 순서를 변경할 수 있다.
→ 문제점 1 해결
formatted = '{0}는 음식을 좋아해. {0}가 요리하는 모습을 봐요.'.format(name)
print(formatted)
>>>
철수는 음식을 좋아해. 철수가 요리하는 모습을 봐요.
→ 형식화 문자열 안에서 위치 인덱스를 여러번 활용할 수 있어 format메서드에 값을 여러번 반복할 필요가 없다.
→ 문제점 3 해결
for i, (item, count) in enumerate(pantry):
old_style = '#%d: %-10s = %d' % (
i + 1,
item.title(),
round(count))
new_style = '#{}: {:<10s} = {}'.format(
i + 1,
item.title(),
round(count))
assert old_style == new_style
→ 형식화를 하기 전에 값을 변경해야 하는 경우에는 코드 읽기가 어려워진다. C 스타일 형식화와 새로운 형식화는 가독성 면에서 차이가 없다.
→ 문제점 2 해결 불가
old_template = (
'Today\'s soup is %(soup)s, '
'buy one get two %(oyster)s oysters, '
'and our special entrée is %(special)s.')
old_formatted = template % {
'soup': 'lentil',
'oyster': 'tongyoung',
'special': 'schnitzel',
}
new_template = (
'Today\'s soup is {soup}, '
'buy one get two {oyster} oysters, '
'and our special entrée is {special}.')
new_formatted = new_template.format(
soup='lentil',
oyster='tongyoung',
special='schnitzel',
)
assert old_formatted == new_formatted
→ 딕셔너리에 미해 작은 따옴표 등이 덜 사용되긴 하지만 여전히 키가 반복되는 경우의 중복을 줄여주지는 못한다.
→ 문제점 4 해결 불가
위와 같이 해결방법으로 제시된 딕셔너리를 사용하는 방법이나 format을 사용하는 방법은 해결하지 못하는 문제점이 존재했다. 이를 해결하기 위해 인터폴레이션을 통한 형식 문자열(f-문자열)이 도입됐다.
→ 형식 문자열 앞에 f문자를 붙인다.
key = 'my_var'
value = 1.234
formatted = f'{key} = {value}'
print(formatted)
>>>
my_var = 1.234
→ 데이터의 순서나 타입이 바뀌더라도 오류가 발생하지 않는다.
→ 문제점 1 해결
formatted = f'{key!r:<10} = {value:.2f}'
print(formatted)
>>>
'my_var' = 1.23
→ format함수의 형식 지정자 안에서 콜론 뒤에 사용할 수 있는 내장 미니 언어를 사용할 수 있다.
→ 값을 유니코드나 repr 문자열로 변환하는 기능 역시 사용할 수 있다.
f_string = f'{key:<10} = {value:.2f}'
c_tuple = '%-10s = %.2f' % (key, value)
str_args = '{:<10} = {:.2f}'.format(key,value)
str_kw = '{key:<10} = {value:.2f}'.format(key=key,value=value)
c_dict = '%(key)-10s = %(value).2f % {'key': key, 'value': value}
assert c_tuple == c_dict == f_string
assert str_args == str_kw == f_string
→f-문자열을 사용한 형식화는 앞에서 살펴본 다른 방법에 비해 항상 더 짧다.
for i, (item, count) in enumerate(pantry):
old_style = '#%d: %-10s = %d' % (
i + 1,
item.title(),
round(count))
new_style = '#{}: {:<10s} = {}'.format(
i + 1,
item.title(),
round(count))
f_string = f'#{i+1}: {item.title():<10s} = {round(count)}'
assert old_style == new_style == f_string
→ f-문자열을 사용하면 위치 지정자 중괄호 안에 완전한 파이썬 식을 넣을 수 있다.
→ 값을 약간 변경하고 싶을 때도 간결한 구문으로 표기할 수 있다.
→ 문제점 2 해결
for i, (item, count) in enumerate(pantry):
print(f'#{i+1}: '
f'{item.title():<10s} = '
f'{round(count)}')
>>>
#1: 아보카도 = 1
#2: 바나나 = 2
#3: 체리 = 15
→ 연속된 문자열을 서로 연결해주는 기능을 사용해 f-문자열을 여러 줄로 나눌 수 있다.
→ 한줄짜리 코드보다 길지만, 다른 방식에 비해 깔끔하다.
places = 3
number = 1.23456
print(f'내가 고른 숫자는 {number:.{places}f}')
>>>
내가 고른 숫자는 1.235
→ 파이썬 식을 형식 지정자 옵션에 넣을 수 있다.