04-2 튜플

윤혜민·박현규 · 17 / 30

이번에는 리스트와 비슷하지만 다른 튜플을 알아보겠습니다. 리스트와 튜플은 요소를 묶어 표현하는 자료형이라 비슷합니다. 하지만 중요한 차이점이 있습니다. 튜플을 사용해보면서 하나씩 알아봅시다.

튜플 사용해보기

리스트는 요소를 대괄호로 감싸 표현하지만 튜플은 요소를 소괄호로 감싸 표현한다는 겁니다. 다음은 리스트와 튜플로 같은 요소를 묶은 후 출력해본 것입니다. 이때 type( ) 함수를 이용해서 변수의 자료형이 무엇인지 체크하여 출력했습니다.

fruits_list = ["사과", "바나나", "오렌지"]
fruits_tuple = ("사과", "바나나", "오렌지")
print(f"리스트: {fruits_list}")
print(f"튜플: {fruits_tuple}")
print(f"리스트 타입: {type(fruits_list)}")
print(f"튜플 타입: {type(fruits_tuple)}")

[실행 결과]

리스트: ['사과', '바나나', '오렌지']

튜플: ('사과', '바나나', '오렌지')

리스트 타입: <class 'list'>

튜플 타입: <class 'tuple'>

대괄호로 감쌌냐, 소괄호로 감쌌냐에 따라서 다르게 취급한다는 걸 알았습니다. 하지만 이건 외관상의 표현일 뿐이고 리스트와 튜플의 핵심적인 차이라고 보기는 어렵네요. 그렇다면 리스트와 튜플은 무엇이 다른 걸까요?

값을 변경할 수 없는 튜플

바로 튜플의 불변성입니다. 04-1 리스트에서 공부했듯이 리스트는 한 번 생성한 후에 요소를 추가하거나 삭제하는 등의 작업을 할 수 있습니다. 다시 말해 리스트는 가변성을 가지고 있습니다. 하지만 튜플은 이런 작업을 할 수가 없습니다. 다시 말해 튜플은 요소를 추가하거나 삭제하는 등 값을 변경할 수 없습니다. 다음 코드를 실행해봅시다.

fruits_list = ["사과", "바나나", "오렌지"]
fruits_tuple = ("사과", "바나나", "오렌지")
# 리스트는 요소 변경 가능
fruits_list[1] = "딸기"
print(f"변경된 리스트: {fruits_list}")
# 튜플은 요소 변경 불가능
fruits_tuple[1] = "딸기" # ➝ 여기서 오류 발생

[실행 결과]

변경된 리스트: ['사과', '딸기', '오렌지']

Traceback (most recent call last):

File "/Users/canine89/Documents/dev/pythonstudy/tuple01.py", line 9, in <module>

fruits_tuple[1] = "딸기" # ➝ 여기서 오류 발생

~~~~~~~~~~~~^^^

TypeError: 'tuple' object does not support item assignment

리스트 fruits_list의 요소 [1]을 변경하는 코드는 문제 없이 동작합니다. 하지만 튜플은 오류가 발생했습니다. 왜 그럴까요? 오류를 복사해서 커서에 물어보면 이렇게 답을 해줍니다.

[Note] 오류 메시지를 드래그하면 오른쪽 위에 [Add to Chat] 메뉴가 나옵니다. 이것을 누르면 채팅 세션에 오류 메시지를 추가해줍니다. 그리고 커서는 [Ask] 모드와 [Agent] 모드가 있는데 [Agent] 모드는 코드를 바로 수정하려는 경향이 있으므로 [Ask] 모드로 바꿔 질문하기 바랍니다.

커서는 '튜플이 불변성이라 값을 바꾸려고 해서 오류가 발생했다'라고 알려줍니다. 실제로 튜플은 한 번 정해진 값을 절대 바꿀 수 없습니다. 그래서 이런 특성을 가진 튜플은 변하면 안 되는 고정된 데이터를 저장할 때 유용합니다. 예를 들어 좌표값 (x, y), 생년월일 (1990, 5, 15) 같은 데이터는 튜플로 표현하는 게 적합합니다. 반대로 학생 명단처럼 수시로 추가되거나 삭제될 수 있는 데이터는 리스트를 사용하는 게 좋겠죠?

튜플 연습하기

튜플도 리스트와 마찬가지로 인덱싱, 슬라이싱, 더하기, 곱하기와 같은 작업을 할 수 있습니다. 비슷한 자료형이므로 결과는 마찬가지입니다. 리스트 기초 연습하기에서 했던 내용을 동일하게 튜플에서 연습해보겠습니다.

점수 평균 계산해보기

다음은 점수 평균을 계산하는 코드입니다. 리스트에서 튜플로 바뀌었을 뿐 크게 달라진 내용은 없습니다. 다만 튜플의 불변성으로 점수의 수정, 삭제는 할 수 없겠네요.

scores = (85, 92, 78, 90, 88)
total = sum(scores)
count = len(scores)
average = total / count
print(f"점수: {scores}")
print(f"총점: {total}점")
print(f"과목 수: {count}개")
print(f"평균: {average}점")

[실행 결과]

점수: (85, 92, 78, 90, 88)

총점: 433점

과목 수: 5개

평균: 86.6점

가장 높은 점수, 가장 낮은 점수 알아보기

너무 비슷한 코드만 작성해서 질릴 수 있습니다. 그러니 이번에는 리스트에서는 해보지 않은 max( ) 함수를 사용해봅시다. max( ) 함수는 이름에서도 알아볼 수 있듯이 리스트나 튜플에서 가장 값이 큰 요소를 반환하는 함수입니다.

student_name = "김철수"
scores = (85, 92, 78, 90, 88)
average = sum(scores) / len(scores)
highest = max(scores)
lowest = min(scores)
print(f"{student_name}의 성적 분석")
print(f"평균: {average:.1f}점")
print(f"최고점: {highest}점")
print(f"최저점: {lowest}점")

[실행 결과]

김철수의 성적 분석

평균: 86.6점

최고점: 92점

최저점: 78점

실행 결과를 보면 max( ) 함수나 min( ) 함수가 어떻게 동작하는 함수인지 쉽게 알 수 있습니다. 그렇다면 이왕 두 함수를 알게 된 김에 조금 더 유용한 사용 방법을 알아봅시다. 만약 같은 점수가 중복되어 있다면 그 점수가 몇 개인지 어떻게 알 수 있을까요? 바로 count( ) 함수를 조합하면 됩니다.

scores = (92, 85, 92, 78, 85, 92)
print(f"최고점 {max(scores)}점이 {scores.count(max(scores))}번 나왔습니다")
print(f"최저점 {min(scores)}점이 {scores.count(min(scores))}번 나왔습니다")

[실행 결과]

실행결과

최고점 92점이 3번 나왔습니다

최저점 78점이 1번 나왔습니다

지금까지는 함수를 1겹만 사용했지만 이렇게 겹쳐서 사용할 수도 있습니다. 파이썬에서 함수를 겹쳐 사용하면 안쪽 함수를 먼저 실행하고 그 다음에 바깥으로 나아가는 방식으로 동작합니다.

직관적으로도 이것이 당연합니다. count( ) 함수는 소괄호에 전달한 값이 튜플이나 리스트에 몇 개가 있는지 세는 함수인데 max(scores)를 실행해야 세어야 할 대상이 무엇인지 알 수 있습니다. 이후 함수를 겹쳐 사용하는 방식은 필요할 때마다 종종 사용하겠습니다.

인덱싱, 슬라이싱하기

인덱싱과 슬라이싱도 마찬가지입니다. 코드를 실행하여 한 번 더 연습해봅시다.

fruits = ("사과", "바나나", "오렌지", "포도", "딸기")
print(f"전체 튜플: {fruits}")
print(f"첫 번째 요소: {fruits[0]}")
print(f"세 번째 요소: {fruits[2]}")
print(f"마지막 요소: {fruits[-1]}")
print(f"뒤에서 두 번째: {fruits[-2]}")

[실행 결과]

전체 튜플: ('사과', '바나나', '오렌지', '포도', '딸기')

첫 번째 요소: 사과

세 번째 요소: 오렌지

마지막 요소: 딸기

뒤에서 두 번째: 포도

실행 결과는 리스트에서 봤던 것과 크게 다르지 않습니다. 인덱싱과 슬라이싱은 빠르게 넘어가겠습니다.

튜플의 구체적인 사용 방법은 리스트와 다르지 않으므로 튜플은 이정도만 연습하면 충분합니다. 튜플은 불변성이 있다라는 점만 기억하고 바로 다음으로 넘어가봅시다. 다음은 중요한 자료형인 딕셔너리입니다.