Python defaultdict로 편리한 딕셔너리 생성: 기본값으로 딕셔너리 쉽게 관리하기

Python에서 데이터 처리를 하다 보면 딕셔너리를 자주 사용하게 됩니다. 하지만 초기화되지 않은 키에 접근할 때 에러가 발생하는 것이 딕셔너리 사용의 불편한 점 중 하나입니다. 이때 defaultdict는 매우 유용한 도구입니다. defaultdict는 기본값을 설정할 수 있어, 키가 없어도 자동으로 초기화되어 에러 없이 딕셔너리를 관리할 수 있습니다.


defaultdict란 무엇인가?

defaultdict는 Python의 collections 모듈에 있는 클래스입니다. 일반 딕셔너리와 비슷하게 작동하지만, 초기화되지 않은 키에 접근할 때 자동으로 기본값을 생성해줍니다. 예를 들어, 빈 리스트나 0으로 자동 초기화할 수 있어 코드를 훨씬 간단하게 만들 수 있습니다.

defaultdict는 특히 집계 작업이나 데이터 분류 작업에서 유용하게 사용할 수 있습니다.


defaultdict 사용법: 기본값 설정과 초기화

defaultdict를 사용하려면 collections 모듈에서 import하고, 초기화하려는 기본값 타입을 지정해줍니다.

from collections import defaultdict

# 리스트를 기본값으로 설정하는 defaultdict 생성
default_dict = defaultdict(list)

# 기본값인 빈 리스트에 값 추가
default_dict["fruits"].append("apple")
default_dict["fruits"].append("banana")
default_dict["vegetables"].append("carrot")

# 결과 출력
print(default_dict)

결과

defaultdict(<class 'list'>, {'fruits': ['apple', 'banana'], 'vegetables': ['carrot']})

설명

  • defaultdict(list)새로운 키에 대해 빈 리스트를 기본값으로 설정합니다.
  • 키가 없을 때도 자동으로 빈 리스트가 생성되므로, append() 메서드로 직접 값을 추가할 수 있습니다.
  • 일반 딕셔너리를 사용하면, 매번 if 조건문으로 키 존재 여부를 확인해야 하지만, defaultdict를 통해 간단히 해결할 수 있습니다.

defaultdict로 데이터 집계하기

defaultdict는 특히 데이터 집계 작업에 유용합니다. 예를 들어, 여러 이름을 등급에 따라 분류하는 코드를 작성해보겠습니다.

from collections import defaultdict

# defaultdict로 등급별로 이름을 분류
grade_dict = defaultdict(list)
students = [("A", "Alice"), ("B", "Bob"), ("A", "Angela"), ("C", "Charlie")]

for grade, name in students:
    grade_dict[grade].append(name)

# 결과 출력
print(grade_dict)

결과

defaultdict(<class 'list'>, {'A': ['Alice', 'Angela'], 'B': ['Bob'], 'C': ['Charlie']})

설명

  • grade_dict[grade].append(name)에서 grade 키가 없으면 defaultdict가 자동으로 빈 리스트를 생성합니다.
  • 이름을 각 등급에 맞게 분류할 수 있으며, 키가 없을 때마다 새 리스트가 자동으로 초기화되므로 코드가 매우 간단해집니다.

숫자 집계에 유용한 int 기본값 설정

defaultdict(int)를 사용하면, 각 키에 대해 기본값이 0으로 설정됩니다. 이 방식은 숫자 카운트 작업에 매우 유용합니다. 예를 들어, 리스트에 있는 항목의 빈도를 카운트할 때 사용할 수 있습니다.

from collections import defaultdict

# 기본값이 0인 defaultdict 생성
count_dict = defaultdict(int)
items = ["apple", "banana", "apple", "orange", "banana", "apple"]

for item in items:
    count_dict[item] += 1

# 결과 출력
print(count_dict)

결과

defaultdict(<class 'int'>, {'apple': 3, 'banana': 2, 'orange': 1})

설명

  • count_dict[item] += 1을 할 때 item이 키에 없다면, defaultdict는 자동으로 0을 기본값으로 설정합니다.
  • 리스트에서 각 항목의 빈도를 쉽게 카운트할 수 있어, 반복문을 통해 조건문 없이 깔끔한 코드를 작성할 수 있습니다.

실전 예제: 데이터 분류 및 집계하기

여러 그룹에 속하는 데이터를 분류하고 집계해야 할 때 defaultdict를 사용하면 코드가 매우 깔끔해집니다. 아래는 단어의 길이별로 단어를 분류하는 예제입니다.

from collections import defaultdict

# 단어 길이에 따라 분류하는 defaultdict 생성
word_dict = defaultdict(list)
words = ["apple", "banana", "grape", "kiwi", "pineapple"]

for word in words:
    word_dict[len(word)].append(word)

# 결과 출력
print(word_dict)

결과

defaultdict(<class 'list'>, {5: ['apple', 'grape'], 6: ['banana'], 4: ['kiwi'], 9: ['pineapple']})

설명

  • len(word)는 각 단어의 길이를 계산하여, 길이에 따라 단어를 분류합니다.
  • 새 길이 값(키)이 등장하면 defaultdict가 빈 리스트를 자동으로 생성해주기 때문에 코드가 간단하고 직관적입니다.

defaultdict로 코드 가독성 향상하기

defaultdict를 사용하지 않는다면, 매번 키의 존재 여부를 확인하는 코드가 필요합니다. 아래는 defaultdict와 일반 딕셔너리를 비교한 예제입니다.

일반 딕셔너리 사용

fruit_dict = {}
fruits = ["apple", "banana", "apple"]

for fruit in fruits:
    if fruit not in fruit_dict:
        fruit_dict[fruit] = 0
    fruit_dict[fruit] += 1

print(fruit_dict)

defaultdict 사용

from collections import defaultdict

fruit_dict = defaultdict(int)
fruits = ["apple", "banana", "apple"]

for fruit in fruits:
    fruit_dict[fruit] += 1

print(fruit_dict)

설명

  • 일반 딕셔너리를 사용하면 if 문을 통해 키가 존재하는지 확인해야 하지만, defaultdict이 과정을 자동으로 처리하여 코드가 간결해집니다.
  • defaultdict자동 초기화 덕분에 가독성이 높은 코드를 작성할 수 있어, 특히 키가 많거나 동적으로 생성되는 데이터에서 편리합니다.

defaultdict로 코드 간소화 요약

Python의 defaultdict는 데이터 집계와 분류 작업에서 자동 초기화를 통해 코드를 단순하게 만듭니다. defaultdict를 사용하면 딕셔너리 키 존재 여부를 확인하는 복잡한 코드를 줄일 수 있어, Python 코드를 보다 효율적으로 작성할 수 있습니다.

defaultdict가 유용한 경우는 다음과 같습니다:

  1. 리스트나 튜플로 데이터를 분류할 때
  2. 숫자를 집계하거나 빈도를 카운트할 때
  3. 데이터의 기본값이 필요할 때 코드에서 불필요한 반복을 줄이기 위해

Python zipzip_longest로 두 리스트 병합하기: 짝 맞추기 대작전

데이터를 다루다 보면 두 리스트를 짝을 맞춰 병합해야 할 때가 많습니다. Python의 zipzip_longest는 이러한 작업을 간단하게 도와주는 유용한 함수입니다. zip은 두 리스트의 길이가 같을 때 주로 사용되며, zip_longest길이가 다른 리스트도 끝까지 짝을 맞출 수 있게 해줍니다.

이번 글에서는 zipzip_longest의 사용법과 실제 코딩에서 유용하게 사용할 수 있는 예제를 통해 이 함수들의 차이점을 이해해보겠습니다.


zip 기본 사용법: 두 리스트 병합하기

zip 함수는 두 개 이상의 리스트를 받아 각각의 요소를 짝지어 튜플로 묶은 새로운 리스트를 만듭니다. 두 리스트의 길이가 다를 경우, 짧은 리스트에 맞춰서 요소를 묶습니다.

list1 = [1, 2, 3]
list2 = ['a', 'b', 'c']

result = list(zip(list1, list2))
print(result)

결과

[(1, 'a'), (2, 'b'), (3, 'c')]

설명

  • zip(list1, list2)는 두 리스트의 요소를 순서대로 짝지어 [(1, 'a'), (2, 'b'), (3, 'c')]를 반환합니다.
  • zip가장 짧은 리스트를 기준으로 병합되므로, 짝이 맞지 않는 요소는 자동으로 제외됩니다.

실전 예제 1: 학생 성적 리스트 병합하기

두 리스트를 zip으로 병합하여 학생 이름과 성적을 쉽게 매칭할 수 있습니다. 이 방법을 통해 복잡한 데이터 정렬 없이 간단히 학생별 성적을 관리할 수 있습니다.

students = ["Alice", "Bob", "Charlie"]
scores = [85, 92, 78]

student_scores = dict(zip(students, scores))
print(student_scores)

결과

{'Alice': 85, 'Bob': 92, 'Charlie': 78}

설명

  • dict(zip(students, scores))를 통해 이름과 성적을 딕셔너리 형태로 병합할 수 있습니다.
  • zip을 사용하면 두 리스트를 한 번에 매칭하여 간단히 데이터를 정리할 수 있습니다.

zip_longest로 길이가 다른 리스트 병합하기

때로는 두 리스트의 길이가 맞지 않는 경우가 있습니다. 이때는 itertools 모듈의 zip_longest를 사용해 짧은 리스트의 부족한 부분을 특정 값으로 채워줍니다.

from itertools import zip_longest

list1 = [1, 2, 3]
list2 = ['a', 'b']

result = list(zip_longest(list1, list2, fillvalue='N/A'))
print(result)

결과

[(1, 'a'), (2, 'b'), (3, 'N/A')]

설명

  • zip_longest(list1, list2, fillvalue='N/A')는 짧은 리스트의 빈 자리를 'N/A'로 채우며 병합합니다.
  • 이 함수는 길이가 맞지 않는 리스트를 다룰 때 매우 유용합니다. 예를 들어, 일부 데이터가 없는 경우 기본값을 추가하여 처리할 수 있습니다.

실전 예제 2: 과목별 성적 정리하기

학생들이 수강한 과목이 달라서 리스트의 길이가 맞지 않을 때 zip_longest를 사용하면 쉽게 해결할 수 있습니다.

subjects = ["Math", "Science", "English"]
scores = [95, 88]  # English 성적이 없을 경우

subject_scores = dict(zip_longest(subjects, scores, fillvalue='Not Available'))
print(subject_scores)

결과

{'Math': 95, 'Science': 88, 'English': 'Not Available'}

설명

  • zip_longestEnglish 과목에 기본값을 추가하여 모든 과목을 포함한 성적 목록을 반환합니다.
  • fillvalue로 기본값을 지정해, 누락된 데이터 처리가 간단해집니다.

zipzip_longest의 차이점 정리

기능 zip zip_longest
기본 동작 가장 짧은 리스트를 기준으로 병합 가장 긴 리스트를 기준으로 병합
누락된 요소 처리 누락된 요소는 자동으로 제외 fillvalue로 지정한 값으로 채움
용도 길이가 같은 리스트 병합 시 유용 길이가 다른 리스트 병합 시 유용

사용 예시

  • zip: 데이터의 길이가 일치하는 경우, 짝을 맞춰 데이터 정리를 할 때 사용
  • zip_longest: 누락된 데이터를 기본값으로 채워야 할 때 유용함

실전 예제 3: 다수의 리스트 병합으로 데이터 정리하기

여러 개의 리스트가 있을 때도 zipzip_longest를 활용하면 데이터 정리를 쉽게 할 수 있습니다.

names = ["Alice", "Bob"]
scores = [85, 90]
grades = ["B", "A"]

# zip 사용
result_zip = list(zip(names, scores, grades))
print("Using zip:", result_zip)

# zip_longest 사용
result_zip_longest = list(zip_longest(names, scores, grades, fillvalue="N/A"))
print("Using zip_longest:", result_zip_longest)

결과

Using zip: [('Alice', 85, 'B'), ('Bob', 90, 'A')]
Using zip_longest: [('Alice', 85, 'B'), ('Bob', 90, 'A'), ('N/A', 'N/A', 'N/A')]

설명

  • zip은 길이가 맞는 요소만 병합하고, zip_longest누락된 요소를 기본값으로 채웁니다.
  • 여러 리스트를 한 번에 병합할 때도 zipzip_longest로 데이터를 손쉽게 정리할 수 있습니다.

요약: zipzip_longest로 효율적인 데이터 정리

Python의 zipzip_longest리스트를 짝지어 병합하고 데이터를 정리하는 데 매우 유용합니다. 두 리스트의 길이가 맞지 않을 때도 zip_longest를 사용해 기본값을 추가하면서 처리할 수 있어, 데이터 누락이 발생하지 않도록 안전하게 코딩할 수 있습니다.

다음과 같은 경우 zipzip_longest를 사용해보세요:

  1. 길이가 동일한 리스트 병합: zip을 통해 간단히 병합
  2. 길이가 다른 리스트 병합: zip_longest를 통해 누락된 부분에 기본값을 추가

Python에서 ***를 함께 사용하기: 위치 인자와 키워드 인자 활용법

Python에서 ***를 동시에 사용하면 함수 호출 시 다양한 형태의 인자를 효율적으로 전달할 수 있습니다. 특히 함수에서 필수 인자, 위치 기반 인자, 키워드 인자를 모두 받을 때 ***는 강력한 도구가 됩니다. 이 가이드에서는 *args**kwargs를 함께 사용할 때의 구조와 다양한 예제를 통해 그 활용 방법을 소개합니다.


1. *args**kwargs의 기본 사용 구조

함수 정의 시 *args**kwargs를 함께 사용하면, 위치 기반 인자와 키워드 인자를 동시에 받아 처리할 수 있습니다. 위치 인자를 먼저 받고, 그다음 키워드 인자를 받기 때문에 함수 매개변수는 필수 인자 > *args > `kwargs`** 순서로 작성해야 합니다.

def mixed_function(a, b, *args, **kwargs):
    print(f"Required: {a}, {b}")
    print(f"Args (additional positional): {args}")
    print(f"Kwargs (additional keyword): {kwargs}")

# 호출 예시
mixed_function(1, 2, 3, 4, x=10, y=20)

출력 결과

Required: 1, 2
Args (additional positional): (3, 4)
Kwargs (additional keyword): {'x': 10, 'y': 20}

설명

  • ab는 필수 인자로, 반드시 값을 전달해야 합니다.
  • *args는 위치 인자 34를 튜플로 묶어 받습니다.
  • **kwargs는 키워드 인자 x=10y=20을 딕셔너리로 묶어 받습니다.
  • 이 구조 덕분에 함수 호출이 더욱 유연해지고, 필요에 따라 다양한 형태의 인자를 전달할 수 있습니다.

2. ***를 사용하여 동적으로 여러 인자 전달하기

***는 함수 호출 시에도 사용할 수 있어, 리스트와 딕셔너리를 동시에 언패킹하여 인자로 전달할 수 있습니다. 이는 복잡한 인자 전달을 한 줄로 간단히 표현할 수 있게 해줍니다.

def order_summary(main, side, drink, *args, **kwargs):
    print(f"Main: {main}, Side: {side}, Drink: {drink}")
    print(f"Additional toppings: {args}")
    print(f"Additional options: {kwargs}")

# 인자 설정
order_items = ["Burger", "Fries", "Cola"]
order_options = {"size": "Large", "extra_cheese": True}

# 리스트와 딕셔너리 동시 언패킹
order_summary(*order_items, "Bacon", "Onion", **order_options)

출력 결과

Main: Burger, Side: Fries, Drink: Cola
Additional toppings: ('Bacon', 'Onion')
Additional options: {'size': 'Large', 'extra_cheese': True}

설명

  • *order_items는 리스트 요소를 언패킹하여 main, side, drink 인자로 전달합니다.
  • "Bacon""Onion"*args로 전달되어 추가 토핑으로 분류됩니다.
  • **order_options는 딕셔너리를 언패킹하여 키워드 인자로 전달되므로, sizeextra_cheese 옵션이 적용됩니다.
  • ***를 함께 사용하면, 리스트와 딕셔너리를 필요한 위치에 맞춰 유연하게 전달할 수 있어 코드가 더욱 깔끔해집니다.

3. 클래스 초기화에서 *args**kwargs로 인자 관리하기

클래스 초기화 시 *args**kwargs를 함께 사용하면, 필수 속성 외의 추가 속성을 유연하게 받을 수 있어 클래스를 재사용하기 쉬워집니다.

class Product:
    def __init__(self, name, price, *args, **kwargs):
        self.name = name
        self.price = price
        self.features = args
        self.options = kwargs

    def show_info(self):
        print(f"Product Name: {self.name}")
        print(f"Price: {self.price}")
        print(f"Features: {self.features}")
        print(f"Options: {self.options}")

# 인스턴스 생성
item = Product("Laptop", 1200, "16GB RAM", "512GB SSD", color="Silver", warranty="2 years")
item.show_info()

출력 결과

Product Name: Laptop
Price: 1200
Features: ('16GB RAM', '512GB SSD')
Options: {'color': 'Silver', 'warranty': '2 years'}

설명

  • nameprice는 필수 속성으로 받습니다.
  • *args는 추가적인 기능 정보를 features 튜플로 저장합니다.
  • **kwargs는 옵션을 딕셔너리 형태로 받아 options에 저장합니다.
  • 기본 속성 외에도 추가 속성을 유연하게 받아 클래스를 다목적으로 활용할 수 있습니다.

4. 딕셔너리와 리스트 결합하기

*args**kwargs를 함께 사용하면, 딕셔너리와 리스트를 조합해 다양한 인자를 한 번에 처리할 수 있습니다. 이는 특히 설정값이나 다양한 옵션을 한꺼번에 전달할 때 유용합니다.

def configure_settings(*args, **kwargs):
    print(f"General Settings: {args}")
    print(f"Advanced Options: {kwargs}")

# 리스트와 딕셔너리 조합
general_settings = ["Dark Mode", "1080p"]
advanced_options = {"volume": 80, "brightness": 70}

configure_settings(*general_settings, **advanced_options)

출력 결과

General Settings: ('Dark Mode', '1080p')
Advanced Options: {'volume': 80, 'brightness': 70}

설명

  • *general_settings는 리스트를 언패킹하여 args로 전달합니다.
  • **advanced_options는 딕셔너리를 언패킹하여 kwargs로 전달합니다.
  • 이 방식으로 설정값과 고급 옵션을 깔끔하고 효율적으로 전달할 수 있습니다.

요약: ***를 함께 사용하는 이유와 장점

Python에서 *args**kwargs를 함께 사용하면, 함수나 클래스에서 위치 인자와 키워드 인자를 동시에 유연하게 처리할 수 있습니다. ***를 결합해 사용하면 코드의 가독성과 재사용성이 높아지며, 반복적이고 복잡했던 인자 전달 방식이 단순해집니다.

***의 동시 사용은 특히 다양한 인자 구성이 필요한 상황에서 매우 유용합니다. Python을 더욱 유연하고 효율적으로 사용하고 싶다면 이 문법을 익혀보세요!

+ Recent posts