본문 바로가기

진리는어디에/Python

[Python] 시퀀스 자료형 #2 튜플(tuple)

이번 강좌에서는 지난 리스트 강좌에 이어 튜플에 대해 살펴 보도록 하겠다. 이번 강좌를 이해하기 위해서는 mutable 타입과 immutable 타입에 대한 개념이 있어야 한다. mutable, immutable관련 강좌는 다음 링크를 참조 하자.

목차

  1. 리스트(list)
  2. >> 튜플(tuple)
  3. 셑(set)
  4. 딕셔너리(dict)

튜플(tuple)이란?

튜플은 리스트와 매우 비슷하게 인덱싱, 슬라이싱, +, * 연산등을 지원하지지만, 리스트와는 결정적인 차이가 있다. 리스트는 mutable 자료형이라 삽입과 삭제, 요소 업데이트와 같은 수정 연산이 가능하지만, 튜플은 immutable 자료형이라 한번 생성되면 읽기만 가능하고 수정이 불가능 하다.

  • list : [1, 2, 3] 처럼 대괄호로 감싸진 mutable 타입
  • tuple : (1, 2, 3) 처럼 소괄호로 감싸진 immutable 타입
s = [1, 2, [3, 4]] # list
t = (1, 2, [3, 4]) # tuple

s.append(3) # ok
t.append(3) # error! 튜플은 추가가 불가능 하다

del s[0]    # ok
del t[0]    # error! 튜플은 삭제가 불가능 하다

s[0] = 10   # ok
t[0] = 10   # error! 튜플은 요소에 새로운 객체를 할당할 수 없다

그리고 튜플을 사용하면서 자주 잘 못 이해하는 부분이 있는데, 튜플의 immutable은 튜플 자체가 immutable하다는 것이지 튜플이 가리키고 있는 요소들이 immutable 하다는 것은 아니다. 각 요소들은 각자의 속성에 따라 mutable과 immutable이 결정 된다.

t = (1, [2, 3])

t[0] = 10      # error! 튜플이 가리키고 있는 객체를 바꾸는 것은 불가능 하다
t[1] = 10      # error! 
t[1].append(5) # ok. 튜플이 가리키고 있는 리스트 객체를 변경하는 것은 가능

튜플 생성 방법

1. 타입 이름 'tuple' 사용

t1 = tuple()             # 타입 이름 사용
print(t1)                # ()

t2 = tuple('ABCD')       # 타입 이름과 iterable 객체를 인자로 사용
print(t2)                # ('A', 'B', 'C', 'D')

t3 = tuple(range(5))     # 타입 이름과 range 객체를 인자로 사용
print(t3)                # (1, 2, 3, 4, 5)

t4 = tuple([1, 2, 3, 4]) # 타입 이름과 리스트 객체를 인자로 사용
print(t4)                # (1, 2, 3, 4, 5)

2. 소괄호 '( )' 사용

t1 = ()  # 빈 튜플 생성

t2 = (1) # 튜플이 아니다. 정수 선언이다.
print(t2) # <class 'int'>

t3 = (1,) # 튜플을 선언하기 위해서는 콤마가 필요하다
print(t3) # <class 'tuple'>

t1 = (1, 2, 3)
t2 = 1, 2, 3    # t1과 동일하다. 튜플의 선언에서 괄호는 필수가 아니다
t3 = 1,         # (1, )과 동일한 튜플이다
t4 = 1          # 1 이다. 튜플이 아니다
Tip. 튜플은 괄호 () 가 아니라 콤마 , 로 선언 된다

많은 책이나 블로그들에서 튜플은 괄호를 이용해 선언 된다고 하지만 사실 튜플은 콤마를 통해 생성된다. 파이썬 공식문서의 튜플 설명 예제에서도 콤마만을 이용하는 것을 볼 수 있다[여기]. 다만 괄호가 필요한 경우는 중첩된(nested) 튜플을 선언할 때 정도지만, 괄호로 구분하는 것이 가독성면에서나 실수를 방지하는 면에서나 여러 모로 유리 하기 때문에 관례적으로 튜플을 사용 할 때는 괄호를 사용한다.

3. 튜플 표현식 사용

튜플 또한 리스트와 마찬가지로 제너레이터 표현식과 비슷하게 생성 가능 하다. 이를 튜플 컴프리헨션(tuple comprehension)이라고 한다. 리스트 처럼 괄호만으로 제너레이터 표현식을 사용한다면 괄호를 튜플로 인식하는 것이 아니라 표현식의 일부로 인식하기 때문에 튜플이 아닌 제너레이터를 생성하게 된다.

s1 = [ i for i in range(10) if i % 2 == 0 ]
print(type(s1)) # <class 'list'>

t1 = ( i for i in range(10) if i % 2 == 0 )
print(type(t1)) # <class 'generator'>, 튜플이 아니다

그래서 튜플 컴프리헨션을 이용해 튜플을 생성하기 위해서는 반드시 타입 이름을 사용해야 한다.

t1 = tuple((i for i in range(10) if i % 2 == 0))
t2 = tuple( i for i in range(10) if i % 2 == 0 )  # t1과 동일

파이썬을 시작하는 사람들이 종종 실수 하는 부분이므로 잘 기억 하도록 하자.

튜플 인덱싱

튜플 인덱싱은 이전에 배웠던 문자열의 인덱싱과 동일하다.

t = (1, 2, 3, [4, 5, 6])

print(t[0])    # 1
print(t[-1])   # [4, 5, 6]

간단한 예제라 설명은 생략한다. 인덱싱에 대한 자세한 설명은 [참고:문자열 인덱싱]을 참고하도록 한다.

튜플 슬라이싱

튜플의 슬라이싱 또한 문자열의 슬라이싱과 동일하다. 슬라이싱의 결과는 리스트 형태로 반환 된다.

t = [0, 1, 2, 3, 4, 5, 6, 7, 9]

print(t[1:3])   # [1, 2]
print(t[1:9:3]) # [1, 4, 7]

sc = slice(1, 9, 3)
print(t[sc])    # [1, 4, 7]

간단한 예제라 설명은 생략한다. 슬라이싱에 대한 자세한 설명은 [참고:문자열 슬라이싱]을 참고하도록 한다.

튜플 unpacking

인덱싱과 슬라이싱은 문자열과 완전 동일하다. 하지만 튜플은 각 요소들을 개별 변수로 풀어놓는 unpacking이 가능하다.말로는 이해가 어려울테니 예제 통해 쉽게 이해하도록 하자.

t = [1, 'AB', [2, 3]]

e1, e2, e3 = t # unpacking

print(f'{e1}, {e2}, {e3}') # 1, AB, [2, 3]

e1, e2 = t          # ValueError: too many values to unpack (expected 2)
e1, e2, e3, e4  = t # ValueError: not enough values to unpack (expected 4, got 3)

3라인에서 3개의 요소를 가진 튜플을 3개의 변수로 언패킹하는 것을 볼 수 있다. 주의할 점은 언패킹시에는 튜플 요소의 개수와 언패킹 요속를 받는 변수들의 개수가 같아야 한다는 것이다. 개수가 다를경우 7,8라인과 같은 예외를 발생 시킨다.

+ 와 * 연산자

튜플의 +, * 연산자 또한 문자열의 +, * 연산자와 비슷하다.

t1 = (1, 2, 3)
t2 = (4, 5, 6)

print(t1 + t2)       # (1, 2, 3, 4, 5, 6)
print(3 * t1)        # (1, 2, 3, 1, 2, 3, 1, 2, 3)
print(t1 * 3)        # (1, 2, 3, 1, 2, 3, 1, 2, 3)
print(t1 + t2 * 3)   # (1, 2, 3, 4, 5, 6, 4, 5, 6, 4, 5, 6)
print((t1 + t2) * 3) # (1, 2, 3, 4, 5, 6, 1, 2, 3, 4, 5, 6, 1, 2, 3, 4, 5, 6)

t1 += t2             # t1 = t1 + t2. (1, 2, 3, 4, 5, 6)
print(t1)

print(3 in t1)       # True

튜플 주요 메소드

  • len() : 튜플의 길이 리턴
s = (0, 1, 2, 3, 4, 5, 6, 7, 8, 9)

print(len(s)) # 10
  • count, index
t = (1, 3, 5, 7, 9, 1, 3, 5, 7, 9)

print(t.count(3))       # 2 (튜플의 요소중 3의 개수를 리턴)
print(t.index(3))       # 1 (튜플에서 가장 처음 만나는 3의 인덱스 리턴)

# 3을 인덱스 3 부터 8 사이에서 찾는다
print(t.index(3, 3, 8)) # 6

마치며

이상 튜플의 기본 사용법에 대해 알아 보았다. 리스트와 매우 흡사하지만 immutable 객체이기 때문에 리스트에 있는  삽입 삭제, 업데이트 관련된 메소드들이 하나도 없어서 상대적으로 가볍다. 시퀀스 자료 구조가 필요한데 수정 할 필요가 없는 경우 튜플을 사용하면 보다 가볍게 사용 할 수 있다. 만일 수정이 필요하다면 당연히 immutable 데이터인 튜플은 사용 할 수 가 없다.

이번 강좌는 여기서 마치고 다음 시간에는 시퀀스 자료형 셑(set)에 대해 알아보도록 하겠다.

부록 1. 같이 보면 좋은 글

유익한 글이었다면 공감(❤) 버튼 꾹!! 추가 문의 사항은 댓글로!!