Study/Python
[Python] 문자열(String) 자료형과 튜플
the.Dev.Cat
2026. 3. 7. 23:04
동빈나 채널의 파이썬 문법 부수기 유튜브 강의를 참고하여 정리한 내용이다.
리스트와 비슷하게 순서가 있는 자료형이지만, 둘 다 불변(immutable)이라는 공통점이 있다.
문자열과 튜플의 특성을 제대로 이해해야 인덱싱, 비교, 수정 시도에서 실수를 줄일 수 있다.
문자열 (str)
초기화
# 작은따옴표
a = 'hello'
# 큰따옴표
b = "world"
# 작은따옴표 안에 작은따옴표가 필요할 때
c = "It's a nice day"
d = 'He said "hello"'
# 백슬래시 이스케이프
e = 'It\'s a nice day'
f = "He said \"hello\""
print(a) # hello
print(c) # It's a nice day
print(d) # He said "hello"
특수 문자 이스케이프
print("줄바꿈:\n두 번째 줄")
# 줄바꿈:
# 두 번째 줄
print("탭:\t탭 다음")
# 탭: 탭 다음
print("백슬래시: \\")
# 백슬래시: \
print("따옴표: \"큰따옴표\"")
# 따옴표: "큰따옴표"
멀티라인 문자열
text = """이건
여러 줄에 걸친
문자열이다"""
print(text)
# 이건
# 여러 줄에 걸친
# 문자열이다
# 또는 작은따옴표 세 개
text2 = '''첫 번째 줄
두 번째 줄'''
문자열 연산
연결(+)과 반복(*)
a = "Hello"
b = "World"
# 연결
c = a + " " + b
print(c) # Hello World
# 반복
d = "ab" * 3
print(d) # ababab
# 숫자와 문자열은 + 불가
print("나이: " + 25)
# TypeError: can only concatenate str (not "int") to str
# 올바른 방법
print("나이: " + str(25)) # 나이: 25
print(f"나이: {25}") # 나이: 25 (f-string 권장)
f-string (권장)
name = "Alice"
age = 25
score = 95.567
# f-string
print(f"이름: {name}, 나이: {age}") # 이름: Alice, 나이: 25
# 표현식도 사용 가능
print(f"내년 나이: {age + 1}") # 내년 나이: 26
# 소수점 포맷
print(f"점수: {score:.2f}") # 점수: 95.57
# 정렬
print(f"{'left':<10}|") # 'left |' (왼쪽 정렬, 10칸)
print(f"{'right':>10}|") # ' right|' (오른쪽 정렬)
print(f"{'center':^10}|") # ' center |' (가운데 정렬)
인덱싱과 슬라이싱
문자열은 리스트처럼 인덱싱과 슬라이싱이 가능하다.
s = "Hello, Python!"
# 인덱싱
print(s[0]) # H
print(s[-1]) # !
print(s[7]) # P
# 슬라이싱
print(s[0:5]) # Hello
print(s[7:]) # Python!
print(s[:5]) # Hello
print(s[::2]) # Hlo yhn (2칸씩)
print(s[::-1]) # !nohtyP ,olleH (역순)
문자열은 Immutable
리스트와 달리 문자열은 수정이 불가능하다. 인덱스로 값을 바꾸려고 하면 에러가 난다.
s = "hello"
s[0] = "H"
# TypeError: 'str' object does not support item assignment
# 수정이 필요하면 새 문자열을 만들어야 함
s = "H" + s[1:]
print(s) # Hello
# 또는 list로 변환 후 수정하고 다시 join
s = "hello"
s_list = list(s)
s_list[0] = "H"
s = "".join(s_list)
print(s) # Hello
# +=는 새 문자열 객체를 만든다 (기존 수정이 아님)
s = "hello"
id_before = id(s)
s += " world"
id_after = id(s)
print(id_before == id_after) # False - 다른 객체
주요 문자열 메서드
대소문자 변환
s = "Hello, World!"
print(s.upper()) # HELLO, WORLD!
print(s.lower()) # hello, world!
print(s.capitalize()) # Hello, world! (첫 글자만 대문자)
print(s.title()) # Hello, World! (각 단어 첫 글자 대문자)
print(s.swapcase()) # hELLO, wORLD! (대소문자 교환)
공백 처리
s = " hello world "
print(s.strip()) # "hello world" - 양쪽 공백 제거
print(s.lstrip()) # "hello world " - 왼쪽만
print(s.rstrip()) # " hello world" - 오른쪽만
# 특정 문자 제거
s2 = "***hello***"
print(s2.strip("*")) # hello
분리와 결합
# split() - 문자열을 리스트로 분리
s = "1 2 3 4 5"
nums = s.split() # 공백 기준
print(nums) # ['1', '2', '3', '4', '5']
s2 = "a,b,c,d"
parts = s2.split(",")
print(parts) # ['a', 'b', 'c', 'd']
# 입력 받을 때 자주 쓰는 패턴
n, m = map(int, input().split()) # "3 4" 입력 시 n=3, m=4
# join() - 리스트를 문자열로 합치기
words = ["hello", "world", "python"]
result = " ".join(words)
print(result) # hello world python
result2 = ",".join(words)
print(result2) # hello,world,python
# 숫자 리스트를 문자열로
nums = [1, 2, 3, 4]
print(" ".join(map(str, nums))) # 1 2 3 4
검색과 확인
s = "hello world"
# 포함 여부
print("world" in s) # True
print("python" in s) # False
# 위치 찾기
print(s.find("world")) # 6 - 없으면 -1 반환
print(s.index("world")) # 6 - 없으면 ValueError 발생
print(s.find("python")) # -1
# print(s.index("python")) # ValueError!
# 시작/끝 확인
print(s.startswith("hello")) # True
print(s.endswith("world")) # True
# 개수 세기
print(s.count("l")) # 3
교체
s = "hello world hello"
print(s.replace("hello", "hi")) # hi world hi
print(s.replace("hello", "hi", 1)) # hi world hello - 첫 번째만
자주 쓰는 문자열 패턴
문자 판별
print("123".isdigit()) # True - 모두 숫자
print("abc".isalpha()) # True - 모두 알파벳
print("abc123".isalnum()) # True - 알파벳+숫자
print(" ".isspace()) # True - 모두 공백
print("HELLO".isupper()) # True - 모두 대문자
print("hello".islower()) # True - 모두 소문자
문자열 ↔︎ 숫자 변환 실수
# input()은 항상 문자열을 반환한다
n = input() # 사용자가 "5" 입력
print(n + 1)
# TypeError: can only concatenate str (not "int") to str
n = int(input()) # 정수로 변환
print(n + 1) # 6
# 비교할 때도 주의
n = input() # "10" 입력
print(n == 10) # False - 문자열과 정수 비교
print(n == "10") # True
print(int(n) == 10) # True
리스트 → 문자열 변환
# 문자 리스트
chars = ['h', 'e', 'l', 'l', 'o']
s = "".join(chars)
print(s) # hello
# 숫자 리스트 → 문자열
nums = [1, 2, 3]
s = "".join(str(n) for n in nums)
print(s) # 123
튜플 (tuple)
리스트와 거의 동일하지만 수정이 불가능하다는 점이 다르다.
초기화
# 소괄호 사용
a = (1, 2, 3)
# 소괄호 없이도 됨 (쉼표가 핵심)
b = 1, 2, 3
print(b) # (1, 2, 3)
print(type(b)) # <class 'tuple'>
# 빈 튜플
c = ()
d = tuple()
# 원소 1개짜리 튜플 - 쉼표 필수
e = (1,)
f = 1,
print(type(e)) # <class 'tuple'>
print(type((1))) # <class 'int'> - 쉼표 없으면 그냥 정수!
쉼표 하나가 튜플을 만든다. (1)은 정수 1이고, (1,)이 원소 하나짜리 튜플이다.
인덱싱과 슬라이싱
리스트와 동일하게 사용한다.
a = (10, 20, 30, 40, 50)
print(a[0]) # 10
print(a[-1]) # 50
print(a[1:3]) # (20, 30)
print(a[::-1]) # (50, 40, 30, 20, 10)
# 포함 여부
print(30 in a) # True
print(len(a)) # 5
Immutable - 수정 불가
a = (1, 2, 3)
a[0] = 10
# TypeError: 'tuple' object does not support item assignment
a.append(4)
# AttributeError: 'tuple' object has no attribute 'append'
한 번 만든 튜플은 수정, 추가, 삭제가 전부 불가능하다.
튜플 연산
a = (1, 2, 3)
b = (4, 5, 6)
# + 는 새 튜플 반환
c = a + b
print(c) # (1, 2, 3, 4, 5, 6)
# * 도 새 튜플 반환
d = a * 2
print(d) # (1, 2, 3, 1, 2, 3)
튜플을 쓰면 좋은 경우
언패킹
# 여러 값을 한 번에 할당
x, y = 10, 20
print(x, y) # 10 20
# 좌표, 범위 등을 반환할 때
def get_range():
return 1, 100 # 튜플 반환
start, end = get_range()
print(start, end) # 1 100
# 스왑 (다른 언어에서는 임시 변수 필요)
a, b = 1, 2
a, b = b, a # 파이썬 스타일
print(a, b) # 2 1
딕셔너리 키로 사용
리스트는 해시가 안 돼서 딕셔너리 키로 사용할 수 없다. 튜플은 불변이라 키로 쓸 수 있다.
# 리스트는 딕셔너리 키로 못 씀
d = {}
d[[1, 2]] = "value"
# TypeError: unhashable type: 'list'
# 튜플은 가능
d[(1, 2)] = "value"
print(d[(1, 2)]) # value
# 2차원 좌표를 키로 쓸 때 유용
visited = {}
pos = (3, 4)
visited[pos] = True
print(visited[(3, 4)]) # True
메모리 효율
같은 데이터를 저장할 때 튜플이 리스트보다 메모리를 적게 쓴다.
import sys
a = [1, 2, 3, 4, 5]
b = (1, 2, 3, 4, 5)
print(sys.getsizeof(a)) # 104 (바이트)
print(sys.getsizeof(b)) # 80 (바이트)
수정이 필요 없는 데이터는 튜플로 저장하는 게 효율적이다.
함수에서 여러 값 반환
def divide(a, b):
quotient = a // b
remainder = a % b
return quotient, remainder # 튜플로 반환
q, r = divide(17, 5)
print(f"몫: {q}, 나머지: {r}") # 몫: 3, 나머지: 2
리스트 vs 튜플 비교
| 구분 | 리스트 [] |
튜플 () |
|---|---|---|
| 가변성 | 가변(mutable) | 불변(immutable) |
| 속도 | 상대적으로 느림 | 상대적으로 빠름 |
| 메모리 | 더 많이 사용 | 더 적게 사용 |
| 딕셔너리 키 | 불가 | 가능 |
| 사용 시점 | 데이터가 변할 때 | 변하지 않을 때 |
코딩테스트에서는 대부분 리스트를 쓴다. 좌표처럼 변하지 않는 쌍을 딕셔너리 키로 쓸 때 튜플을 선택한다.
자주 하는 실수 모음
# 1. 문자열 인덱싱으로 수정 시도
s = "hello"
s[0] = "H" # TypeError
# 2. 원소 하나짜리 튜플에 쉼표 빠뜨리기
t = (1) # int, 튜플 아님
t = (1,) # 이게 튜플
# 3. split() 결과를 그대로 숫자로 사용
line = "3 4"
a, b = line.split()
print(a + b) # "34" - 문자열 연결
a, b = map(int, line.split())
print(a + b) # 7 - 정수 덧셈
# 4. 문자열 반복을 덧셈으로 착각
s = "a"
s *= 5
print(s) # aaaaa - 이건 됨
s = "a"
s += "b" * 3
print(s) # abbb - 이것도 됨
# 5. 튜플의 메서드 사용 시도
t = (1, 2, 3)
t.append(4) # AttributeError: 'tuple' object has no attribute 'append'
t.sort() # AttributeError: 'tuple' object has no attribute 'sort'
# sorted()는 사용 가능 (새 리스트 반환)
result = sorted(t)
print(result) # [1, 2, 3] - 리스트로 반환
문자열과 튜플의 핵심은 불변성이다.
수정이 필요하면 새 객체를 만들어야 한다는 걸 잊지 않으면 에러 대부분은 피할 수 있다.