1. 개요
다음과 같이 파이썬 코드에서는 문자열을 많이 쓴다.
1) 사용자 인터페이스 또는 명령줄 유틸리티에 메시지 표시
2) 파일과 소켓에 데이터 쓰기
3) 어떤 일이 잘못되었는지 Exception에 자세히 기록
4) 디버깅
형식화(formatting)는 미리 정의된 문자열에 데이터 값을 끼워 넣어서 사람이 보기 좋은 문자열로 저장하는 과정이고 파이썬에서는 4가지 방식이 있다. 하지만 한 가지 방법을 제외하면 나머지는 모두 심각한 단점이 있으므로 이를 이해하고 피해야한다.
2. % 형식화 연산자 사용
파이썬에서 문자열을 형식화하는 가장 일반적인 방법이다.
a = 0b10111011
b = 0xc5f
print('이진수: %d, 십육진수: %d' % (a, b))
>>>
이진수: 187, 십육진수: 3167
이는 C의 printf 함수에서 비롯됐으며, 파이썬에 이식되었다. 파이썬을 처음 접하는 프로그래머 중 상당수는 (다른 언어에서 써봤기 때문에) 익숙하고 사용하기 간편하다는 이유로 C 스타일 형식 문자열을 사용한다.
하지만 파이썬에서 C 스타일 형식 문자열을 사용하는 데는 4가지 문제점이 있다.
2-1. 오른쪽 tuple관련 오류
오른쪽에 있는 tuple 내 데이터 값의 순서를 바꾸거나 값의 타입을 바꾸면 타입 변환이 불가능해지므로 오류가 발생할 수 있다.
key = 'my_var'
value = 1.234
formatted = '%-10s = %.2f' % (key, value)
print(formatted)
>>>
my_var = 1.23
위의 경우 잘 실행 되지만, 아래의 경우 type error 발생
reordered_tuple = '%-10s = %.2f' % (value, key)
>>>
Traceback ...
TypeError: must be real number, not str
이런 오류를 피하기 위해서는 % 연산자의 좌우가 서로 잘 맞는지 계속 검사해야하므로 실수하기도 쉽고 매우 번거롭다.
2-2. 값 변경시 읽기 어려움
형식화 하기 전에 값을 살짝 변경해야 한다면 식을 읽기가 매우 어려워진다. (값을 살짝 변경하는 경우가 꽤 많이 있다.)
pantry = [
('아보카도', 1.25),
('바나나', 2.5),
('체리', 15),
]
for i, (item, count) in enumerate(pantry):
print('#%d: %-10s = %.2f' % (i, item, count))
>>>
#0: 아보카도 = 1.25
#1: 바나나 = 2.50
#2: 체리 = 15.00
위의 경우를 살짝 변경한다면,
for i, (item, count) in enumerate(pantry):
print('#d: %-10s = %d' % (
i + 1,
item.title(),
round(count)))
>>>
#1: 아보카도 = 1
#2: 바나나 = 2
#3: 체리 = 15
tuple의 길이가 너무 길어져서 여러 줄에 나눠 써야 하고 가독성이 나빠진다.
2-3. 같은 값을 여러 번 사용해야 하는 경우
같은 값을 여러 번 사용해야 하는 경우 튜플에서 같은 값을 여러 번 반복해야 하는 번거로움이 있다.
template = '%s는 음식을 좋아해. %s가 요리하는 모습을 봐요.'
name = '철수'
formatted = template % (name, name)
print(formatted)
>>>
철수는 음식을 좋아해. 철수가 요리하는 모습을 봐요.
2-4. 딕셔너리를 사용하는 경우 번잡함
딕셔너리를 통해 첫번째(tuple 순서 변경시 오류) 문제점을 해결할 수 있다.
key = 'my_var'
value = 1.234
old_way = '%-10s = %.2f' % (key, value)
new_way = '%(key)-10s = %(value).2f' % {
'key': key, 'value': value} # 원래 방식
reordered = '%(key)-10s = %(value).2f % {
'value': value, 'key': key} # 바꾼 방식
assert old_way == new_way == reordered
한편, 세번째(같은 값을 여러 번 반복하는 번거로움) 문제점도 해결 가능하다. 딕셔너리를 사용하면 여러 형식 지정자에 같은 키를 지정할 수 있기 때문이다.
name = '철수'
template = '%s는 음식을 좋아해. %s가 요리하는 모습을 봐요.'
before = template % (name, name) # 튜플
template = '%(name)s는 음식을 좋아해. %(name)s가 요리하는 모습을 봐요.'
after = template % {'name': name} # 딕셔너리
하지만 다른 문제가 심해지거나 새로운 문제가 생긴다.
두번째(값 변경시) 문제점의 경우 식이 더 길어지고 시각적으로 잡음이 더 많아진다.
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
결론은 딕셔너리를 사용하면 중복, 순서와 같은 문제를 해결할 수 있지만 더 번잡해진다.
menu = {
'soup': 'lentil',
'oyster': 'tongyoung',
'special': 'schnitzel',
}
template = ('Today\'s soup is %(soup)s, '
'buy one get two %(oyster)s oysters, '
'and our special entree is %(special)s.')
formatted = template % menu
print(formatted)
>>>
Today's soup in lentil, buy one get two tongyoung oysters, and our special entree is schinitzel.
3. 내장 함수 format과 str.format
파이썬 3부터 더 표현력이 좋은 고급 문자열 형식화 기능이 도입되었고 이 기능은 format 내장 함수를 통해 모든 파이썬 값에 사용할 수 있다.
예를 들어 다음 코드는 새로운 옵션(천 단위 구분자 ,와 중앙에 값을 표시하는 ^)을 사용해 값을 형식화한다.
a = 1234.5678
formatted = format(a, ',.2f')
print(formatted)
b = 'my 문자열'
formatted = format(b, '^20s')
print('*', formatted, '*')
>>>
1,234.57
* my 문자열 *
그리고 {} 메서드를 통해 여러 값에 대해 한꺼번에 기능 적용이 가능하다.
key = 'my_var'
value = 1.234
formatted = '{} = {}'.format(key, value)
print(formatted)
>>>
my_var = 1.234
하지만, format 메서드도 두 번째 문제점은 해결하지 못한다. C 스타일과 가독성 면에서 거의 차이가 없을뿐더러, 둘 다 읽기에 좋지 않다.
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))
4. 인터폴레이션을 통한 형식 문자열
앞서 언급된 문제를 완전히 해결하기 위해 파이썬 3.6부터 인터폴레이션(interpolation)을 통한 형식 문자열(f-문자열)이 도입됐다. 네 번째 문제점인 키와 값을 불필요하게 중복 지정해야 하는 경우를 없애주며 간결함을 제공한다.
key = 'my_var'
value = 1.234
formatted = f'{key} = {value}'
print(formatted)
>>>
my_var = 1.234
콜론 뒤에 사용할 수 있는 내장 미니 언어도 사용가능하다.
formatted = f'{key!r<10} = {value:.2f}'
print(formatted)
>>>
'my_var' = 1.23
다음은 f-문자열과 C 스타일, str.format 메서드 사용하는 경우의 비교이다. f-문자열이 항상 더 짧다.
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
한편, 다음과 같이 파이썬 식을 형식 지정자 옵션에 넣을 수도 있다.
places = 3
number = 1.23456
print(f'내가 고른 숫자는 {number: .{places}f}')
>>>
내가 고른 숫자는 1.235
'언어 > 파이썬' 카테고리의 다른 글
[Effective Python] BETTER WAY 9 - for나 while 루프 뒤에 else 블록을 사용하지 말라 (0) | 2022.12.31 |
---|---|
[Effective Python] BETTER WAY 7 - range보다는 enumerate를 사용하라 (0) | 2022.12.29 |
[Effective Python] BETTER WAY 6 - 인덱스를 사용하는 대신 대입을 사용해 데이터를 언패킹해라 (0) | 2022.12.29 |
[Effective Python] BETTER WAY 5 - 복잡한 식을 쓰는 대신 도우미 함수를 작성하라 (0) | 2022.12.28 |
[Effective Python] BETTER WAY 3 - bytes와 str의 차이를 알아두라 (0) | 2022.12.24 |