01. 컨테이너

컨테이너 (Container)

여러 개의 값을 저장할 수 있는 것 (~객체~)

  • 시퀀스형 (Sequence) : 순서가 있는(ordered) 데이터

    • 순서가 있다 != 정렬되어 있다.

    • 리스트(list), 튜플(tuple), 레인지(range), 문자형(string), 바이너리(binary)

  • 비 시퀀스형 (Non-sequence) : 순서가 없는(unordered) 데이터

    • 세트(set), 딕셔너리(dictionary)

1.1 시퀀스형 컨테이너

시퀀스(sequence)는 데이터가 순서대로 나열된(ordered) 형식을 나타냅니다.

주의!

순서대로 나열된 것이 ‘정렬되었다(sorted)’라는 뜻은 아닙니다.

특징

  1. 순서를 가질 수 있습니다.

  2. 특정 위치의 데이터를 가리킬 수 있습니다.

종류

파이썬에서 기본적인 시퀀스 타입은 다음과 같습니다.

  • 리스트(list)

  • 튜플(tuple)

  • 레인지(range)

  • 문자형(string)

  • 바이너리(binary) : 따로 다루지는 않습니다.

1.1.1 리스트

list figure
[value1, value2, value3]

리스트(list)는 대괄호[]list() 를 통해 만들 수 있습니다. 순서가 있는 시퀀스로, 값에 대한 접근은 list[idx]를 통해 합니다.

  • 인덱스는 0 부터 시작

# 빈 list를 만들어봅시다.
# 변수명 my_list인 list를 대괄호로 만들어봅시다.
# 변수명 another_list인 list를 list()로 만들어 봅시다.
# 두 변수의 타입을 출력해 봅시다.

# literal : 단축키와 비슷. 소스 코드의 고정된 값을 대표하는 용어
my_list = []

# initialize : 리스트를 정식으로 생성하는 방법
another_list = list()

print(type(my_list))
print(type(another_list))
<class 'list'>
<class 'list'>
# 원소를 포함한 list를 만들어 봅시다.
# 변수명이 locations인 list에 ssafy 지역 5곳을 원소로 포함해 만들어 봅시다.
# 변수 locations를 출력해 봅시다.
# locationsㅇ의 타입을 출력해 봅시다.

locations = ['서울', '대전', '구미', '광주', '부울경']
print(locations)
print(type(locations))
['서울', '대전', '구미', '광주', '부울경']
<class 'list'>
# location의 첫번째 값을 인덱스로 접근해 봅시다.

locations[0]
'서울'

1.1.2 튜플

(value1, value2)

튜플(tuple)은 리스트와 유사하지만, 수정 불가능(불변, immutable)하고, 읽을 수 밖에 없습니다. 소괄호(()) 혹은 tuple()을 통해 생성하고, 값에 대한 접근은 my_tuple(idx)로 합니다.

직접 사용하기 보다는 파이썬 내부에서 다양한 용도로 활용되고 있습니다.

  • multiple assignment

  • 추후 함수에서 복수의 값을 반환하는 경우에도 활용

# tuple을 만들어봅시다.
# 변수명이 my_tuple인 tuple을 만들어 봅시다. 단, 무작위 정수 2개를 포함하여 만듭니다.
# my_tuple의 타입을 출력해 봅시다.

my_tuple = (1, 2)
print(type(my_tuple))
<class 'tuple'>
# 아래와 같은 방식으로도 만들 수 있습니다.

another_tuple = 1, 2
print(another_tuple)
print(type(another_tuple))
(1, 2)
<class 'tuple'>
# 파이썬 내부에서는 다음과 같이 활용됩니다. (변수 및 자료형 예제에서 사용된 코드입니다.)

x, y = 1, 2
print(x)
print(y)
1
2
# 실제로는 tuple로 처리됩니다.

x, y = (1, 2)
print(x)
print(y)
1
2
# 변수의 값을 swap하는 코드 역시 tuple을 활용하고 있습니다. 

x, y = y, x
print(x)
print(y)
2
1
# 변수명이 empty인 빈 tuple을 만들어 봅시다.
# 빈 tuple은 빈 괄호 쌍으로 만들어집니다.
# empty의 타입을 출력해 봅시다.
# empty의 길이를 출력해 봅시다.

empty = ()
print(type(empty))
print(len(empty))
<class 'tuple'>
0
# 변수명이 single_tuple인 하나의 요소(값)로 구성된 tuple을 만들어 봅시다. (길이가 1)
# 하나의 요소(값)로 구성된 tuple은 값 뒤에 쉼표를 붙여서 만듭니다.
# single_tuple의 타입을 출력해 봅시다.
# single_tuple의 길이를 출력해 봅시다.

single_tuple = ('hello',)
print(type(single_tuple))
print(len(single_tuple))
<class 'tuple'>
1
# 길이가 1인 tuple을 만들 때 쉼표가 없는 경우 어떻게 되는지 확인 해봅시다.

tuple_or_not = ('hello')
print(type(tuple_or_not))
<class 'str'>

1.1.3 레인지

레인지(range())는 숫자의 시퀀스를 나타내기 위해 사용됩니다.

기본형 : range(n)

0부터 n-1까지 값을 가짐

범위 지정 : range(n,m)

n부터 m-1까지 값을 가짐

범위 및 스텝 지정 : range(n,m,s)

n부터 m-1까지 +s만큼 증가한다

# range를 만들어봅시다.
# 0부터 2까지 값을 가지는 range를 만들고 타입을 출력해 봅시다.

type(range(1))
range
# 0부터 9까지 값을 가지는 range를 만들고 list로 형 변환을 해 봅시다.
# 작성한 range를 list()로 감싸 형 변환 할 수 있습니다.

list(range(10))
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
# 4부터 8까지의 숫자를 담은 range를 만들고 list로 형 변환을 해 봅시다.

list(range(4, 9))
[4, 5, 6, 7, 8]
# range(start, end, [step, ])을 활용합니다.
# 0부터 -9까지 담긴 range를 만들고 list로 형 변환을 해 봅시다.
# 출력 결과는 다음과 같습니다.
# [0, -1, -2, -3, -4, -5, -6, -7, -8, -9]

list(range(0, -10, -1))
[0, -1, -2, -3, -4, -5, -6, -7, -8, -9]

1.1.4 시퀀스에서 활용할 수 있는 연산자 / 함수

operation

설명

x in s

containment test

x not in s

containment test

s1 + s2

concatenation

s * n

n번만큼 반복하여 더하기

s[i]

indexing

s[i:j]

slicing

s[i:j:k]

k간격으로 slicing

len(s)

길이

min(s)

최솟값

max(s)

최댓값

s.count(x)

x의 개수

containment test

시퀀스 포함 여부 확인

  • in

  • not in

# containment test를 확인해봅시다.
# 변수 my_string에 문자열 'string'을 담습니다.
# 문자열 'a'가 my_string에 포함되는지 'in' 연산자를 통해 확인하고 결과를 출력해봅시다.

my_string = 'string'
print('a' in my_string)
False
# containment test를 확인해봅시다.
# 변수명이 my_list이고 정수 1, 2, 3, 5, 1들을 원소로 가지는 list를 만듭니다.
# 정수 3이 my_list에 포함되는지 'in' 연산자를 통해 확인하고 결과를 출력해봅시다.

my_list = [1, 2, 3, 5, 1]
print(3 in my_list)
True

concatenation (+)

시퀀스 간의 concatenation (연결 / 연쇄)

# concatenation(연결, 연쇄)를 해봅시다.
# 문자열 '안녕'과 문자열 '하세요'를 더하고 결과를 출력해 봅시다.
# 정수 1과 2를 포함한 tuple과 정수 5와 6을 포함한 tuple을 더하고 결과를 출력해봅시다.

print('안녕,' + '하세요')
print((1, 2) + (5, 6))
안녕,하세요
(1, 2, 5, 6)

시퀀스 반복 (*)

# 숫자 0이 6개 있는 list를 만들어봅시다.
# 단, 곱셉 연산자를 활용하여 만듭니다.

[0] * 6
[0, 0, 0, 0, 0, 0]

인덱싱 (indexing)

시퀀스의 특정 인덱스 값에 접근

  • 해당 인덱스가 없는 경우 IndexError

# indexing과 slicing을 하기 위해 list하나를 만들어주세요.
# 변수명은 locations, 원소는 무작위 지역 5곳을 문자열로 작성합니다.
# locations의 첫번째 원소에 index로 접근해 봅시다.

locations = ['서울', '대전', '구미', '광주', '부울경']
locations[0]
'서울'

슬라이싱 (slicing)

시퀀스를 특정 단위로 슬라이싱

# 두번째, 세번째 값만 가져와봅시다.
# slicing을 사용합니다.

locations[1:3]
['대전', '구미']

시퀀스를 k간격으로 특정 단위로 슬라이싱

# 0부터 30까지의 정수를 원소로 가지는 list를 만들어 봅시다.
# range를 활용하여 list를 생성합니다.

sample_list = list(range(0, 31))
print(sample_list)
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30]
# list[start: end: step]
# 위에서 작성한 list를 활용하여 0부터 30까지 3씩 증가하는 값을 가지는 새로운 list를 만들어 봅시다.
# 새롭게 생성한 list를 출력해 봅시다.

test_list = sample_list[0:len(sample_list):3]
test_list = sample_list[0::3]
print(test_list)
[0, 3, 6, 9, 12, 15, 18, 21, 24, 27, 30]

최소 / 최대

시퀀스에서의 최솟/최댓값

  • 문자열은 ascii코드에 따름

  • ord 함수 활용하여 확인 가능

# 위에서 만든 list의 최솟값, 최댓값을 확인해봅시다.

print(min(test_list))
print(max(test_list))
0
30

count

시퀀스에서의 특정 원소의 개수

  • 시퀀스에 등장하지 않는 경우 0 반환

# list에 담긴 특정한 것의 개수를 확인할 수도 있습니다.

sample_list = [1, 2, 1, 3, 1, 5]
sample_list.count(1)
3

1.2 비 시퀀스형 컨테이너

비 시퀀스형 컨테이너(Non-sequence)는 순서가 없는(unordered) 데이터입니다.

  • 세트 (set)

  • 딕셔너리 (dictionary)

1.2.1 세트

세트(set)는 순서가 없고 중복된 값이 없는 자료구조입니다.

  • set은 중괄호({}) 혹은 set()을 통해 생성합니다.

    • 빈 세트를 만들려면 set()을 사용해야 합니다. ({}로 사용 불가능)

  • 순서가 없어 별도의 값에 접근할 수 없습니다.

  • set은 수학에서의 집합과 동일하게 처리됩니다.

    • 활용 가능한 연산자는 차집합(-), 합집합(|), 교집합(&)입니다.

{value1, value2, value3}
# set 두개를 만들어서 연산자들을 활용해봅시다.

set_a = {1, 2, 3}
set_b = {3, 6, 9}
  • 차집합은 연산자 -를 사용합니다.

# set_a와 set_b의 차집합을 구해봅시다.

print(set_a - set_b)
{1, 2}
  • 합집합은 연산자 |를 사용합니다.

# set_a와 set_b의 합집합을 구해봅시다.

print(set_a | set_b)
{1, 2, 3, 6, 9}
  • 교집합은 연산자 &을 사용합니다.

# set_a와 set_b의 교집합을 구해봅시다.

print(set_a & set_b)
{3}
# set은 중복된 값이 있을 수 없습니다.

set_c = {1, 1, 1}
print(set_c)
{1}
  • set을 활용하면 다른 컨테이너에서 중복된 값을 손쉽게 제거할 수 있습니다.

  • 단, set으로 변환하는 순간 순서를 보장할 수 없습니다.

# set으로 중복된 값을 제거해봅시다.
# 정수 1, 2, 3, 1, 1, 2를 원소로 가지는 list를 만듭니다.
# 생성한 list를 set으로 형 변환 합니다.
# 결과를 출력 해봅시다.

list_a = [1, 2, 3, 1, 1, 2]
print(set(list_a))
{1, 2, 3}
# 다시 list로 바꿔서 확인해봅시다.

list(set(list_a))
[1, 2, 3]

1.2.2 딕셔너리

딕셔너리(dictionary)는 keyvalue가 쌍으로 이뤄져있습니다.

{Key1:Value1, Key2:Value2, Key3:Value3, ...}
  • 중괄호({}) 혹은 dict()을 통해 만들 수 있습니다.

  • key변경 불가능(immutable)한 데이터만 가능합니다.

immutable

string, integer, float, boolean, tuple, range)

  • valuelist, dictionary를 포함한 모든 것이 가능합니다.

# 비어있는 dictionary를 두가지 방법으로 만들어봅시다.
# {}와 dict()로 만들 수 있습니다.
# 두 변수의 타입을 출력해 봅시다.

dict_a = {}
print(type(dict_a))
dict_b = dict()
print(type(dict_b))
<class 'dict'>
<class 'dict'>
# dictionary에 중복된 key는 존재할 수가 없습니다.

dict_a = {1: 1, 2: 2, 3: 3, 1: 4}
print(dict_a)
{1: 4, 2: 2, 3: 3}
# 지역번호가 담긴 전화번호부를 만들어봅시다.
# 변수 phone_book에 key를 지역명, value를 지역번호로 가지는 원소를 작성합니다.
# 예) 서울 - 02

phone_book = {'서울': '02', '경기': '031'}
# 위에서 작성한 phone_book이 가지고 있는 key 목록을 확인 해 봅시다.
# dictionary의 .keys() 메소드를 활용하여 key를 확인 해볼 수 있습니다.

phone_book.keys()
dict_keys(['서울', '경기'])
# 위에서 작성한 phone_book이 가지고 있는 value 목록을 확인 해 봅시다.
# 딕셔너리의 .values() 메소드를 활용하여 value를 확인 해볼 수 있습니다.

phone_book.values()
dict_values(['02', '031'])
# 위에서 작성한 phone_book이 가지고 있는 key와 value 목록을 확인 해 봅시다.
# 딕셔너리의 .items() 메소드를 활용하여 key, value를 확인 해볼 수 있습니다.

phone_book.items()
dict_items([('서울', '02'), ('경기', '031')])
for k, v in phone_book.items():
    print(k, v)
서울 02
경기 031

1.3 컨테이너 형변환

파이썬에서 컨테이너는 서로 변환할 수 있습니다.

typecasting
# 하나의 결과를 확인 한 후, 주석 `#` 을 활용하여 이전의 코드를 비활성화 합니다.
# 형변환 후의 결과를 확인 합니다.

# list를 형변환 해봅시다.

l = [1, 2, 3, 4]
str(l), tuple(l), set(l)
# range(l)
# dict(l)
('[1, 2, 3, 4]', (1, 2, 3, 4), {1, 2, 3, 4})
# tuple을 형변환 해봅시다.

t = (1, 2, 3, 4)
str(t), list(t), set(t)
# range(t)
# dict(t)
('(1, 2, 3, 4)', [1, 2, 3, 4], {1, 2, 3, 4})
# range를 형변환 해봅시다.

r = range(1, 5)
str(r), list(r), set(r), tuple(r)
# dict(r)
('range(1, 5)', [1, 2, 3, 4], {1, 2, 3, 4}, (1, 2, 3, 4))
# set을 형변환 해봅시다.

s = {1, 2, 3, 4}
str(s), list(s), tuple(s)
# range(s)
# dict(s)
('{1, 2, 3, 4}', [1, 2, 3, 4], (1, 2, 3, 4))
# dictionary를 형변환 해봅시다.

d = {'name': 'ssafy', 'year': 2020}
str(d), list(d), tuple(d), set(d)
# range(d)
("{'name': 'ssafy', 'year': 2020}",
 ['name', 'year'],
 ('name', 'year'),
 {'name', 'year'})

1.4 데이터의 분류

mutable vs. immutable

데이터는 크게 변경 가능한 것 (mutable)들과 변경 불가능한 것 (immutable)으로 나뉘며, python은 각각을 다르게 다룹니다.

1.4.1 변경 불가능한 데이터 (immutable)

  • 리터럴 (literal)

    • 숫자 (Number)

    • 글자 (String)

    • 참/거짓 (Bool)

  • range()

  • tuple()

  • frozenset()

# immutable 데이터의 복사는 어떻게 이루어질까?

num1 = 20
num2 = num1 
num2 = 10

print(num1)
print(num2)
20
10
# 아래 코드 블럭을 실행하여 복사 과정을 확인해 봅시다.

1.4.2 변경 가능한 데이터 (mutable)

  • list

  • dict

  • set

# mutable 데이터의 복사는 어떻게 이루어질까?

num1 = [1, 2, 3, 4]
num2 = num1
num2[0] = 100

print(num1)
print(num2)
[100, 2, 3, 4]
[100, 2, 3, 4]
# 아래 코드 블럭을 실행하여 복사 과정을 확인해 봅시다.

1.5 정리

컨테이너

container