Python의 데이터타입중 list와 tuple에 대해 정리합니다.

개요


  • 데이터 타입중 ‘list’와 ‘tuble’ 과 관련된 다양한 기능을 정리합니다.

리스트 & 튜플

  • 복수개의 값을 담을 수 있는 데이터 구조

  • 실생활에서 사용하는 리스트(학생 리스트, 성적 리스트 등등)과 동일한 의미로 이해

  • list - mutable (생성된 후에 변경 가능)

  • tuple - immutable (생성된 후에 변경 불가능)


공통점

  • 리스트와 튜플은 모두 인덱싱과 슬라이싱이 가능하다.

  • 순회가능(iterable) - 리스트와 튜플은 모두 for문을 이용해 순회할 수 있다.

차이점

  • 리스트의 요소는 (생성 후)바꿀 수 있으나(mutable), 튜플의 요소는 (생성 후)바꿀 수 없다.(immutable)

  • 리스트보다는 튜플의 순회속도가 약간 더 빠르다.


리스트

리스트 초기화

  • [] 안에 값을 담아서 생성

  • list() 함수로 생성

  • str.split()함수로 생성 되기도 한다.

1
2
L1 = [] 
print(L1)
[]
1
2
L1 = [1, 2, 3, 5, 10]
print(L1)
[1, 2, 3, 5, 10]
1
2
L1 = ['korea', 'seoul', 1, 23, [34, 56]]
print(L1)
['korea', 'canada', 1, 23, [34, 56]]
1
2
3
4
L1 = hello word
L2 = L1.str.split()

print(L2)

list() 함수

  • 다른 데이터 타입을 리스트로 변환할 때도 사용
1
2
3
L1 = 'hello world' # 문자열 데이터
L2 = list(L1) #리스트 형으로 변경 -> 하나의 문자별로 각각 리스트의 값이 된다.
print(L2)
['h', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd']
1
2
3
4
L3 = (1, 2, 3) #int형 데이터
L4 = list(L3) #리스트 형으로 변경

print(L4) 
[1, 2, 3]

string split 함수

  • 구분자로 구분되는 리스트를 반환 (실제로 자주 사용 됨)

  • split() 의 () 안에 입력되는 특정 값을 기준으로 분류된다.

  • 아무것도 입력하지 않고 ()을 하거나 (‘ ‘)로 입력하면 1칸 공백을 기준으로 split한다.

1
2
3
4
5
6
7
8
L1 = 'hello world nice weather'
L2 = L1.split()

L3 = 'today is shiny day'
L4 = L3.split(' ')

print(L2)
print(L4)
['hello', 'world', 'nice', 'weather']
['today', 'is', 'shiny', 'day']

리스트 indexing

  • 문자열의 인덱싱과 동일하게 동작

  • [] 연산자를 이용하여 항목을 얻어온다.

  • List[i] : List 의 i번째 원소를 반환 (0부터 시작, 역순인 경우 -1부터 시작)

  • i가 음수인 경우도 가능하며 마지막원소가 -1로 하여 앞으로 갈때마다 1씩 감소함

1
2
3
4
L1 = [1, 2, 3, 4, 5, 6]
print(L1[2]) # L1 리스트에서 2번째 인덱스 -> 3번째 수
print(L1[5])
print(L1[-1]) 
3
6
6

리스트 개별 아이템에 접근

  • 인덱스에 접근하여 값을 업데이트 가능
1
2
L1 = 'hello world'
print(L1[0])
h
1
2
3
4
L2 = 'jello world'
L3 = 'j' + L1[1:]  #1번재 인덱스 부터 끝까지 => ello world

print(L3)
jello world
1
2
3
4
L4 = L1.replace('h', 'j') 

print(L4) # L1의 h를 j로 치환한 결과를 출력
print(L1) # 기존의 L1출력 <- L4는 L1를 활용해 변수선언을 한 것 뿐이지, L1 에 직접적인 변화를 주지는 않았다.
jello world
hello world
1
2
3
4
5
L1 = [1, 2, 3, 4, 5]
L1[0] = 100 # 0번째 인덱스인 1을 100으로 변경
L1[-1] = 90 # 맨 마지막 인덱스인 5를 90으로 변경

print(L1)
[100, 2, 3, 4, 90]

리스트 slicing

  • 문자열 슬라이싱과 동일하게 동작

  • 슬라이싱의 결과 역시 list!

  • List[start:end,step] 순서로 입력

    • start 인덱스 부터 end인덱스 까지 step만큼 건너 뛴 부분을 리스트로 반환

    • step은 defL1ult가 1

    • start와 end도 입력하지 않아도 된다. 단, ‘:’은 반드시 넣어야 한다.

1
2
3
4
5
6
7
8
9
10
L1 = [1, 2, 3, 4, 5, 6, 7, 8]
print(L1[4:7])
print(L1[:7])
print(L1[3:])
print(L1[:])

# slicing 
# start:end:increment(1)

L1[1:7]
[5, 6, 7]
[1, 2, 3, 4, 5, 6, 7]
[4, 5, 6, 7, 8]
[1, 2, 3, 4, 5, 6, 7, 8]
[2, 3, 4, 5, 6, 7]

list 멤버 함수

  • 생성된 리스트 객체에 동작하는 함수

  • 추후, 클래스와 멤버 함수 개념을 정리할 예정

append() - 요소 추가 함수

  • 리스트의 끝에 항목을 추가함
1
2
3
4
L1 = [1, 2, 3, 4, 5]
L1.append(10)

print(a)
[1, 2, 40, 30, 5]

extend() - 요소 추가 함수

  • 리스트를 연장

  • += 로도 가능함

1
2
3
4
5
6
7
L2 = [1, 2, 3, 4, 5]
L3 = [6, 7, 8, 9, 10]

L2.extend(L3)


print(L2)
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
1
2
3
4
5
6
# a.extend(b)와 동일
L2 = [1, 2, 3, 4, 5]
L3 = [6, 7, 8, 9, 10]

L2 += L3
print(L2)
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

insert() - 요소 추가 함수

  • 리스트의 원하는 위치에 추가 가능

  • 앞에 인덱스를, 뒤에 아이템을 명시

1
2
3
4
L4 = [1, 3, 4, 5, 6]
L4.insert(1, 40) # 1번째 인덱스에 40을 넣는다. 

print(L4)
[1, 40, 3, 4, 5, 6]

remove() - 요소 제거 함수

  • 값으로 항목 삭제

  • 한 리스트에 동일값이 있을 경우 remove 앞쪽의 인덱스에 있는 값이 삭제 된다.

1
2
3
L5= [1, 2, 30, 40, 30, 5]
L5.remove(9)
print(L5) # list에 9가 없기 때문에 에러가 발생
1
2
L5.remove(30)
print(L5)   # list에 있는 30중 앞쪽 인덱스의 30이 삭제 되었다.
[1, 2, 40, 30, 5]

pop() - 요소 제거 함수

  • 지우고자 하는 아이템을 반환 후, 삭제

  • ()에 들어가는 숫자는 인덱스 번호

1
2
3
4
5
L5 = [1, 2, 3, 4, 5]
L6 = L5.pop(1) #1번째 인덱스 값을 반환 후 삭제

print(L5) 
print(L6)
[1, 3, 4, 5]
2

순서상 L5의 1번째 인덱스값을 지우는 L6가 먼저 선언되어서 L5에서 1번째 인덱스값인 2가 사라진다.

index() - 요소 위치 찾기 함수

  • 찾고자 하는 값의 인덱스 반환
1
2
3
4
5
L7 = [1, 3, 2, 1, 4]
print("1의 위치:{}".format(L7.index(1)))
print("3의 위치:{}".format(L7.index(3)))

L7.index(5) # 5의 위치: 없으므로 Error 발생 (5 is not in list)
1의 위치:0
3의 위치:1

replace()

  • 개별 아이템 접근에서 정리한 내용과 동일

  • 인덱스로 접근하여 특정값을 변경 가능 (mutable)

1
2
3
4
5
6
L1 = 'hello world'

L4 = L1.replace('h', 'j') 

print(L4) # L1의 h를 j로 치환한 결과를 출력
print(L1) # 기존의 L1출력 <- L4는 L1를 활용해 변수선언을 한 것 뿐이지, L1 에 직접적인 변화를 주지는 않았다.
jello world
hello world

in 키워드

  • 리스트 내에 해당 값이 존재하는지 확인

  • value in [list]

  • True, False 중 한가지로 반환

1
2
3
4
5
6
L1 = [1, 2, 3, 4, 5, 10]
L2 = 10

L3 = L2 in a # False

print(L3)
False

list 정렬

  • sort() -> 리스트 자체를 내부적으로 정렬

  • sorted() -> 리스트의 정렬된 복사본을 반환

sorted : 원본 데이터를 보존하고자 할때 유용

1
2
3
4
5
L1 = [9, 10, 7, 19, 1, 2, 20, 21, 7, 8]
L1.sort(reverse=True)
L2 = sorted(L1)
print(L1)
print(L2)
[21, 20, 19, 10, 9, 8, 7, 7, 2, 1]
[1, 2, 7, 7, 8, 9, 10, 19, 20, 21]

tuple

  • 리스트와 달리 ()로 표현한다.

  • 리스트와 같이 복수개의 값을 갖는 컬렉션 타입

  • 한번 생성하면 변경이 불가능하다.(immutable)

  • 즉, indexing으로 개별접근을 해도 값의 추가, 변경, 삭제는 불가능 하다.

1
2
3
4
5
L1 = [1, 2, 3]
T1 = (1, 2, 3)

print(type(L1))
print(type(T1))
<class 'list'>
<class 'tuple'>
1
2
3
4
L1[0] = 100 # 0번째 index 자리에 100을 추가
print(L1)

# 리스트는 변경이 가능 (Mutable)
[100, 2, 3]
1
2
b[0] = 100  # 튜플은 변경이 불가능하다!
print(b) 

튜플 indexing

1
2
3
4
5
6
# 인덱싱과 슬라이싱
T = [4, 5, 1, 2, 10, 6]

print("T[2] = {}".format(T[2]))
print("T[4] = {}".format(T[4]))
print("T[-1] = {}".format(T[-1]))
T[2] = 1
T[4] = 10
T[-1] = 6

튜플 slicing

1
2
3
4
5
6
T = [4, 5, 1, 2, 10, 6]

print("T[0:3] = {}".format(T[0:3]))
print("T[:3] = {}".format(T[:3])) 
print("T[2:] = {}".format(T[2:]))
print("T[0:4:2] = {}".format(T[0:4:2]))
T[0:3] = [4, 5, 1]
T[:3] = [4, 5, 1]
T[2:] = [1, 2, 10, 6]
T[0:4:2] = [4, 1]
1

튜플 관련 함수

소괄호 없이 생성

1
2
3
4
# 소괄호 없이 튜플 생성할 수 있다.

T = 1, 2, 3, 4
print(T)
(1, 2, 3, 4)

원소가 1개인 튜플 만들기

1
2
3
4
# 원소가 하나인 튜플은 반드시 원소 뒤에 쉼표를 붙여야 한다.
T = 1, 
print(type(T))
print(T)
<class 'tuple'>
(1,)

함수의 가변 인자로 활용

  • 아직 함수의 내용을 정리하지 않았기에 활용 용도가 있다는 것 정도만 이해하고 넘어가면 좋겠다.
1
2
3
4
5
6
7
8
9
10
11
12
13
# 함수의 가변 인자로 사용
def f(*x): # *: 인자수가 정해지지 않았음을 의미
    print("입력된 데이터의 타입:{}".format(type(x)))
    sum_x = 0
    product_x = 1
    for val in x:
        sum_x += val
        product_x *= val
    return sum_x, product_x
        
#S, P = f(1, 2, 3, 4, 5) # 출력을 각각 S와 P로 받음
S, P = f(1,2) # 출력을 각각 S와 P로 받음
print(S, P)
입력된 데이터의 타입:<class 'tuple'>
3 2

tuple unpacking

  • 튜플은 괄호를 생략해도 만들 수 있다.
1
2
a = (100, 200)
type(a)
tuple
1
2
a = 100, 200
type(a)
tuple
  • 이를 활용해 여러개의 변수에 여러개의 튜플의 값을 차례대로 대입시킬 수 있다.
1
2
a, b, c, d = 100, 200, 300, 400
print(a, b, c, d)
100 200 300 400

튜플형태의 a와 b의 값을 교환하려면?

1
2
3
4
a = 5
b = 4

print(a, b)
5 4
1
2
3
4
5
6
# 잘못된 예시

a = b #b를 a에 대입 -> a가 b의 값으로 업데이트 됨
b = a # b를 a에 대입 -> 이미 b의 값이 업데이트된 a (사실상 b)가 b에 업데이트 되는 것

print(a, b)
4 4
1
2
3
4
5
6
7
8
9
10
11
12
13
# 올바른 예시 - 일반적인 programming 방식

a = 5
b = 4

print(a,b)

# logic
temp = a # 임시로 어떤 값에 a를 입력해둔다.
a = b # a를 b의 값으로 업데이트 시킨다.
b = temp # b의 값을 임시로 입력해둔 temp(즉 원래의 a값) 으로 업데이트 시킨다.

print(a,b)
5 4
4 5

tuple unpacking 을 통해 이 과정을 한번에 해결 할 수 있다. (파이썬에서 자주 tuple이 가장 많이 사용되는 예시)

1
2
3
4
5
6
7
8
a = 5
b = 4

print(a,b)

a, b = b, a 

print(a, b)
5 4
4 5

List와 Tuple의 공통점

공통점 1. Indexing과 Slicing이 가능하다.

  • 리스트와 튜플 모두 인덱싱과 슬라이싱이 가능하다.

  • 관련 내용은 위에서 개별적으로 증명하였기에 따로 정리하지 않음

공통점 2. for문을 이용한 순회가 가능하다(iterable)

  • for data in List:

  • for data in Tuple:

  • 즉, max, min등의 순회 가능한 요소를 입력받는 함수의 입력으로 사용할 수 있다. (추후 ‘함수’ 관련 파트에서 더 자세하게 다룰 예정)

1
2
3
4
5
6
7
8
9
10
11
# for문을 이용한 순회
L = [1, 2, 3, 4, 5]
T = (1, 2, 3, 4, 5)

print("List 순회")
for val in L:
    print(val ** 2) #val의 제곱을 출력

print("\nTuple 순회")
for val in T:
    print(val ** 2) #val의 제곱을 출력
List 순회
1
4
9
16
25

Tuple 순회
1
4
9
16
25

List와 Tuple의 차이점

차이점 1. 가변(mutable)과 불변(immutable)

  • 위에서 증명했듯이 list 는 요소를 바꿀 수 있지만, tuple은 불가능하다.

  • 이러한 차이 때문에 list는 dict의 key로 사용될 수 있지만, tuple은 불가능 하다.

  • 조건을 입력해서, 해당 조건에 대응되는 값들을 출력하는 사전 구축은 데이터 전처리에서 자주 사용되는 기능이다. (추후 전처리에서 자세히 설명 예정)

차이점 2. Tuple의 순회속도가 List보다 조금 더 빠르다.

  • 요소 변경이 필요없고, 요소에 대한 연산 결과 만 필요한 경우는 리스트 보다는 튜플이 더 적합하다.

  • 보통 (데이터가 큰 경우에 한해) 리스트로 작업 후, 튜플로 자료형을 바꾼 뒤 순회를 한다.

1
2
3
# 순회 속도 비교하기
large_L = list(range(100000000)) # 100000000개의 값이 담긴 리스트 생성
large_T = tuple(range(100000000)) # 100000000개의 값이 담긴 튜플 생성
1
2
3
# time 모듈의 time 함수 사용 : 현재시간을 반환해주는 함수
import time
time.time()
1625271172.8716881
1
2
3
4
5
6
7
# 리스트 순회 속도 측정
import time
start_time = time.time()
for val1 in large_L:
    pass
end_time = time.time()
print(end_time - start_time)
3.0789952278137207
1
2
3
4
5
6
# 튜플 순회 속도 측정
start_time = time.time()
for val2 in large_T:
    pass
end_time = time.time()
print(end_time - start_time)    
3.0692572593688965

리스트를 튜플로 변환 후 순회

  • 리스트 -> 튜플 : tuple(list)

  • 튜플 -> 리스트 : list(tuple)

1
large_T2 = tuple(large_L) #large_L 을 튜플로 변경해 large_T2로 변수 선언
1
2
3
4
5
6
# 튜플 순회 속도 측정
start_time = time.time()
for val2 in large_T2:
    pass
end_time = time.time()
print(end_time - start_time)    
3.0806164741516113

댓글남기기