BETTER WAY 4. C 스타일 형식 문자열을 str.format과 쓰기보다는 f-문자열을 통한 인터폴레이션을 사용하라

C__W.A·2021년 6월 29일
0

이펙티브 파이썬

목록 보기
3/8
post-thumbnail

✅ C 스타일 형식 문자열에 네가지 문제점

❌ 첫번째 문제점

형식화 식에서 오른쪽에 있는 tuple 내 데이터 값의 순서를 바꾸거나 값의 타입을 바꾸면 타입 변환이 불가능하므로 오류가 발생 할 수 있다.

key = "my_var"
value = 1.234
show = "%-10s = %.2f" %(key, value)
print(show)
-> my_var = 1.23

reshow = "%-10s = %.2f" %(value, key)
-> TypeError: must be real number, not str

⚠ 출력할 데이터 타입 다르기 때문에 에러가 발생한다.

reshow_string = "%.2f = %-10s" %(key, value)
-> TypeError: must be real number, not str

형식 문자열에 순서를 바꿔도 같은 오류가 발생한다.

✍🏻이런식의 형식 문자열 사용하면 좌우가 잘 맞는지 계속 검사해야하는 번거로움이 있다. 검사하는 과정에서도 실수 할 수도 있다.

❌ 두번째 문제점

형식화 하기 전에 값을 살짝 변경해야 한다면 식을 읽기가 매우 어려워진다.

fruit = [
	("바나나", 1.25),
    	("딸기", 3.6),
    	("블루베리", 2.5),
]

for idx, (item,price) in enumerate(fruit):
	print("#%d: %-10s = %.2f" %(idx, item, price))
    ->
      #0: 바나나        = 1.25
      #1: 딸기         = 3.60
      #2: 블루베리       = 2.50

이 코드에서 약간에 변화를 줘서 1번부터 시작하게 만들고 싶다. 코드를 수정하면

fruit = [
	("바나나", 1.25),
    	("딸기", 3.6),
    	("블루베리", 2.5),
]

for idx, (item,price) in enumerate(fruit):
	print("#%d: %-10s = %.2f" % (
        idx + 1,
        item.title(),
        round(price)
    ))

이런 식으로 코드를 수정해야 한다.

✍🏻이런식으로 코드를 수정할 경우 형식화 식에 있는 tuple에 길이가 너무 길어져서 여러 줄에 나줘서 써야 하는 문제가 발생한다. 그로 인해 가독성이 나빠진다.

❌ 세번째 문제점

형식화 문자열에서 같은 값을 여러 번 사용하고 싶다면 튜플에서 같은 값을 여러 번 반복해야 한다.

template = "%s는 음식을 좋아해. %s가 요리하는 모습을 봐요."
name = "철수"
formatted = template % (name, name)
print(formatted)
-> 
철수는 음식을 좋아해. 철수가 요리하는 모습을 봐요.

name이란 변수를 반복해서 사용해야 한다. 이런식의 코딩은 실수하기 쉽고 가독성도 안좋다. title 메서드를 사용할 때도 하나는 title을 쓰고 실수로 다른 하나에는 안 쓰는 경우가 발생 할 수 있다

name = "철수"
formatted = template % (name.title(), name)

❌ 네번째 문제점

네번째 문제점은 C 스타일 형식화 식의 딕셔너리를 사용하는 방식이다. 이러한 방식은 첫번째 문제점과 형식 지정자에 같은 키를 지정할 수 있어서 같은 값을 반복하지 않아 세번째 문제점도 해결할 수 있다. 하지만 딕셔너리를 사용하면 문장이 번잡스러워 진다.

soup = "lentil"
formatted = "Today\'s soup is %(soup)s." % {'soup' : soup}
print(formatted)

이런 불필요한 중복으로 인해 딕셔너리를 사용하는 형식화 식이 너무 길어진다.

menu = {
    'soup' : 'lentil',
    'oyster': 'tongyoung',
    'special': 'schnitzel',
}

template = (
    'today\'s soup is %(soup)s,'
    'buy one get two %(oyster)s oyster,'
    'and our special entree is %(special)s.'
)

formatted =  template % menu
print(formatted)

이런식의 코딩 스타일은 이해하기 위해 긴 코드를 여러번 위아래로 훑으면서 형식화 문자열과 딕셔너리를 뒤져야 한다. 도중에 약간에 코드를 수정한다면 가독성도 안좋고 버그 찾기도 힘들다.

✅내장 함수 format과 str.format

파이썬3 부터는 오랜된 C스타일 형식화 문자열보다 더 표현력이 좋은 고급 문자열 형식화 기능이 도입됐다.
사용 법

a = 1234.5678
formatted = format(a, ",.2f")
print(formatted)

b = "this is 문자열"
formatted = format(b,'^20s')
print(formatted)

str타입에 새로 추가된 format 메서드를 호출하면 여러값을 한꺼번에 이 기능을 적용할 수 있다.

key = "my_var"
value = 1.234

formatted = '{} = {}'.format(key, value)
print(formatted)

->
my_var = 1.234

이런식으로 사용하면 C 스타일과 다르게 데이터값 변수 위치를 신경 안써도 된다. 단순하게 순서대로 입력된다고 생각하면 된다. 값을 넣을 때 어떤 형식으로 변환할 지 정할 수 있다. 만약 따로 순서를 정하고 싶다는 아래 코드처럼 번호를 입력해주면 된다.(✔첫번째 문제점 해결)

formatted = "{1} = {0}".format(key, value)
print(formatted)
->
1.234 = my_var

같은 위치 인덱스를 여러번 사용할 수도 있다.

name = "john"
formatted = "{0}는 음식을 좋아해. {0}가 요리하는 모습을 봐요".format(name)

세번째 문제점 해결

😀format은
첫번째 문제점세번째 문제점을 해결 할 수 있다.

하지만...
🤔두번째 문제점네번째 문제점은 해결되지 않는다.

for i, (item, count) in enumerate(pantry):
	old_style = "%d: %-10s = %d" %(
    		i+1,
    		item.title(),
    		round(count)
    )
    
for i, (item, count) in enumerate(pantry):
	new_style = "#{}: {<10s} = {}".format(
    		i+1,
    		item.title(),
    		round(count)
    )

두번째 문제점인 형식이 길어져서 가독성이 안 좋아지는 문제는 해결되지 않는다.

old_template = (
	'Today \`s soup is %{soup}s,'
)
old_formatted = old_template % {
	'soup': 'lentil'
}

new_template = (
	'Today \'s soup is {soup}
)
new_formatted = new_template.format(
	soup: 'lentil'
)

네번째 문제점도 별차이 없다.

위치 지정자의 표현력 부족으로 인해 생기는 제약이 너무 커서 str의 format 메서드의 가치를 떨어뜨린다.

✅ 인터폴레이션을 통한 형식 문자열

파이썬 3.6부터 이러한 문제점들을 해결하기 위해 인터포레이션을 통한 형식 문자열(f-문자열)이 도입됐다. 새로운 방식은 문자열 앞에 f 문자를 붙여야 한다.
f-문자열은 형식 문자열의 표현력을 극대화하고, 앞으로 설명한 네번째 문제점인 형식화 문자열에서 키와 값을 불필요하게 중복 지정해야 하는 경우를 없애준다.

key = "my_var
value = 1.234

formatted = f'{key} = {value}'
print(formatted)
➜
my_var = 1.234

✍🏻유니코드나 repr문자열로 변환가능

formatted = f'{key!r:<10} = {value:.2f}'
print(formatted)'my_var'	= 1.23

✍ C스타일 형식화나 str.fomate 메서드을 한 줄로 표현 가능

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(),l
        round(count)
    )
    
    	f_string = f'#{i+1}: {item.title():<10s} = {round(count)}'

😀 format 메서드 C스타일과 비교해도 f-문자열이 휄씬 짧아지고 가독성이 좋다.

✍ 한 줄로도 가능하지만 여러줄로 표현 가능하다.

for i, (item, count) in enumerate(pantry):
	print(f'#{i+1}'
    f'{item.title():<10s} = '
    f'{round(count)}'
    )#1: 아보카도 	= 1
#2: 바나나		= 2
#3: 체리		= 15
 

😀 한줄 보다 긴 코드가 되지만, 다른 접근 방식으로 표현한 여러 줄 코드보다 휄씬 깔끔하다.

✍ 파이썬 식을 형식 지정자 옵션에 넣을 수도 있다.

places = 3
number = 1.23456
print(f'내가 고른 숫자는 {number:.{places}f}')
➜
내가 고른 숫자는 1.235

✨f-문자열이 제공하는 표현력, 간결성, 명확성을 고려할 때 파이썬 프로그래머가 사용할 수 있는 형식화 옵션 중에서 f-문자열이 최고다. 값을 문자열로 형식화해야 하는 상황을 만나게 되면 다른 다안 대신 f-문자열을 택하라

✔이 글은 코딩의 기술 - 이펙티브 파이썬을 참고하여 작성했습니다.

profile
기술은 문제를 해결하기 위해 존재한다

0개의 댓글