파알못 파이썬: 2. 데이터 구조

Table of Content

파이썬을 공부한 내용을 정리한 글입니다. 파알못이라 이상하거나 틀린 내용이 있을 수 있습니다…

리스트(Lists)

리스트는 대괄호 []로 선언합니다. 더하기(+) 및 곱하기(*) 연산이 가능하며 list() 함수를 사용하여 매개변수를 통해 전달받은 요소들을 리스트에 추가할 수 있습니다.

letters = ["a", "b", "c"]  # 1차원 리스트
matrix = [[0, 1], [2, 3]]  # 2차원 리스트

# [0, 0, 0, 0, 0]
zeros = [0] * 5

# [0, 0, 0, 0, 0, 'a', 'b', 'c']
combined = zeros + letters

# [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]
numbers = list(range(20))

# ['H', 'e', 'l', 'l', 'o', ' ', 'W', 'o', 'r', 'l', 'd']
chars = list("Hello World")

# 리스트 요소의 개수(11개)
len(chars)

리스트 아이템에 접근하기

letters = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h']
letters[0] = 'A'
print(letters[-1])  # 맨 마지막 요소 출력: h
print(letters[1:3])  # 시작 인덱스부터 끝 인덱스 직전까지 출력
print(letters[::2])  # 처음부터 두 단계식 건너뛰며 출력
print(letters[1::3])  # 시작 인덱스부터 세 단계씩 건너뛰며 출력

numbers = list(range(20))
print(numbers[::-1])  # 리스트 거꾸로 출력

Unpacking Lists 구문

리스트의 아이템을 여러 변수에 하나씩 할당하려면 아래 구문을 사용합니다.

numbers = list(range(3))
first, second, three = numbers
# first, second = numbers # 에러! 개수가 서로 맞지 않음

print(first) # 0
print(second) # 1
print(three) # 2

위의 방법은 할당할 변수의 개수와 리스트 아이템의 개수가 일치해야 합니다. 할당할 변수의 개수가 리스트 아이템의 개수보다 적으면 나머지 리스트 아이템의 값을 튜플로 받을 변수명 앞에 아스테리스크(*)를 붙입니다.

numbers = list(range(10))
first, second, *other = numbers

print(first) # 0
print(second) # 1
print(other) # 튜플: [2, 3, 4, 5, 6, 7, 8, 9]

리스트 반복

for문을 사용하여 리스트의 모든 아이템들에 접근할 수 있습니다.

letters = ['a', 'b', 'c']

# letters 아이템을 하나씩 반환
for letter in letters:
    print(letter)

# 열거형 객체를 하나씩 반환
for letter in enumerate(letters):
    print(letter) # 튜플 형태로 출력

# 열거형 객체에서 index와 이에 대응하는 값을 하나씩 반환
for index, letter in enumerate(letters):
    print(index, letter)

리스트 아이템 추가 및 제거

letters = ['a', 'b', 'c']

# 추가
letters.append('d')  # 리스트의 맨 끝에 아이템 추가
letters.insert(0, '-')  # 특정 인덱스에 아이템 추가

# 제거
letters.pop()  # 리스트의 맨 끝 아이템 제거
letters.pop(0)  # 특정 인덱스의 아이템 제거
letters.remove("b")  # 특정 값을 갖는 아이템 제거. 해당 아이템이 없으면 오류 발생. 해당 아이템이 여러 개인 경우 첫번째 것만 제거.
del letters[0:2]  # 범위를 지정하여 리스트의 아이템 제거
letters.clear() # 리스트의 모든 객체 제거
print(letters)

리스트 아이템 찾기

letters = ['a', 'b', 'c']

print(letters.count('a'))  # 특정 값을 갖는 아이템의 개수 계산

# 리스트의 아이템이 존재하는지 검사
if 'd' in letters:
    print(letters.index('d'))

리스트 정렬하기

리스트를 정렬 할 땐 sort() 함수 또는 sorted() 함수를 사용합니다. sort() 함수는 리스트의 값을 수정하지만 sorted() 함수는 리스트의 값을 수정하지 않고 수정된 리스트의 값을 반환합니다.

numbers = [3, 23, 1, 93, 8, 6]

# sort() 함수는 기본 오름차순 정렬. 원본 리스트를 수정함.
numbers.sort()  # 오름차순 정렬
numbers.sort(reverse=True)  # 내림차순 정렬

# sorted() 함수는 정렬된 리스트를 반환할 뿐 원본 리스트를 수정하지 않음
sorted(numbers)  # 오름차순 정렬
sorted(numbers, reverse=True)  # 내림차순 정렬

print(numbers)

튜플로 구성된 리스트를 정렬할 때 어떤 값을 기준으로 정렬할지 키 값을 넘길 수 있습니다.

items = [
    ('Product1', 10), 
    ('Product3', 9), 
    ('Product2', 12),
]

# 정렬에 사용할 키 값을 반환하는 함수
def sort_item(item):
    return item[1]

items.sort()  # 각 튜플의 첫번째 아이템을 기준으로 정렬
items.sort(key=sort_item)  # 정렬 기준으로 삼을 키(함수) 전달
items.sort(key=sort_item, reverse=True)  # 역정렬

print(items)

바로 위의 예제와 달리 람다 함수를 사용하면 익명 함수를 선언하여 코드를 줄일 수 있습니다.

  • 람다 함수의 구조: lambda parameters:expression
items = [
    ('Product1', 10),
    ('Product3', 9),
    ('Product2', 12),
]

items.sort()  # 각 튜플의 첫번째 아이템을 기준으로 정렬
items.sort(key=lambda item: item[1])  # 람다 함수를 사용하여 정렬 기준인 키(익명 함수) 전달
items.sort(key=lambda item: item[1], reverse=True)  # 역정렬

print(items)

map() 함수

map() 함수를 사용하면 리스트의 아이템을 제어할 수 있습니다.

items = [
    ('Product1', 10),
    ('Product3', 9),
    ('Product2', 12),
]

# map() 함수를 사용하여 리스트 내 튜플의 두 번째 값을 각각 꺼내옴
x = map(lambda item: item[1], items)
for item in x:
    print(item)

# map() 함수를 사용하여 리스트 내 튜플의 두 번째 값을 모두 가져와 리스트로 생성
prices = list(map(lambda item: item[1], items))
print(prices)

List Comprehension

map() 함수 대신 List Comprehension을 사용해도 같은 결과를 얻을 수 있습니다.

items = [
    ('Product1', 10),
    ('Product3', 9),
    ('Product2', 12),
]

prices = [item[1] for item in items] # List Comprehension
print(prices)

리스트 필터링

filter() 함수를 사용하면 조건에 맞는 리스트의 아이템을 추려낼 수 있습니다.

items = [
    ('Product1', 10),
    ('Product3', 9),
    ('Product2', 12),
]

# 개수가 10 이상인 튜플 추려내기
filtered = list(filter(lambda item: item[1] >= 10, items))
print(filtered)

filter() 함수 대신 List Comprehension을 사용해도 같은 결과를 얻을 수 있습니다.

items = [
    ('Product1', 10),
    ('Product3', 9),
    ('Product2', 12),
]

filtered = [item for item in items if item[1] >= 10] # List Comprehension
print(filtered)

zip() 함수

zip() 함수는 여러 리스트들을 전달받아 같은 인덱스끼리 잘라서 결합한 후 리스트로 반환합니다.

list1 = [1, 2, 3]
list2 = [10, 20, 30]

listed1 = list(zip(list1, list2))
listed2 = list(zip('ABC', list1, list2))

print(listed1)  # [(1, 10), (2, 20), (3, 30)]
print(listed2)  # [('A', 1, 10), ('B', 2, 20), ('C', 3, 30)]

스택(Stack)

스택은 LIFO(후입선출)형 자료구조입니다.

stack = []
stack.append(1) # 자료 추가
stack.pop() # 자료 제거

if not stack:
    print("Empty")

큐(Queue)

큐는 FIFO(선입선출)형 자료구조입니다.

from collections import deque

queue = deque([])
queue.append(1)
queue.append(2)
queue.append(3)
queue.pop() # 가장 나중에 들어온 자료 제거
queue.popleft() # 가장 먼저 들어온 자료 제거
print(queue)

if not queue:
    print("Empty")

튜플(Tuples)

튜플은 읽기 전용인 일종의 열거형(iterable, member를 하나씩 차례로 반환 가능한)리스트 입니다. 소괄호 ()를 사용하거나 생략하여 선언합니다.

point1 = ()
point2 = (1, 2) # 소괄호로 튜플 선언
point3 = 1, 2 # 소괄호 없이 튜플 선언

print(type(point1))
print(type(point2))
print(type(point3))

더하기(+) 및 곱하기(*) 연산을 통해 새로운 튜플을 만들 수 있습니다.

point1 = (1, 2) + (3, 4)
point2 = (1, 2) * 3

print(point1) # (1, 2, 3, 4)
print(point2) # (1, 2, 1, 2, 1, 2)

특정 자료를 튜플 데이터로 변환하려면 tuple() 함수를 사용합니다.

point1 = tuple([1, 2, 3])  # list -> tuple 변환
point2 = tuple("Hello World")  # str -> tuple 변환

print(point1)  # (1, 2, 3)
print(point2)  # ('H', 'e', 'l', 'l', 'o', ' ', 'W', 'o', 'r', 'l', 'd')

튜플 값에 접근하는 방법도 리스트와 비슷합니다.

point = (1, 2, 3)

print(point[1:2])
x, y, z = point
if 10 in point:
    print("Exists")

# point[0] = 10  # 에러!

배열(Arrays)

배열은 리스트보다 메모리를 덜 차지하고 처리속도가 빠른 자료구조입니다. array() 함수를 사용하며 선언합니다.

이 함수의 첫번째 매개변수엔 typecode를 지정해야 하는데 이에 대해선 https://docs.python.org/3/library/array.html 를 참고하기 바랍니다.

from array import array

numbers = array("i", [1, 2, 3])
numbers.append(4)
numbers.insert(0, 0)  # 특정 위치에 아이템 추가
# numbers[1] = 1.0 # 에러!

# 배열 요소 하나씩 꺼내오기
for num in numbers:
    print(num)

print(numbers)

세트(Sets)

세트는 고유한 아이템만을 가지는 자료구조입니다. 중복을 갖지 않는 자료가 필요할 때 사용하며 중괄호 {}로 선언합니다.

numbers = [1, 1, 2, 2, 3, 4, 4, 2]
uniques = set(numbers)  # set() 함수로 리스트를 세트로 변환
print(uniques)  # {1, 2, 3, 4}

second = {1, 1, 4}  # 중괄호 {}를 사용하여 세트 생성
second.add(5)
second.remove(5)
print(len(second))  # 2
print(second)  # {1, 4}

세트는 집합 연산이 가능합니다. 수학시간에 배웠던 집합의 연산을 떠올리면 적절할 것 같습니다.

numbers = [1, 1, 2, 2, 3, 4, 4, 2]
first = set(numbers)
second = {1, 4}

print(first | second)  # 합집합 결과 출력: {1, 2, 3, 4}
print(first & second)  # 교집합 결과 출력: {1. 4}
print(first - second)  # 차집합 결과 출력: {2, 3}
print(first ^ second)  # 교집합의 여집합 결과 출력: {2, 3}

세트는 인덱싱을 지원하지 않습니다. 인덱싱을 사용하려면 리스트로 대체하여 사용해야 합니다.

numbers = [1, 1, 2, 2, 3, 4, 4, 2]
first = set(numbers)

# set 객체는 인덱싱을 지원하지 않음. 리스트로 대체 사용.
# print(first[0]) # 오류 발생

# set 객체 내에 특정 값이 존재하는지 확인 가능
if 1 in first:
    print("Exist")

딕셔너리(Dictionaries)

딕셔너리는 키와 값으로 구성된 자료의 모음집입니다. 키는 정수 또는 문자열로 설정할 수 있으며 값은 다양한 자료형으로 설정할 수 있습니다.

# 딕셔너리 생성
point = {"x": 1, "y": 2}
point = dict(x=1, y=2)

# 키에 대응하는 값 지정
point["x"] = 10
point["y"] = 20
point["z"] = 30  # 키가 없던 경우 새롭게 추가

# 키에 대응하는 값 삭제
del point["z"]

# 딕셔너리 값 전체 출력
print(point)

for문을 사용하면 딕셔너리의 아이템을 하나씩 꺼내올 수 있습니다. 이 때 key, value 키워드를 사용합니다.

point = {"x": 1, "y": 2}

# 딕셔너리 아이템 하나씩 꺼내오기 1
for key in point:
    print(key, point[key])

# 딕셔너리 아이템 하나씩 꺼내오기 2
for key, value in point.items():
    print(key, value)

# 딕셔너리 아이템을 하나씩 튜플로 가져오기
for x in point.items():
    print(x)

딕셔너리에 키가 존재하는지 확인하려면 if문이나 get() 함수를 사용합니다.

point = {"x": 1, "y": 2}

# 키가 있는지 확인하는 방법 1: if문
if "a" in point:
    print(point["a"])

# 키가 있는지 확인하는 방법 2: get() 함수
print(point.get("z"))  # 키가 없으면 None 반환
print(point.get("x"))  # 키 값이 있으면 키에 해당하는 값 반환
print(point.get("z", "None"))  # 키 값이 있으면 키에 해당하는 값 반환, 그렇지 않으면 설정된 값 반환

Dictionary Comprehension

딕셔너리에 10 이하의 짝수를 추가하려면 다음과 같이 for문을 사용할 수 있습니다.

values = {}
for x in range(5):
    values[x*2] = x * 2

이 대신 Dictionary Comprehension을 사용하면 한 줄로 코딩이 가능합니다.

values = {x*2: x*2 for x in range(5)}

변수 스와핑(Swapping Variables)

두 변수의 값을 서로 바꾸는 경우 임시 변수를 하나 만들어 바꾸는 방법이 있습니다.

x = 10
y = 11

z = x
x = y
y = z

print("x", x)
print("y", y)

파이썬에선 다음과 같이 간략하게 두 변수 값을 바꿀 수 있습니다.

x = 10
y = 11
x, y = y, x # 변수 스와핑

print("x", x)
print("y", y)

제너레이터(Generators) 표현식

제너레이터는 리스트와 달리 모든 값을 메모리에 저장하지 않고 호출될 떄마다 값을 하나씩 전달(yield)합니다.

from sys import getsizeof

values = (x*2 for x in range(100000))  # 제너레이터: ()
print("gen: ", getsizeof(values))  # 128 bytes
values = (x*2 for x in range(10000000000000))
print("gen: ", getsizeof(values))  # 범위가 크게 늘어도 128 bytes

values = [x*2 for x in range(100000)]  # 리스트: []
print("list:", getsizeof(values))  # 824472 bytes
values = [x*2 for x in range(10000000000000)]
# print("list:", getsizeof(values))  # 오래 걸림. 안 돌리는 걸 추천.

언패킹 연산자(Unpacking Operator)

언패킹 연산자는 자료를 파헤쳐 각각의 아이템을 반환하도록 해주는 연산자입니다.

numbers = [1, 2, 3]  # 리스트
print(numbers)  # 리스트 형태 전체 출력: [1, 2, 3]
print(*numbers)  # 리스트 아이템만 출력: 1 2 3

values = [range(5), "Hello"]
print(values) # [range(0, 5), 'Hello']

values = [*range(5), *"Hello"]
print(values) # [0, 1, 2, 3, 4, 'H', 'e', 'l', 'l', 'o']

언패킹 연산자는 여러 리스트들의 아이템을 하나로 합칠 때 유용하게 사용할 수 있습니다.

first = [1, 2]
second = [3, 4, 5]

values = [first, 'A', second] # [[1, 2], 'A', [3, 4, 5]]: 리스트 내에 리스트가 들어간 형태
print(values)

values = [*first, 'A', *second] # [1, 2, 'A', 3, 4, 5]: 리스트 내에 정수 및 문자만 들어간 형태
print(values)

딕셔너리에서의 언패킹 연산자는 아스테리스크 2개를 사용합니다. 아스테리스크 1개를 사용하면 키가 언패킹됩니다.

first = {"x": 1}
second = {"x": 10, "y": 2}
combined1 = {**first, **second, "z": 1}
combined2 = {*first, *second}

print(combined1)  # {'x': 10, 'y': 2, 'z': 1}
print(combined2)  # y x
print(*combined1)  # x y z

연습문제: 가장 많이 반복되는 문자 찾기

단어를 분석하여 가장 많이 등장하는 문자를 찾는 연습문제입니다.

word = "Supercalifragilisticexpialidocious"

char_frequency = {}
for char in word:
    if char in char_frequency:
        char_frequency[char] += 1
    else:
        char_frequency[char] = 1

char_frequency_sorted = sorted(
    char_frequency.items(),
    key=lambda kv: kv[1],
    reverse=True)

print("모든 단어:", char_frequency_sorted)
print("가장 많은 단어:", char_frequency_sorted[0])

댓글 남기기