상세 컨텐츠

본문 제목

dict에 대한 기초 지식

Python

by techbard 2024. 11. 6. 07:41

본문

반응형

 

특성

1. Key - Value 쌍의 구조를 가진 컨테이너이다.
2. 다른 프로그래밍 언어에서는 연관 배열(associative array)이나 해쉬테이블(hashtable)로 불린다.
3. 매우 자주 사용한다.
4. 순서가 고정되어 있다고 가정하면 안된다.

 

## dict 생성 방법들

d1 = {}
print(f'd1 => {type(d1)}')
print('=' * 30)

d2 = {'x':10, 'y':20}
print(f'd2 => {d2}')
print('=' * 30)

d3 = dict(x=10, y=20)
print(f'd3 => {d3}')
print('=' * 30)

d4 = {k : 0 for k in ('x', 'y')} # dict comprehension
print(f'd4 => {d4}')
print('=' * 30)

d5 = dict(zip(['x', 'y'], [10, 20]))
print(f'd5 => {d5}')
print('=' * 30)

d6 = dict([('x', 10), ('y', 20)])
print(f'd6 => {d6}')
print('=' * 30)

d7 = dict.fromkeys(['x', 'y'], 0)
print(f'd7 => {d7}')
print('=' * 30)

# 결과
d1 => <class 'dict'>
==============================
d2 => {'x': 10, 'y': 20}
==============================
d3 => {'x': 10, 'y': 20}
==============================
d4 => {'x': 0, 'y': 0}
==============================
d5 => {'x': 10, 'y': 20}
==============================
d6 => {'x': 10, 'y': 20}
==============================
d7 => {'x': 0, 'y': 0}
==============================

 

# 숫자가 키인 dict의 생성

fs = ['apple', 'banana', 'cherry']

df1 = dict(zip(range(len(fs)), fs))
print(f'df1 => {df1}')

print('*' * 40)

df2 = {}
for i, f in enumerate(fs):
    df2[i] = f
print(f'df2 => {df2}')

print('*' * 40)

df3 = {i : f for i, f in enumerate(fs)}
print(f'df3 => {df3}')

# 결과
df1 => {0: 'apple', 1: 'banana', 2: 'cherry'}
****************************************
df2 => {0: 'apple', 1: 'banana', 2: 'cherry'}
****************************************
df3 => {0: 'apple', 1: 'banana', 2: 'cherry'}

 

# dict 키-값의 조작

k = ['x', 'y']
v = [0, 0, 0]

d1 = dict(zip(k, v))
d1.setdefault('z', 0) # 키-값 쌍 추가
print(f'setdefault -> {d1}')
print('*' * 40)

d2 = dict(zip(k, v))
d2.update(x=1) # 키의 값 수정, 키가 없으면 키-값 쌍 추가
print(f'update -> {d2}')
print('*' * 40)

d3 = dict(zip(k, v))
d3.pop('y')
print(f'pop -> {d3}')
print('*' * 40)

d4 = dict(zip(k, v))
d4.update(x=100)
value = d4.get('x', 0)
print(f'get -> value of key "x": {value}')
print('*' * 40)

d5 = dict(zip(k, v))
d5['x'] = 99
print(f'assign -> {d5['x']}')
print('*' * 40)

# 결과
setdefault -> {'x': 0, 'y': 0, 'z': 0}
****************************************
update -> {'x': 1, 'y': 0}
****************************************
pop -> {'x': 0}
****************************************
get -> value of key "x": 100
****************************************
assign -> 99
****************************************

 

### dict 요소 조작 #2

def main():
    items = ['apple', 'banana']
    # 숫자를 dict key로 쓰기 위한 방법
    d = dict(zip(range(len(items)), items))

    d[len(items)] = 'strawberry' # 배열 인덱스로 dict 추가

    dt = {len(d):'lime'} # 임시 dict 생성
    d.update(dt) # 기존 dict에 임시 dict 추가

    print(d)

    print('\t')

    # Python 3.9에 추가된 dict merge, update
    # dict union 연산자

    d0 = {0:'apple'}
    d1 = {1:'peach'}

    ds01 = d0 | d1
    print('d0 | d1 ->', ds01)

    # 머지 순서 지켜짐 (Python 3.7 이상부터)
    ds10 = d1 | d0
    print('d1 | d0 ->', ds10)

    print('\t')

    # dict update 연산자

    d3 = {3:'pineapple'}
    d4 = {3:'tomato'}

    d3 |= d4
    print(d3)

    print('\t')

    d3 |= {3:'grape'}
    print(d3)

    pass

if __name__ == '__main__':
    main()
 
 #결과
 
 {0: 'apple', 1: 'banana', 2: 'strawberry', 3: 'lime'}
	
d0 | d1 -> {0: 'apple', 1: 'peach'}
d1 | d0 -> {1: 'peach', 0: 'apple'}
	
{3: 'tomato'}
	
{3: 'grape'}

 

### dict 언패킹

def print_info(name, age, address):
    print('이름', name)
    print('나이', age)
    print('주소', address)

def main():
    d = {'name': '고길동', 'age': 45, 'address':
        '서울특별시 도봉구 쌍문동 2-2'}
    print(*d)

    print('\t')

    print_info(**d)

if __name__ == '__main__':
    main()

#결과
name age address
	
이름 고길동
나이 45
주소 서울특별시 도봉구 쌍문동 2-2

 

# dict에서 key와 value로 조회
birth_years = {1994: 'bill', 1969: 'emily', 1982: 'elizabeth', 2000: 'turner'}

print(f'bitrh_years: {birth_years.items()}')
print(f'key: elizabeth exist in birth_years?', end=' ') 
print('elizabeth' in birth_years)

print(f'value: elizabeth exist in birth_years.values()?', end=' ') 
print('elizabeth' in birth_years.values())

print(len(birth_years))

# 결과
bitrh_years: dict_items([(1994, 'bill'), (1969, 'emily'), (1982, 'elizabeth'), (2000, 'turner')])
key: elizabeth exist in birth_years? False
value: elizabeth exist in birth_years.values()? True
4

 

# dict의 shallow copy
# 단, 중첩 dict의 경우는 복사가 아닌 참조가 된다.
id_value = {1: '1', 2: '1', 3: '1'}
another_id_value = id_value.copy()
another_id_value[1] = '0'
another_id_value[2] = '0'
another_id_value[3] = '0'

print(f'id_value: {id_value}')
print(f'copy & modify another_id_value: {another_id_value}')

# 결과
id_value: {1: '1', 2: '1', 3: '1'}
copy & modify another_id_value: {1: '0', 2: '0', 3: '0'}

 

# dict setdefault 메서드
# 키가 없어도 에러를 내지않고 키-밸류 생성
# 이미 키가 있으면 현재 밸류 유지
computers = {'Lenovo': 'ThinkPad', 'Microsoft': 'Surface Pro'}
computers.setdefault('Apple', 'MacBook')
print(f'first setdefault of MB: {computers['Apple']}')

computers.setdefault('Apple', 'MacBook Pro')
print(f'second setdefault to MB Pro: {computers['Apple']}')

# 결과
first setdefault of MB: MacBook
second setdefault to MB Pro: MacBook

 

  • 예제
    • 컨테이너
# dict의 생성 #1d = dict()print(type(d))
결과)<class 'dict'>

# dict의 생성 #2

d = {}print(type(d))
결과)<class 'dict'>
# dictd의 생성 #3

dict = {}

dict['apple'] = 1

dict['lemon'] = 2

dict['peach'] = 3

 

print(dict)

 

결과)

{'apple': 1, 'lemon': 2, 'peach': 3}


# dictd의 생성 #4

d = dict(a = 'a', b = 'b')

 

print(d['a'])
결과)a

 

# dict는 고정된 순서를 보장하지 않는다. (실행할 때마다 순서는 바뀐다.)chars = {'name':'mike', 'age': '24', 'address':'san diego'}print(chars)
결과){'age': '24', 'name': 'mike', 'address': 'san diego'}
chars = {'name':'mike', 'age': '24', 'address':'san diego'}print(chars)
결과){'name': 'mike', 'age': '24', 'address': 'san diego'}
# dict() 함수를 이용해서 생성할 때는 key = 형식으로 인자를 준다.chars = dict(name = 'mike', age = '24', address = 'san diego')

print(chars)

 

결과){'age': '24', 'name': 'mike', 'address': 'san diego'}
# 변수명으로 다른 사전 요소 추가d = dict( three = 3, four = 4)dx = dict( one = 1, two = 2, **d )

 

print( dx )
결과){'three': 3, 'one': 1, 'four': 4, 'two': 2}
  • 요소 접근

# dict에서는 인덱스로 요소를 참조할 수 없다.

chars = dict(name = 'mike', age = '24', address = 'san diego')print(chars[0])
결과)KeyError: 0
# in 내장 키워드로 Key가 존재하는지 확인하기chars = dict(name = 'mike', age = '24', address = 'san diego')if 'name' in chars:print(chars['name'])
결과)mike
# 순회해서 Key 가져오고, Key를 통해 Value 가져오기chars = dict(name = 'mike', age = '24', address = 'san diego')for key in chars:print('key: ' + key, '| value: ' + chars[key])
결과)key: address | value: san diegokey: name | value: mikekey: age | value: 24
# items() 메쏘드를 이용해 순회하기chars = dict(name = 'mike', age = '24', address = 'san diego')for key, value in chars.items():print('key: ' + key, '| value: ' + value)
결과)key: name | value: mikekey: address | value: san diegokey: age | value: 24
# 또 다른 value 읽기 메쏘드 (키를 찾지 못해도 exception을 발생시키지 않는다.)d = dict(one = 1, two = 2)

 

 

print(d.get('one', 'default'))
결과)1
d = dict(two = 2)

 

print(d.get('one', 'default'))
결과)default
# dict 요소의 삭제d = dict( one = 1, two = 2 )
del d['one']print(d)
d.pop('two')

 

print(d)
결과){'two': 2}

 

{}
  • 언팩킹 된 사전을 다시 사전으로 묶기

# Key, Value를 가지고 사전으로 묶기

chars = dict(name = 'mike', age = '24', address = 'san diego')key = chars.keys()value = chars.values()chars_dupe = zip(key, value)print(dict(chars_dupe))
결과){'address': 'san diego', 'name': 'mike', 'age': '24'}
  • 사전 업데이트 하기
# 항목에 assign을 하는 경우 혼동을 방지하기 위해 update() 메쏘드를 제공하는 것으로 보임d = {    'name': 'mike',    'age': 33,    'address': 'LA'}

 

d.update({'name': 'mike', 'age': 33})print(d)

 

결과){'name': 'mike', 'address': 'LA', 'age': 33}

 

d.update({'name': 'mike', 'age': 34, 'address': 'LA'})print(d)
결과){'address': 'LA', 'age': 34, 'name': 'mike'}

 

### Dict 컴프리헨션

def main():
    f = ['apple', 'banana', 'lemon']
    c = [1, 2, 3]

    d = dict(zip(f, c)) ; print('d         ->', d)
    d1 = {f:c for f, c in d.items()} ; print('d1        ->', d1)
    d2 = {f:c + 1 for f, c in d.items()} ; print('d2 (d1+1) ->', d2)
    d3 = {f:c for f, c in d.items() if c == 3} ; print('d3 (c==3) ->', d3)
    d4 = {f:c for f, c in d.items() if f.startswith('a')} ; print('d4 (a***) ->', d4)

    pass

if __name__ == '__main__':
    main()

# 결과
d         -> {'apple': 1, 'banana': 2, 'lemon': 3}
d1        -> {'apple': 1, 'banana': 2, 'lemon': 3}
d2 (d1+1) -> {'apple': 2, 'banana': 3, 'lemon': 4}
d3 (c==3) -> {'lemon': 3}
d4 (a***) -> {'apple': 1}

 

 

 

 

 

  • 사전과 함수 인자를 이용한 트릭
def display(name, address):    print('My name is', name + '.')    print('I live in', address + '.')
d = {    'name': 'mike',    'address': 'LA'}
print(display(**d))
결과)My name is mike.I live in LA.
  • 소팅된 사전 얻기

# 내장함수 sorted()를 이용해 key로 소팅된 사전을 얻을 수 있다.

d = dict(one=1, two=2)


for k in sorted(d.keys()):    print(k, d[k])
결과)one 1

 

two 2
# value로 소팅된 사전 얻기d = dict(one=2, two=1)
for k in sorted(d, key=lambda x: d[x]):

 

    print(k, d[k])
결과)two 1

 

one 2

# dict.__missing__ 서브 클래싱

 

from collections import Iterable

 

class range_dict(dict):

def __missing__(self, key):

for k, v in self.items():

if isinstance(k, Iterable):

left, right = k

if left <= key < right:

return v

raise KeyError("cannot find {} in range_dict".format(key))

 

codes = range_dict({

(0, 10): 'red',

(10, 100): 'yellow',

(100, 1000): 'green'

})

 

print(codes[99])

 

# 결과

yellow

 

# dict.__missing__ 을 이용한 passthru

 

class passthrudict(dict):

def __missing__(self, key):

return key

 

censor = passthrudict({

'hell': 'h***',

'darn': 'd*rn'

})

 

sentence = "That darn cat!"

 

print(' '.join(censor[w] for w in sentence.split()))

 

# 결과

That d*rn cat!

 

# dict를 이용한 count

 

ls = list(range(100))

 

def concordance(list):

freq = {}

 

for l in list:

if l not in freq:

freq[l] = 0

freq[l] += 1

return freq

 

print(concordance(ls))

 

# 결과

{0: 1, 1: 1, 2: 1, 3: 1, 4: 1, 5: 1, 6: 1, 7: 1, 8: 1, 9: 1, 10: 1, 11: 1, 12: 1, 13: 1, 14: 1, 15: 1, 16: 1, 17: 1, 18: 1, 19: 1, 20: 1, 21: 1, 22: 1, 23: 1, 24: 1, 25: 1, 26: 1, 27: 1, 28: 1, 29: 1, 30: 1, 31: 1, 32: 1, 33: 1, 34: 1, 35: 1, 36: 1, 37: 1, 38: 1, 39: 1, 40: 1, 41: 1, 42: 1, 43: 1, 44: 1, 45: 1, 46: 1, 47: 1, 48: 1, 49: 1, 50: 1, 51: 1, 52: 1, 53: 1, 54: 1, 55: 1, 56: 1, 57: 1, 58: 1, 59: 1, 60: 1, 61: 1, 62: 1, 63: 1, 64: 1, 65: 1, 66: 1, 67: 1, 68: 1, 69: 1, 70: 1, 71: 1, 72: 1, 73: 1, 74: 1, 75: 1, 76: 1, 77: 1, 78: 1, 79: 1, 80: 1, 81: 1, 82: 1, 83: 1, 84: 1, 85: 1, 86: 1, 87: 1, 88: 1, 89: 1, 90: 1, 91: 1, 92: 1, 93: 1, 94: 1, 95: 1, 96: 1, 97: 1, 98: 1, 99: 1}

 

# collections - Counter

 

from collections import Counter

 

ls = list(range(100))

 

print(Counter(ls))

 

# 결과

Counter({0: 1, 1: 1, 2: 1, 3: 1, 4: 1, 5: 1, 6: 1, 7: 1, 8: 1, 9: 1, 10: 1, 11: 1, 12: 1, 13: 1, 14: 1, 15: 1, 16: 1, 17: 1, 18: 1, 19: 1, 20: 1, 21: 1, 22: 1, 23: 1, 24: 1, 25: 1, 26: 1, 27: 1, 28: 1, 29: 1, 30: 1, 31: 1, 32: 1, 33: 1, 34: 1, 35: 1, 36: 1, 37: 1, 38: 1, 39: 1, 40: 1, 41: 1, 42: 1, 43: 1, 44: 1, 45: 1, 46: 1, 47: 1, 48: 1, 49: 1, 50: 1, 51: 1, 52: 1, 53: 1, 54: 1, 55: 1, 56: 1, 57: 1, 58: 1, 59: 1, 60: 1, 61: 1, 62: 1, 63: 1, 64: 1, 65: 1, 66: 1, 67: 1, 68: 1, 69: 1, 70: 1, 71: 1, 72: 1, 73: 1, 74: 1, 75: 1, 76: 1, 77: 1, 78: 1, 79: 1, 80: 1, 81: 1, 82: 1, 83: 1, 84: 1, 85: 1, 86: 1, 87: 1, 88: 1, 89: 1, 90: 1, 91: 1, 92: 1, 93: 1, 94: 1, 95: 1, 96: 1, 97: 1, 98: 1, 99: 1})

 

##table에 존재하는 문자열 패턴이 존재하는 것만 골라냄
##주의: dict.fromkeys 메서드가 원하는 결과가 나오지 않음
##https://ericssblog.wordpress.com/2017/11/04/dict-fromkeys-is-dangerous/

lookup_list = ['111', '010', '110']
match_list = ['1110000', '0010010', '010101101', '111110010']


def lookup_match(lookup, match):
    res_dict = {k : [] for k in lookup} # !!!
    for l in lookup:
        for m in match_list:
            if l in m:
                res_dict[l].append(m)
    return res_dict

ret = lookup_match(lookup_list, match_list)
print(ret)

# 결과
{'111': ['1110000', '111110010'], '010': ['0010010', '010101101', '111110010'], '110': ['1110000', '010101101', '111110010']}

## 이상한 dict.fromkeys()

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

weird_d = dict.fromkeys(key_list, [])
print(f'init dict: {weird_d}')
print('*' * 50)
weird_d['a'].append(0)
print(f'after list appended: {weird_d}')
print('*' * 50)
weird_d['a'] = 1
print(f'after value assigned: {weird_d}')

# 결과
init dict: {'a': [], 'b': [], 'c': []}
**************************************************
after list appended: {'a': [0], 'b': [0], 'c': [0]}
**************************************************
after value assigned: {'a': 1, 'b': [0], 'c': [0]}

## 위의 결과를 보면 아주 이상하다.
## dict의 value에 list를 생성했는데, 그 list는 다 연결되어 있는 듯이
## 하나의 키에 value를 넣으면 모든 키 value에 같은 값이 들어간다.
## 위의 블로그에 소개 되어 있듯이 이뮤터블이 아닌 뮤터블을 value에 넣으면
## 모든 뮤터블이 공용처럼 작동한다.
## 블로그 저자는 그래서 fromkeys를 쓰지말고 dict 컴프리핸션을 쓰라고 권장한다.
## 이런 버그 아닌 스펙 아닌 현상 때문에 거의 2시간을 날렸다.
반응형

관련글 더보기

댓글 영역