Python 자료구조 완전 기초: list, tuple, dict, set과 참조 이해하기

Alchemist·2026년 9월 29일

KT AIVLE

목록 보기
32/130

프로그램에서는 값을 하나씩 저장하는 것보다 여러 데이터를 어떤 구조로 묶고 다룰지가 더 중요해지는 순간이 많다.

사용자 목록, 주문 내역, API 응답, 로그, 태그처럼 실제 데이터는 대부분 여러 값의 집합으로 표현된다.

Python에서 가장 자주 사용하는 기본 자료구조는 다음 네 가지다.

  • list
  • tuple
  • dict
  • set

이 글에서는 각 자료구조의 특징과 함께 인덱싱, 슬라이싱, 참조, 복사, 문자열 메서드, 컴프리헨션까지 정리한다.


자료구조를 선택하는 기준

자료구조순서수정중복대표 용도
list있음가능가능사용자 목록, 로그
tuple있음불가가능좌표, 고정 값 묶음
dict삽입 순서 유지가능키 중복 불가API 데이터, 객체 정보
set순서를 전제로 하지 않음가능불가태그, 권한, 중복 제거

처음에는 이 정도 기준만 알아도 대부분의 상황에서 적절한 자료구조를 고를 수 있다.


리스트 list

리스트는 여러 값을 순서대로 저장하는 자료구조다.

users = ["kim", "lee", "park"]

인덱스는 0부터 시작한다.

print(users[0])
print(users[1])
kim
lee

리스트에 값 추가하기

users = ["kim", "lee"]

users.append("park")

print(users)

여러 값을 추가할 때는 extend()를 사용할 수 있다.

users.extend(["choi", "jung"])

특정 위치에 삽입:

users.insert(1, "han")

값 삭제하기

값으로 삭제:

users.remove("lee")

인덱스로 삭제:

del users[0]

삭제하면서 값을 가져오기:

removed_user = users.pop()

리스트 정렬

원본 리스트 자체를 정렬하는 sort():

scores = [80, 95, 70, 88]

scores.sort()

print(scores)

새 리스트를 반환하는 sorted():

scores = [80, 95, 70, 88]

sorted_scores = sorted(scores)

print(scores)
print(sorted_scores)

원본을 보존해야 한다면 두 방식의 차이를 이해해야 한다.


튜플 tuple

튜플은 리스트처럼 순서를 가지지만 생성 후 항목을 수정할 수 없다.

position = (37.5, 127.0)
latitude = position[0]
longitude = position[1]

튜플 언패킹

position = (37.5, 127.0)

latitude, longitude = position

함수에서 여러 값을 반환할 때도 자주 사용된다.

def get_min_max():
    return 10, 100

minimum, maximum = get_min_max()

한 개짜리 튜플

괄호보다 쉼표가 중요하다.

value = (10,)

다음은 튜플이 아니라 정수다.

value = (10)

딕셔너리 dict

딕셔너리는 키와 값의 연결 관계를 저장한다.

user = {
    "id": 101,
    "name": "kim",
    "role": "admin",
}

값 조회:

print(user["name"])

값 추가:

user["age"] = 30

값 수정:

user["role"] = "editor"

존재하지 않는 키와 get()

다음 코드는 키가 없으면 KeyError를 발생시킨다.

user["email"]

값이 없을 가능성이 있다면 get()이 편리하다.

email = user.get("email")

print(email)

기본값도 지정할 수 있다.

email = user.get(
    "email",
    "등록되지 않음",
)

딕셔너리 반복

키와 값을 함께 순회:

for key, value in user.items():
    print(key, value)

키만:

for key in user.keys():
    print(key)

값만:

for value in user.values():
    print(value)

JSON과 딕셔너리

웹 API에서 받은 JSON 객체는 Python에서 보통 딕셔너리와 리스트의 조합으로 변환된다.

response_data = {
    "status": "success",
    "users": [
        {
            "id": 1,
            "name": "kim",
        },
        {
            "id": 2,
            "name": "lee",
        },
    ],
}

사용자 이름 출력:

for user in response_data["users"]:
    print(user["name"])

데이터 분석이나 API 개발을 공부한다면 list와 dict 조합에 익숙해지는 것이 특히 중요하다.


집합 set

집합은 중복되지 않는 값을 저장한다.

tags = {
    "python",
    "api",
    "python",
    "backend",
}

print(tags)

"python"은 한 번만 남는다.


리스트에서 중복 제거

user_ids = [1, 1, 2, 3, 3, 4]

unique_ids = set(user_ids)

print(unique_ids)

단, 집합을 사용할 때는 원래 순서를 보존하기 위한 자료구조로 생각하면 안 된다.


집합 연산

admin_permissions = {
    "read",
    "write",
    "delete",
}

editor_permissions = {
    "read",
    "write",
}

교집합:

print(
    admin_permissions
    & editor_permissions
)

합집합:

print(
    admin_permissions
    | editor_permissions
)

차집합:

print(
    admin_permissions
    - editor_permissions
)

부분집합 검사:

print(
    editor_permissions.issubset(
        admin_permissions
    )
)

권한, 태그, 카테고리처럼 포함 관계가 중요한 데이터에 잘 맞는다.


인덱싱

리스트, 튜플, 문자열처럼 순서가 있는 객체는 인덱스로 접근할 수 있다.

users = ["kim", "lee", "park"]

print(users[0])
print(users[-1])

-1은 마지막 항목이다.

print(users[-2])

뒤에서 두 번째 항목을 의미한다.


슬라이싱

일부분을 잘라낼 때 사용한다.

numbers = [0, 1, 2, 3, 4, 5]

print(numbers[1:4])
[1, 2, 3]

끝 인덱스는 포함되지 않는다.


시작과 끝 생략

numbers[:3]
numbers[3:]

스텝:

numbers[::2]

역순:

numbers[::-1]

문자열에도 동일하게 적용된다.

text = "python"

print(text[::-1])
nohtyp

in과 not in

roles = ["user", "editor", "admin"]

print("admin" in roles)
print("guest" not in roles)

문자열에서도 사용할 수 있다.

url = "/api/users"

if "/api/" in url:
    print("API 요청")

변수는 객체의 참조를 가진다

다음 코드를 보자.

original = [
    "python",
    "sql",
]

copied = original

copied.append("ai")
print(original)
print(copied)

결과:

['python', 'sql', 'ai']
['python', 'sql', 'ai']

copied = original은 새로운 리스트를 생성한 것이 아니다.

두 변수가 같은 리스트 객체를 가리킨다.


얕은 복사

별도의 리스트를 만들고 싶다면 복사가 필요하다.

original = [
    "python",
    "sql",
]

copied = original.copy()

copied.append("ai")
print(original)
print(copied)

결과:

['python', 'sql']
['python', 'sql', 'ai']

슬라이싱으로도 얕은 복사를 만들 수 있다.

copied = original[:]

중첩 객체와 깊은 복사

얕은 복사는 바깥 컨테이너만 새로 만든다.

original = [
    {"name": "kim"},
    {"name": "lee"},
]

copied = original.copy()

copied[0]["name"] = "park"
print(original)

원본 내부 딕셔너리도 변경된다.

완전히 독립된 중첩 구조가 필요하다면 deepcopy()를 사용할 수 있다.

import copy

copied = copy.deepcopy(original)

문자열 메서드

문자열도 객체이기 때문에 다양한 메서드를 제공한다.

text = "  Python API Tutorial  "

앞뒤 공백 제거:

text.strip()

대문자:

text.upper()

소문자:

text.lower()

시작 문자열 검사:

text.strip().startswith("Python")

find()와 in

문자열에 특정 값이 있는지만 확인하려면 in이 읽기 쉬운 경우가 많다.

url = "/api/users"

if "/api/" in url:
    print("API 경로")

위치를 찾아야 한다면 find()를 사용할 수 있다.

index = url.find("users")

찾지 못하면 -1을 반환한다.


join()과 split()

여러 문자열 연결:

skills = [
    "python",
    "sql",
    "fastapi",
]

text = ", ".join(skills)

print(text)
python, sql, fastapi

문자열 분리:

skills = text.split(", ")

리스트 컴프리헨션

반복문으로 새 리스트를 만드는 패턴을 간결하게 표현할 수 있다.

일반 반복문:

numbers = [1, 2, 3, 4]

squares = []

for number in numbers:
    squares.append(
        number ** 2
    )

컴프리헨션:

squares = [
    number ** 2
    for number in numbers
]

조건이 포함된 리스트 컴프리헨션

짝수만 제곱:

numbers = [
    1,
    2,
    3,
    4,
    5,
    6,
]

result = [
    number ** 2
    for number in numbers
    if number % 2 == 0
]

print(result)
[4, 16, 36]

조건과 변환이 너무 복잡해지면 일반 반복문이 더 읽기 쉽다.


딕셔너리 컴프리헨션

users = [
    "kim",
    "lee",
    "park",
]

length_by_user = {
    user: len(user)
    for user in users
}

집합 컴프리헨션

values = [
    1,
    1,
    2,
    3,
    3,
]

squares = {
    value ** 2
    for value in values
}

중복은 자동 제거된다.


어떤 자료구조를 선택해야 할까?

순서가 있고 값이 자주 바뀐다

logs = []

→ list

값 묶음이 고정되어야 한다

rgb = (
    255,
    120,
    80,
)

→ tuple

이름을 통해 값을 찾는다

user = {
    "id": 1,
    "name": "kim",
}

→ dict

중복 제거와 집합 관계가 중요하다

permissions = {
    "read",
    "write",
}

→ set


실전 예제: 사용자 행동 로그 집계

logs = [
    {
        "user_id": 1,
        "action": "login",
    },
    {
        "user_id": 2,
        "action": "view",
    },
    {
        "user_id": 1,
        "action": "purchase",
    },
    {
        "user_id": 3,
        "action": "login",
    },
    {
        "user_id": 2,
        "action": "view",
    },
]

활동 사용자와 행동별 횟수를 집계해보자.

active_users = set()
action_count = {}

for log in logs:
    active_users.add(
        log["user_id"]
    )

    action = log["action"]

    action_count[action] = (
        action_count.get(
            action,
            0,
        )
        + 1
    )

print(active_users)
print(action_count)

여기서는 자료구조의 역할이 분명하다.

  • 로그 전체 → list
  • 로그 한 건 → dict
  • 중복 없는 사용자 → set
  • 행동별 집계 → dict

자료구조는 결국 데이터를 앞으로 어떻게 사용할지에 따라 선택한다.


핵심 정리

  • list는 순서가 있고 수정 가능한 데이터 묶음이다.
  • tuple은 순서가 있지만 수정할 수 없어 고정된 값을 표현하기 좋다.
  • dict는 키를 이용해 값을 조회하는 구조에 적합하다.
  • set은 중복 제거와 집합 연산에 강하다.
  • 인덱스는 0부터 시작하며 음수 인덱스로 뒤에서 접근할 수 있다.
  • 슬라이싱은 시퀀스 일부를 간결하게 추출하는 기능이다.
  • a = b는 컬렉션을 복사하는 것이 아니라 같은 객체를 참조하게 만들 수 있다.
  • 컴프리헨션은 간단한 컬렉션 변환을 짧고 읽기 좋게 표현할 수 있다.
profile
html_programming_language

0개의 댓글