상위 목록: 하위 목록: 작성 날짜: 읽는 데 13 분 소요

문자열(String)

Python에서는 따옴표를 이용하여 문자열을 생성할 수 있습니다.

문자열은 이름, 문장, 파일 경로, 입력받은 값처럼 글자로 이루어진 데이터를 표현하는 자료형(str)입니다. 프로그램이 사용자와 주고받는 데이터는 대부분 문자열이므로, 문자열을 자르고 바꾸고 검사하는 방법은 가장 자주 사용하는 기본기입니다.

문자열은 문자들이 순서대로 나열된 시퀀스(Sequence)이므로, 목록(List)처럼 인덱싱(Indexing)과 슬라이싱(Slicing)으로 문자를 꺼내 볼 수 있습니다.

하지만 문자열은 변경할 수 없는(Immutable) 자료형입니다. 목록과 달리 문자열의 문자를 직접 수정하거나 추가, 삭제할 수 없습니다.

문자열을 바꾸고 싶다면 +, replace() 등을 이용해 새로운 문자열을 만들어 다시 할당해야 합니다.

또한 문자열은 목록이 아니므로 append(), insert(), sort()처럼 목록을 직접 변경하는 함수는 사용할 수 없습니다. 대신 문자열 전용 함수(메서드)를 사용합니다.


제 8강에서 사용한 네 가지 기준으로 문자열을 정리하면 다음과 같습니다.

  • 변경 불가능(Immutable) : 한 번 만든 문자열의 내용은 바뀌지 않습니다.
  • 순서가 있음 : 각 문자는 0번부터 시작하는 위치를 가집니다.
  • 중복 허용 : "banana"처럼 같은 문자가 여러 번 나와도 됩니다.
  • 해시 가능(Hashable) : 내용이 바뀌지 않으므로 사전의 key나 집합의 원소로 사용할 수 있습니다.

이번 강좌에서 다루는 모든 문자열 메서드는 원본을 바꾸지 않고 결과를 새 문자열(또는 숫자·목록·논리값)로 반환합니다. 목록 메서드가 원본을 바꾸고 None을 반환하던 것과 정반대이므로, 문자열 메서드의 결과는 반드시 변수에 대입하거나 바로 사용해야 합니다.

이번 강좌에서는 문자열의 생성, 연산, 불변성, 포맷, 대소문자 변환, 공백·접두사 제거, 검색, 분리·치환·결합과 Python 3.14에 추가된 템플릿 문자열을 다룹니다.



생성

문자열은 글자를 작은따옴표(')나 큰따옴표(")로 감싸서 만듭니다. 두 따옴표는 기능이 완전히 같으므로, 문자열 안에 들어갈 따옴표의 종류에 따라 편한 쪽을 선택합니다.

a = 'Python 3.14.6'
b = "It's you"
c = 'It\'s you'
d = "alpha\nbeta"

print(a)
print(b)
print(c)
print(d)
결과
Python 3.14.6
It’s you
It’s you
alpha
beta

작은따옴표('')를 사용하거나 큰따옴표("")를 사용하여 문자열을 생성할 수 있습니다.

문자열 안에 따옴표를 포함시키려면 \을 병기하여 사용가능합니다.

줄바꿈은 \n을 사용하여 줄을 바꿀 수 있습니다.

b는 큰따옴표로 감쌌기 때문에 안쪽의 작은따옴표를 그대로 쓸 수 있고, c는 작은따옴표로 감쌌기 때문에 안쪽 작은따옴표 앞에 \를 붙여 문자열의 끝이 아니라 글자임을 표시했습니다. 두 문자열의 내용은 완전히 같습니다.

d의 \n은 두 글자가 아니라 줄바꿈 문자 하나를 의미하므로, alpha와 beta가 두 줄로 출력됩니다.

  • Tip : \을 사용하는 이스케이프 코드는 \n을 비롯하여 수평 탭 : \t, \ 문자 : \\, 백스페이스 : \b 등이 존재합니다.

  • Tip : 따옴표를 세 번 연속으로 사용(""" 또는 ''')하면 \n 없이도 여러 줄의 문자열을 작성할 수 있습니다.


a = "C:\new"
b = r"C:\new"

print(a)
print(b)
print(len(a), len(b))
결과
C:
ew
C:\new
5 6

Windows의 파일 경로처럼 \가 들어가는 문자열은 의도치 않게 이스케이프 코드로 해석될 수 있습니다.

a의 \n은 줄바꿈 문자 하나로 바뀌었기 때문에 C:와 ew가 두 줄로 출력되고, 길이도 5입니다.

따옴표 앞에 r을 붙인 원시 문자열(Raw String)은 \를 이스케이프로 해석하지 않고 글자 그대로 저장합니다. 그래서 b는 C:\new 그대로 출력되며 길이는 6입니다.

  • Tip : 원시 문자열은 파일 경로나 정규 표현식처럼 \를 많이 쓰는 문자열에 주로 사용합니다.



연산

문자열도 목록과 같은 시퀀스이므로 +, *, 인덱싱, 슬라이싱을 목록과 같은 방식으로 사용할 수 있습니다. 다만 결과는 목록이 아니라 새 문자열입니다.

a = "al"
b = "pha"
c = "alphabet"

print(a + b)
print(a * 2)
print(a[-1])
print(b[0:1])
print(c[0:-1:2])
결과
alpha
alal
l
p
apae

+를 이용하여 문자열끼리 합칠 수 있습니다.

*를 이용하여 문자열을 반복할 수 있습니다.

문자열[index]을 이용하여 index에 위치한 문자를 출력합니다.

문자열[start:end]을 이용하여 start부터 end-1까지의 문자열을 출력합니다.

문자열[start:end:step]을 이용하여 start부터 end-1까지 step간격의 문자열을 출력합니다.

결과를 차례대로 읽으면, "al" + "pha"는 alpha, "al" * 2는 alal, a[-1]은 마지막 문자 l, b[0:1]은 첫 문자 하나인 p입니다.

c[0:-1:2]는 "alphabet"에서 마지막 문자 t를 제외한 "alphabe"를 0, 2, 4, 6번 위치만 골라 a, p, a, e를 이은 apae가 됩니다.

Python에는 문자 하나만 따로 표현하는 자료형이 없으므로, a[-1]의 결과도 길이가 1인 문자열입니다.


print("pha" in "alpha")
print("ap" in "alpha")
print(len("파이썬"))

try:
    "Python" + 3
except TypeError as e:
    print(e)

print("Python" + str(3))
결과
True
False
3
can only concatenate str (not “int”) to str
Python3

문자열에서 in 연산자는 문자 하나가 아니라 연속된 부분 문자열이 들어 있는지 확인합니다. "pha"는 "alpha" 안에 그대로 이어져 있으므로 True이고, "ap"는 a와 p가 모두 있지만 붙어 있지 않으므로 False입니다.

len()은 바이트 수가 아니라 문자의 개수를 반환하므로, 한글 "파이썬"의 길이는 3입니다.

+는 문자열끼리만 연결할 수 있으며, 숫자를 연결하면 TypeError가 발생합니다. 숫자를 문자열에 붙이려면 str()로 먼저 변환하거나, 아래의 포맷 기능을 사용합니다.

  • Tip : 반복문 안에서 +로 문자열을 계속 이어 붙이면 매번 새 문자열이 만들어집니다. 여러 조각을 합칠 때는 조각을 목록에 모은 뒤 join()으로 한 번에 합치는 방법이 효율적입니다.



불변성

문자열이 변경할 수 없다는 것은 문자열을 다룰 때 가장 중요한 성질입니다. 문자열을 “바꾸는” 것처럼 보이는 모든 작업은 실제로는 새 문자열을 만들어 변수에 다시 연결하는 작업입니다.

a = "Python"

print(a[0])
print(len(a))

try:
    a[0] = "J"
except TypeError as e:
    print(e)

b = "J" + a[1:]

print(b)
print(a)
결과
P
6
‘str’ object does not support item assignment
Jython
Python

문자열은 문자열[index]로 읽을 수는 있지만, 문자열[index] = 값처럼 요소에 대입하면 TypeError가 발생합니다.

문자를 바꾸려면 슬라이싱과 +를 이용해 새로운 문자열을 만들어야 합니다.

새 문자열 b를 만들어도 원본 문자열 a는 그대로 유지되는 것을 확인할 수 있습니다.

오류 메시지 'str' object does not support item assignment는 “문자열 객체는 요소 대입을 지원하지 않는다”는 뜻입니다. "J" + a[1:]은 "J"와 "ython"을 이어 붙인 완전히 새로운 문자열입니다.

  • Tip : upper(), replace(), strip() 등 문자열 함수들도 원본을 바꾸지 않고 변경된 새 문자열을 반환합니다. 결과를 사용하려면 변수에 다시 할당해야 합니다.


s = "Python"

s.upper()
print(s)

s = s.upper()
print(s)
결과
Python
PYTHON

s.upper()만 호출하면 대문자로 바뀐 새 문자열이 만들어지지만 아무 변수에도 저장되지 않고 버려집니다. 그래서 첫 번째 출력은 여전히 Python입니다.

s = s.upper()처럼 결과를 다시 대입해야 s가 새 문자열을 가리키게 됩니다. 목록의 a = a.sort()가 잘못된 코드인 것과 반대로, 문자열은 s = s.upper()가 올바른 코드입니다.


a = "Python"
b = a

a = a + "!"

print(a, b, a is b)
결과
Python! Python False

b = a로 같은 문자열을 가리키게 한 뒤 a = a + "!"를 실행하면, 기존 문자열 "Python"이 바뀌는 것이 아니라 새 문자열 "Python!"이 만들어져 a만 그쪽을 가리킵니다. b는 여전히 원래 문자열을 가리키므로 Python이 출력됩니다.

이처럼 문자열은 어디에서도 내용이 바뀌지 않기 때문에, 여러 변수가 같은 문자열을 가리켜도 서로 영향을 주지 않습니다. 목록에서 보았던 “한쪽을 바꾸면 다른 쪽도 바뀌는” 문제가 생기지 않으며, 내용이 고정되어 있으므로 해시값도 변하지 않아 사전의 key로 사용할 수 있습니다.



포맷

문자열 중간에 변수의 값을 끼워 넣어 문장을 만드는 것을 문자열 포맷(Format)이라 합니다. +와 str()로 일일이 연결하는 것보다 읽기 쉽고, 숫자의 자릿수나 정렬 방식도 함께 지정할 수 있습니다.

a = "%d %f %e" % (10, 10, 10)
b = "%o %x" % (8, 16)
c = "%c %s" % ('A', "AAA")

print(a)
print(b)
print(c)
결과
10 10.000000 1.000000e+01
10 10
A AAA

"%포맷형식" % (변수 또는 값)의 형태로 포맷을 구성할 수 있습니다.

문자열 안의 %d, %f 같은 자리 표시자가 왼쪽부터 차례대로 튜플의 값으로 채워집니다. 그래서 a는 10을 정수(10), 실수(10.000000), 지수(1.000000e+01) 형식으로 각각 표현한 문자열이 됩니다.

b에서 8을 8진법으로 표현하면 10, 16을 16진법으로 표현하면 10이므로 10 10이 출력됩니다.


연산 의미
%d 정수
%f 실수
%e 지수
%o 8진법
%x 16진법
%c 문자
%s 문자열
  • Tip : %c는 정수를 입력할 경우 유니코드(Unicode) 코드 값으로 판단하여 해당 문자를 출력합니다.


print("%d" % 3.99)
print("%s" % [1, 2])
print("%.1f%%" % 12.345)
결과
3
[1, 2]
12.3%

%d에 실수를 전달하면 반올림하지 않고 소수점 이하를 버린 정수로 표시합니다. 3.99가 3으로 출력되는 것에 주의합니다.

%s는 어떤 값이든 str()로 변환하여 넣으므로, 목록도 [1, 2]처럼 출력됩니다.

% 포맷에서 % 기호 자체를 출력하려면 %%처럼 두 번 씁니다.


version = "3.14.6"

print("%5d|%-5d|%.2f" % (42, 42, 3.14159))
print("{} {}".format("Python", version))
print(f"Python {version}")
결과
   42|42   |3.14
Python 3.14.6
Python 3.14.6

%5d처럼 %와 형식 사이에 숫자를 넣으면 출력 너비를, %-5d는 왼쪽 정렬을, %.2f는 소수점 이하 자릿수를 지정합니다.

첫 줄의 ` 42는 너비 5칸에 맞춰 왼쪽에 공백 3칸이 채워진 것이고, 42 는 왼쪽 정렬이므로 오른쪽에 공백이 채워진 것입니다. 3.14159는 소수점 둘째 자리까지 반올림되어 3.14`가 됩니다.

% 포맷 외에도 문자열.format()과 f-string(f"...")을 이용해 문자열을 구성할 수 있습니다.

현재는 가독성이 좋은 f-string이 가장 널리 사용되며, 자세한 내용은 17강에서 다룹니다.

  • Tip : % 포맷은 오래된 코드에서 자주 볼 수 있고, format()은 서식 문자열을 미리 만들어 두고 나중에 값을 채울 때 유용합니다. 새로 작성하는 코드에서는 값이 들어갈 자리에 변수 이름이 바로 보이는 f-string을 우선 사용합니다.

  • 문자열 포맷 사용하기 : 17강 바로가기



대소문자 변환

영문 데이터는 같은 단어라도 Python, python, PYTHON처럼 대소문자가 다르게 입력되는 경우가 많습니다. 대소문자 변환 메서드는 출력 형식을 맞추거나, 대소문자와 관계없이 비교할 때 사용합니다.

a = "abcd"
b = "ABCD"

print(a.upper())
print(b.lower())
print("hello world".capitalize())
print("hello world".title())
print("AbCd".swapcase())
결과
ABCD
abcd
Hello world
Hello World
aBcD

문자열.upper()은 소문자를 대문자로 변경합니다.

문자열.lower()은 대문자를 소문자로 변경합니다.

문자열.capitalize()는 첫 글자만 대문자로, 나머지는 소문자로 변경합니다.

문자열.title()은 각 단어의 첫 글자를 대문자로 변경합니다.

문자열.swapcase()는 대문자와 소문자를 서로 바꿉니다.

다섯 메서드 모두 원본 a, b를 바꾸지 않고 변환된 새 문자열을 반환합니다. 숫자나 한글처럼 대소문자 구분이 없는 문자는 그대로 유지됩니다.


print("they're bill's friends".title())
print("PYTHON".lower() == "python")
print("Straße".lower())
print("Straße".casefold())
결과
They’Re Bill’S Friends
True
straße
strasse

title()은 문자가 아닌 글자(공백, 따옴표 등) 다음에 오는 문자를 단어의 시작으로 판단합니다. 그래서 they're의 ' 다음 r까지 대문자가 되어 They'Re처럼 어색한 결과가 나올 수 있습니다.

대소문자를 구분하지 않고 비교하려면 두 문자열을 모두 lower()로 바꾼 뒤 ==로 비교합니다.

문자열.casefold()는 lower()보다 더 강하게 소문자로 바꾸는 메서드입니다. 독일어 ß처럼 lower()로는 바뀌지 않는 문자도 ss로 변환하므로, 여러 언어의 문자열을 대소문자 구분 없이 비교할 때는 casefold()를 사용합니다.



공백 제거

사용자 입력이나 파일에서 읽은 문자열에는 앞뒤에 공백이나 줄바꿈 문자가 붙어 있는 경우가 많습니다. 눈에 보이지 않는 이 문자들 때문에 비교가 실패하는 일이 잦으므로, 문자열을 처리하기 전에 양쪽 공백을 제거하는 것이 일반적입니다.

a = "   l   aa   l   "

print(a.strip())
print(a.rstrip())
print(a.lstrip())
결과
l aa l
   l aa l
l aa l   

문자열.strip()은 양측 공백을 제거합니다.

문자열.rstrip()은 우측 공백을 제거합니다.

문자열.lstrip()은 좌측 공백을 제거합니다.

세 메서드 모두 양 끝의 공백만 제거하며, l aa l처럼 문자 사이에 있는 공백은 그대로 남습니다.


a = "  hello\n"

print(repr(a.strip()))
print(repr("\t text \n".strip()))
print("xxhixx".strip("x"))
결과
‘hello’
‘text’
hi

인수 없이 호출한 strip()은 띄어쓰기뿐 아니라 줄바꿈(\n), 탭(\t) 같은 보이지 않는 공백 문자를 모두 제거합니다. repr()은 문자열을 따옴표와 함께 출력하여 공백이 남아 있는지 눈으로 확인할 수 있게 해 줍니다.

  • Tip : 문자열.strip(chars)처럼 인수를 전달하면 공백 대신 chars에 포함된 문자들을 양측에서 제거합니다.



접두사·접미사 제거

파일 이름에서 확장자를 떼거나, 정해진 머리말을 제거하는 것처럼 문자열의 앞이나 뒤에서 정확히 특정 문자열만 제거해야 할 때가 있습니다. 이때 strip 계열 메서드를 쓰면 의도와 다른 결과가 나오기 쉬우므로 전용 메서드를 사용합니다.

a = "test_tests.py"

print(a.removeprefix("test_"))
print(a.removesuffix(".py"))
print(a.removeprefix("abc"))
print(a.lstrip("test_"))
결과
tests.py
test_tests
test_tests.py
.py

문자열.removeprefix(x)는 문자열이 x로 시작하면 그 부분을 제거한 문자열을 반환합니다. Python 3.9 이상에서 사용할 수 있습니다.

문자열.removesuffix(x)는 문자열이 x로 끝나면 그 부분을 제거한 문자열을 반환합니다. Python 3.9 이상에서 사용할 수 있습니다.

x로 시작하거나 끝나지 않으면 원래 문자열을 그대로 반환합니다.

두 메서드는 딱 한 번만, 그리고 정확히 일치하는 경우에만 제거합니다. 그래서 "test_tests.py"에서 앞의 test_만 사라지고 뒤의 tests는 남습니다.

  • Tip : lstrip("test_")은 "test_"라는 문자열이 아니라 t, e, s, _ 문자 각각을 계속 제거하므로 예상과 다른 결과가 나옵니다. 정확히 앞부분만 제거하려면 removeprefix()를 사용합니다.

마지막 결과 .py는 lstrip()이 t, e, s, _에 해당하는 문자를 앞에서부터 해당하지 않는 문자(.)를 만날 때까지 모두 지웠기 때문에 나온 것입니다.



검출

문자열 안에서 특정 문자나 부분 문자열이 어디에, 몇 번 나오는지 알아야 할 때가 있습니다. 단순히 있는지 없는지만 알면 될 때는 in을, 위치가 필요하면 find()나 index()를, 개수가 필요하면 count()를 사용합니다.

a = "Time is an illusion."

print(a.find('x'))
print(a.index('u'))
print(a.count('i'))
결과
-1
14
4

문자열.find(x)은 해당 문자가 가장 처음에 나온 위치를 반환합니다.

문자열.index(x)은 해당 문자가 가장 처음에 나온 위치를 반환합니다.

문자열.count(x)은 해당 문자의 횟수를 반환합니다.

결과를 읽으면, 'x'는 문자열에 없으므로 find()가 -1을 반환했고, 'u'는 14번 위치에서 처음 나오며, 'i'는 Time, is, illusion에 걸쳐 모두 4번 나옵니다. 위치는 공백과 마침표를 포함하여 0부터 셉니다.

  • Tip : find와 index의 차이점은 find의 경우 찾지 못했을 경우 -1을 반환하며, index는 ValueError가 발생합니다.


a = "banana"

print(a.find("an"))
print(a.find("an", 2))
print(a.rfind("an"))
print(a.count("an"))
print("aaaa".count("aa"))
결과
1
3
3
2
2

find()와 count()는 문자 하나뿐 아니라 여러 글자로 된 부분 문자열도 찾을 수 있습니다.

a.find("an", 2)처럼 두 번째 인수로 검색을 시작할 위치를 지정하면 그 위치부터 찾습니다. 1번 위치의 an을 건너뛰고 3번 위치의 an을 찾습니다.

문자열.rfind(x)는 오른쪽 끝에서부터 찾아 마지막으로 나온 위치를 반환합니다.

count()는 겹치지 않게 개수를 셉니다. "aaaa"에는 aa가 0, 1, 2번 위치에서 시작하는 것처럼 보이지만, 한 번 센 부분은 다시 세지 않으므로 결과는 2입니다.

  • Tip : find()가 반환하는 -1은 문자열의 마지막 위치를 뜻하는 인덱스로도 쓰이므로, a[a.find(x)]처럼 결과를 확인 없이 인덱스로 사용하면 찾지 못했을 때 엉뚱한 문자를 꺼냅니다. 존재 여부만 확인할 때는 x in 문자열이 가장 명확합니다.



분리·치환·결합

문자열을 여러 조각으로 나누는 split(), 일부를 다른 문자열로 바꾸는 replace(), 여러 조각을 하나로 합치는 join()은 데이터를 가공할 때 짝을 이루어 자주 사용합니다. 예를 들어 "1,2,3"처럼 쉼표로 구분된 데이터를 목록으로 나누어 처리한 뒤, 다시 하나의 문자열로 합칠 수 있습니다.

a = "Time is an illusion."
b = "/"

print(a.split())
print(a.replace("Time", "Today"))
print(b.join(a))
print(b.startswith('/'))
결과
[‘Time’, ‘is’, ‘an’, ‘illusion.’]
Today is an illusion.
T/i/m/e/ /i/s/ /a/n/ /i/l/l/u/s/i/o/n/.
True

문자열.split()은 공백마다 분리하여 리스트로 변환합니다. 문자열.split(x)처럼 구분자 x를 지정하면 x마다 분리합니다.

문자열.replace(x, y)은 x 문자열을 모두 y 문자열로 변경한 새 문자열을 반환합니다.

문자열.join(x)은 x의 요소 사이사이에 문자열을 삽입하여 합칩니다. 문자열도 시퀀스이므로 x가 문자열이면 문자 하나하나가 요소가 됩니다.

문자열.startswith(x)은 문자열이 x로 시작하는지 확인합니다. 반대로 문자열.endswith(x)는 x로 끝나는지 확인합니다.

join()은 호출하는 방향이 헷갈리기 쉽습니다. b.join(a)는 구분자 b가 주체이고, 합칠 대상 a를 인수로 받습니다. 그래서 "/"가 a의 문자 사이사이에 끼어든 결과가 나왔습니다.


print("1,2,3".split(","))
print("Time is an illusion.".split(" ", 1))
print(", ".join(["사과", "배", "포도"]))
결과
[‘1’, ‘2’, ‘3’]
[‘Time’, ‘is an illusion.’]
사과, 배, 포도

문자열.split(x, maxsplit)의 maxsplit으로 최대 분리 횟수를 지정할 수 있습니다.

join()은 문자열 목록을 하나의 문자열로 합칠 때 주로 사용합니다.

"1,2,3".split(",")의 결과 ['1', '2', '3']은 숫자가 아니라 문자열의 목록이라는 점에 주의합니다. 계산에 사용하려면 int()로 변환해야 합니다.

split(" ", 1)은 한 번만 분리하므로 첫 단어 Time과 나머지 전체 is an illusion.의 두 조각이 됩니다.

  • Tip : join()에 전달하는 요소는 모두 문자열이어야 합니다. 숫자가 포함되어 있으면 TypeError가 발생합니다.


a = "a  b   c"

print(a.split())
print(a.split(" "))
print("a,b,,c".split(","))
결과
[‘a’, ‘b’, ‘c’]
[‘a’, ‘’, ‘b’, ‘’, ‘’, ‘c’]
[‘a’, ‘b’, ‘’, ‘c’]

인수 없는 split()과 split(" ")은 결과가 다릅니다.

인수 없는 split()은 연속된 공백(띄어쓰기, 탭, 줄바꿈)을 하나의 구분자로 취급하고 양 끝의 공백도 무시하므로, 공백이 몇 칸이든 단어만 깔끔하게 나눕니다.

반면 split(" ")은 공백 한 칸마다 정확히 자르므로, 공백이 연속된 곳에서는 사이에 있는 빈 문자열 ''이 결과에 포함됩니다. 구분자를 직접 지정한 split(",")도 마찬가지로 ,, 사이에서 빈 문자열이 생깁니다.

  • Tip : 띄어쓰기로 구분된 단어를 나눌 때는 인수 없는 split()을, CSV처럼 빈 칸도 의미가 있는 데이터는 구분자를 지정한 split(",")을 사용합니다.


print("a-b-c".replace("-", "", 1))

nums = [1, 2, 3]

try:
    ",".join(nums)
except TypeError as e:
    print(e)

print(",".join(map(str, nums)))
결과
ab-c
sequence item 0: expected str instance, int found
1,2,3

문자열.replace(x, y, count)처럼 세 번째 인수를 전달하면 앞에서부터 count번만 바꿉니다. 첫 번째 -만 제거되어 ab-c가 되었습니다.

숫자 목록을 join()으로 합치면 sequence item 0: expected str instance, int found, 즉 “0번 원소가 문자열이 아니라 정수”라는 TypeError가 발생합니다.

map(str, nums)는 각 원소에 str()을 적용하여 문자열로 바꿔 주므로, 그 결과를 join()에 전달하면 1,2,3을 얻을 수 있습니다.



템플릿 문자열(t-string)

f-string은 값을 끼워 넣는 즉시 완성된 문자열을 만들어 버리기 때문에, 어떤 부분이 고정된 글자이고 어떤 부분이 끼워 넣은 값인지 나중에 구분할 수 없습니다. 템플릿 문자열은 이 두 부분을 나누어 보관하여, 값을 문자열로 합치기 전에 검사하거나 가공할 수 있게 해 줍니다.

from string.templatelib import Interpolation

name = "Python"
version = "3.14"
template = t"{name} {version} 버전"

print(type(template))
print(template.strings)
print(template.values)

result = []
for item in template:
    if isinstance(item, Interpolation):
        result.append("[" + str(item.value) + "]")
    else:
        result.append(item)

print("".join(result))
결과
<class ‘string.templatelib.Template’>
(‘’, ‘ ‘, ‘ 버전’)
(‘Python’, ‘3.14’)
[Python] [3.14] 버전

Python 3.14 이상에서는 문자열 앞에 t를 붙인 템플릿 문자열(Template String, t-string)을 사용할 수 있습니다. (PEP 750)

f-string과 문법은 같지만, 결과가 곧바로 문자열(str)이 되지 않고 string.templatelib.Template 객체가 됩니다.

Template 객체는 고정된 문자열 부분(strings)과 중괄호 안에서 계산된 값(values)을 분리해서 보관합니다.

결과를 읽으면, strings는 {name} 앞의 빈 문자열 '', 두 값 사이의 공백 ' ', 마지막의 ' 버전' 세 조각이고, values는 중괄호 안에서 계산된 'Python'과 '3.14'입니다. 고정 부분은 항상 값보다 하나 더 많습니다.

반복문으로 순회하면 문자열 부분과 Interpolation 객체가 차례대로 나오며, Interpolation.value로 계산된 값을 꺼낼 수 있습니다.

예제에서는 Interpolation인 경우에만 값을 대괄호로 감쌌기 때문에, 고정 부분은 그대로 두고 끼워 넣은 값만 [Python], [3.14]로 표시되었습니다.

그러므로 값을 어떻게 합칠지 직접 처리할 수 있어, HTML 이스케이프나 SQL 처리처럼 삽입되는 값을 검사하거나 변환해야 하는 경우에 유용합니다.

  • Tip : t-string은 str이 아니므로 템플릿 + "문자열"처럼 문자열과 바로 연결할 수 없으며, 위 예제처럼 직접 처리해 문자열로 만들어야 합니다.

  • Tip : 빈 문자열 부분은 반복할 때 생략되므로, template.strings에는 ''가 있지만 반복문에서는 나타나지 않습니다.



정리

이름 설명 반환값/특징
r"..." 원시 문자열 \를 글자 그대로 저장
+, * 연결, 반복 새 문자열, +는 문자열끼리만
x in 문자열 부분 문자열 포함 여부 논리값
"%d" % 값, format(), f"..." 문자열 포맷 새 문자열
upper(), lower(), casefold() 대소문자 변환 새 문자열
capitalize(), title(), swapcase() 첫 글자·단어별·반전 변환 새 문자열
strip(), lstrip(), rstrip() 양 끝 공백(또는 지정 문자들) 제거 새 문자열
removeprefix(), removesuffix() 정확히 일치하는 앞·뒤 제거 새 문자열, Python 3.9 이상
find(), rfind() 위치 검색 정수, 없으면 -1
index() 위치 검색 정수, 없으면 ValueError
count() 겹치지 않는 개수 정수
startswith(), endswith() 시작·끝 확인 논리값
split() 분리 문자열 목록
replace(x, y, count) 치환 새 문자열
구분자.join(x) 결합 새 문자열, 원소는 모두 문자열
t"..." 템플릿 문자열 Template 객체, Python 3.14 이상

문자열은 변경할 수 없고, 순서가 있으며, 해시 가능한 자료형입니다. 모든 문자열 메서드는 원본을 바꾸지 않고 새 값을 반환하므로, 결과를 사용하려면 반드시 변수에 대입해야 합니다.

댓글 남기기