파이썬 문자열 포맷팅 이해하기

파이썬 문자열 포맷팅 이해하기

문자열 포맷팅 (Formatted String)

파이썬에서는 문자열 포맷팅을 사용해 특정 변수를 원하는 문자열의 특정 위치에 삽입할 수 있다. 이런 방식을 문자열 보간법(string interpolation)이라고 부른다.

파이썬에서 대표적인 문자열 포맷팅 방식에는 파이썬 3.6 버전에 추가된f-string이라는 방식이 있다. 비교적 최신에 추가 됬으므로 물론 그 전에 이미 다른 방식의 포맷팅이 파이썬에서는 존재했다. 이미 존재했던 방식들보다 f-string이 매우 쉽기 때문에 이 글에서는 다른 방식은 다루지 않을려고 한다. 혹시나 궁금하다면 이 링크에서 이 전의 포맷팅 방식이 얼마나 불편했는지 확인할 수 있다.

우선 먼저 f-string 포맷팅이 어떻게 사용되는지 확인해보자.

>>> f"{name} has {heads} heads and {arms} arms"
'Zaphod has 2 heads and 3 arms'
위 예시를 보며 주목해야 할 두 가지 중요한 점이 있다.
  1. 따옴표를 열기 전, 문자열 리터럴은 문자 'f'와 시작해야 한다.
  2. 변수 이름은 중괄호({ })로 둘러싸여 있고 변수에 상응하는 값으로 교체된다.

임의 표현식(Arbitrary Expression)

f-string 포맷팅을 사용하면 다음과 같이 중괄호 사이에 원하는 파이썬 표현식을 사용할 수도 있다.

>>> n = 3
>>> m = 4
>>> f"{n} times {m} is {n*m}"
'3 times 4 is 12'

f-string런타임일 때 평가되기 때문에, 모든 유효한 파이썬 표현식을 f-string 안에 사용할 수 있다. 더 많은 사용법을 확인해보자.

# 기본 연산
>>> f"{2 * 37}"
'74'

# 직접적인 메서드 호출
>>> f"{name.lower()} is funny."
'eric idle is funny.'

# 함수 호출
>>> def to_lowercase(input):
...     return input.lower()

>>> name = "Eric Idle"
>>> f"{to_lowercase(name)} is funny."
'eric idle is funny.'

# 클래스 안에서 생성된 객체 사용
class Comedian:
    def __init__(self, first_name, last_name, age):
        self.first_name = first_name
        self.last_name = last_name
        self.age = age

    def __str__(self):
        return f"{self.first_name} {self.last_name} is {self.age}."

    def __repr__(self):
        return f"{self.first_name} {self.last_name} is {self.age}. Surprise!"

다음과 같이 클래스를 변수에 할당하고 f-string에서 사용하는 것도 가능하다.

>>> new_comedian = Comedian("Eric", "Idle", "74")
>>> f"{new_comedian}"
'Eric Idle is 74.'

__str__()__repr__() 메서드는 객체가 문자열로 표시되는 방법을 다룸으로, 클래스를 정의할 때 둘 중 하나 이상의 메서드를 포함해야한다. 하나를 고른다면, __repr__() 메서드가 __str__() 메서드 대신 사용될 수 있음으로 __repr__() 메서드를 사용하는 것을 추천한다.

f-string은 기본으로 __str__() 메서드를 사용하지만, 변환 플래그(conversion flag)를 포맷팅안에 포함하면 __repr__() 메서드를 사용할 수 있다.

>>> f"{new_comedian}"
'Eric Idle is 74.'
>>> f"{new_comedian!r}"
'Eric Idle is 74. Surprise!'

멀티라인 포맷팅(Multiline f-strings)

멀티라인 문자열에서도 f-string을 사용하는 것이 가능하다.

>>> name = "Eric"
>>> profession = "comedian"
>>> affiliation = "Monty Python"
>>> message = (
...     f"Hi {name}. "
...     f"You are a {profession}. "
...     f"You were in {affiliation}."
... )
>>> message
'Hi Eric. You are a comedian. You were in Monty Python.'

여기서 중요한 점은 멀티라인 문자열의 각 줄마다 f로 시작해주어야 한다는 점이다. 그렇지 않았을 때, 다음과 같이 포맷팅이 사용되지 않은 일반적인 문자열을 얻게된다.

>>> message = (
...     f"Hi {name}. "
...     "You are a {profession}. "
...     "You were in {affiliation}."
... )
>>> message
'Hi Eric. You are a {profession}. You were in {affiliation}.'

F-string의 속도

f-string의 f는 빠르다는 의미의 fast를 의미할 수도 있다. 이 전에 존재하던 %-formatting이나 str.format() 방식보다 f-string 포맷팅 방식이 빠를 수 있는 이유는 상수 값이 아닌 런타임 중에 평가되는 표현식이기 때문이다.

다음 코드를 보면 f-string 포맷팅이 제일 빠른 속도를 내는 것을 확인할 수 있다.

# %-formatting
>>> import timeit
>>> timeit.timeit("""name = "Eric"
... age = 74
... '%s is %s.' % (name, age)""", number = 10000)
0.003324444866599663

# str.format()
>>> timeit.timeit("""name = "Eric"
... age = 74
... '{} is {}.'.format(name, age)""", number = 10000)
0.004242089427570761

# f-strings
>>> timeit.timeit("""name = "Eric"
... age = 74
... f'{name} is {age}.'""", number = 10000)
0.0024820892040722242

Reference