TIL #25 : [Python] 딕셔너리 순회하기 + For Loops

셀레스틴 허·2020년 12월 29일
2
post-thumbnail

딕셔너리 순회

For Loop으로 딕셔너리 수정

  1. value 수정
>>> prices = {'apple' : 0.40, 'orange' : 0.35, 'banana': 0.25}
>>> for k, v in prices.items():
...		prices[k] = round(v * 0.9, 2) # 10% 할인 적용
...
>>>	prices 
# {'apple': 0.36, 'orange': 0.32, 'banana': 0.23}

직접적으로 수정하지 않고 원본 딕셔너리와 함께(prices[k] 이처럼) 쓰는 이유는:
k와 v의 변경사항은 원본 딕셔너리에 반영되지 않기 때문이다. 직접적으로 루프 내에서 k 또는 v 중 하나를 수정하면, 원본 딕셔너리에는 아무것도 변경되지 않고 원본 딕셔너리와의 연결마저 잃는다.

  1. key 수정
>>> prices = {'apple': 0.40, 'orange': 0.35, 'banana': 0.25}
>>> for key in list(prices.keys()):  # view가 아닌 list로 바꾸기
...     if key == 'orange':
...         del prices[key]  # prices에서 key 지우기
...
>>> prices
# {'apple': 0.4, 'banana': 0.25}

이렇게 코드를 작성하면 전체 list를 순회하기 때문에 메모리를 생각하면 적합한 방식이 아니다. list로 변환하지 않고.keys()만 사용해서 key를 제거하려고 할 경우 RuntimeError가 뜬다. RuntimeError가 뜨는 이유는 .keys()는 dictionary-view 객체를 반환하는데 dictionary-view 객체는 한번에 하나씩 키를 추출하기 때문이다. 그러나 순회 중 del prices[key]와 같은 딕셔너리를 수정하는 행위는 RuntimeError를 raise하기 때문이다.


실무 문제

  1. key를 value로, value를 key로 교체

기본문법: 변수[key] = value

>>> a_dict = {'one': 1, 'two': 2, 'three': 3, 'four': 4}
>>> new_dict = {}
>>> for key, value in a_dict.items():
...     new_dict[value] = key # value와 key를 바꿔서 값을 교체하기 
...
>>> new_dict
{1: 'one', 2: 'two', 3: 'three', 4: 'four'}

** 이 방법이 통하려면 원본 data가 hashable한 data type이어야 한다.

  1. items 필터

기존 딕셔너리에서 조건과 맞는 요소만 골라서 새로운 딕셔너리를 만들고 싶을 때 쓰는 방법이다.

>>> a_dict = {'one': 1, 'two': 2, 'thee': 3, 'four': 4}
>>> new_dict = {}  # Create a new empty dictionary
>>> for key, value in a_dict.items():
...     # 이 조건과 맞는 value는 new_dict 안에 넣어라
...     if value <= 2:
...         new_dict[key] = value
...
>>> new_dict
{'one': 1, 'two': 2}
  1. 연산
>>> incomes = {'apple': 5600.00, 'orange': 3500.00, 'banana': 5000.00}
>>> total_income = 0.00
>>> for value in incomes.values():
...     total_income += value  # Accumulate the values in total_income
...
>>> total_income
14100.0

딕셔너리 comprehensions

아래 예제는 2개의 리스트를 새로운 딕셔너리로 반환한다. 이 경우에는 zip(*iterables)를 이용해 요소를 쌍으로 순회할 수 있다.

>>> objects = ['blue', 'apple', 'dog']
>>> categories = ['color', 'fruit', 'pet']
>>> a_dict = {key: value for key, value in zip(categories, objects)}
>>> a_dict
{'color': 'blue', 'fruit': 'apple', 'pet': 'dog'}
  1. key를 value로, value를 key로 교체 (feat. 딕셔너리 comprehensions)

아까 key를 value로, value를 key로 교체했던 방식도 딕셔너리 comprehension을 통해 더 Pythonic하게 풀 수 있다.

>>> a_dict = {'one': 1, 'two': 2, 'thee': 3, 'four': 4}
>>> new_dict = {value: key for key, value in a_dict.items()}
>>> new_dict
{1: 'one', 2: 'two', 3: 'thee', 4: 'four'}

새로운 딕셔너리 new_dict를 key와 value가 교체된 형태로 만들었다.

  1. items 필터(feat. 딕셔너리 comprehensions)

if 조건문을 써서 원하는 조건을 표기하면 된다.

>>> a_dict = {'one': 1, 'two': 2, 'thee': 3, 'four': 4}
>>> new_dict = {k: v for k, v in a_dict.items() if v <= 2}
>>> new_dict
{'one': 1, 'two': 2}
  1. 연산(feat. list comprehensions)

list comprehension을 활용해 딕셔너리 value를 순회하고 sum() 메서드로 total_income를 만들 수 있다.

>>> incomes = {'apple': 5600.00, 'orange': 3500.00, 'banana': 5000.00}
>>> total_income = sum([value for value in incomes.values()])
>>> total_income
14100.0
  1. 큰 딕셔너리로 작업하는 경우(feat. 메모리)

큰 딕셔너리로 작업중이며 메모리가 중요한 문제일 경우 generator expression을 쓸 수 있다. generator expression은 iterator를 반환하며 list comprehension과 형식이 같아 보이지만 ()를 사용해서 정의한다. 리스트를 만들어서 메모리에 다 보관하지 않고 그때 그때 필요에 의해 요소를 추출하기 때문에 훨씬 메모리 사용에 효율적이다.

>>> total_income = sum(value for value in incomes.values())
>>> total_income
14100.0

또는 그냥 sum()메서드를 쓰는 것도 방법이다.

>>> total_income = sum(incomes.values())
>>> total_income
14100.0
  1. 특정 item 제거

key-view 객체는 일반 set 작업을 지원하므로 이를 활용해 특정 item을 딕셔너리에서 지울 수 있다.

아래 예제는 set difference 작업을 진행했으며, set로 변환할 필요 없이 key-view 객체를 바로 사용할 수 있다.

>>> incomes = {'apple': 5600.00, 'orange': 3500.00, 'banana': 5000.00}
>>> non_citric = {k: incomes[k] for k in incomes.keys() - {'orange'}}
>>> non_citric
{'apple': 5600.0, 'banana': 5000.0}
  1. 딕셔너리 sorting

sorted()와 딕셔너리 comprehension을 이용해 딕셔너리 속 item들을 sort할 수 있다. sorted(incomes)는 새로운 딕셔너리를 생성하는데 사용할 수 있는 sort된 key 리스트를 반환한다.

>>> incomes = {'apple': 5600.00, 'orange': 3500.00, 'banana': 5000.00}
>>> sorted_income = {k: incomes[k] for k in sorted(incomes)}
>>> sorted_income
{'apple': 5600.0, 'banana': 5000.0, 'orange': 3500.0}

sorted order로 딕셔너리 순회

  1. keys로 sort

for key in sorted(iterable) , 또는 for key in sorted(iterable.keys()) 형식을 활용한다. 두가지 방법 모두 정렬된 key 리스트를 반환한다.

  1. values로 sort

딕셔너리 item을 value로 sort할 때 각 item의 value를 반환하는 함수를 만들어서, 이 함수를 sorted()의 키워드 인자로 설정한다.

>>> incomes = {'apple': 5600.00, 'orange': 3500.00, 'banana': 5000.00}
>>> def by_value(item):
...     return item[1]
...
>>> for k, v in sorted(incomes.items(), key=by_value):
...     print(k, '->', v)
...
('orange', '->', 3500.0)
('banana', '->', 5000.0)
('apple', '->', 5600.0)

키 없이 value만 sort하기:

>>> for value in sorted(incomes.values()):
...     print(value)
...
3500.0
5000.0
5600.0
  1. 반대로 sort
    sorted()의 키워드 인자로reverse=True를 추가한다. reverse는 불인 value만 받으며 True로 설정할시 요소들이 reversed 순서로 정렬된다.
>>> incomes = {'apple': 5600.00, 'orange': 3500.00, 'banana': 5000.00}
>>> for key in sorted(incomes):
...     print(key, '->', incomes[key])
...
apple -> 5600.0
banana -> 5000.0
orange -> 3500.0

** 여기서 중요한 포인트는 sorted()는 기존 딕셔너리를 수정하지 않으며 새로운 독립적인 리스트를 반환한다. 즉 기존 딕셔너리는 그대로며 정렬되거나 바뀌지 않는다.


.popitem()으로 제거

순회를 돌며 순차적으로 딕셔너리 속 item 제거하기.

a_dict = {'color': 'blue', 'fruit': 'apple', 'pet': 'dog'}

while True:
    try:
        print(f'Dictionary length: {len(a_dict)}')
        item = a_dict.popitem()
        # item 가지고 뭘 하기
        print(f'{item} removed')
    except KeyError:
        print('The dictionary has no item now...')
        break

파이썬 내장 함수 이용

  1. map()

기본 문법: map(function, iterable, ...)

>>> prices = {'apple': 0.40, 'orange': 0.35, 'banana': 0.25}
>>> def discount(current_price):
...     return (current_price[0], round(current_price[1] * 0.95, 2))
...
>>> new_prices = dict(map(discount, prices.items()))
>>> new_prices
{'apple': 0.38, 'orange': 0.33, 'banana': 0.24}

dicount()는 튜플을 반환하며 current_price[0]가 key를, (current_price[1] * 0.95, 2)가 새로운 value를 나타낸다.

  1. filter()

기본 문법: filter(function, iterable)

>>> prices = {'apple': 0.40, 'orange': 0.35, 'banana': 0.25}
>>> def has_low_price(price):
...     return prices[price] < 0.4
...
>>> low_price = list(filter(has_low_price, prices.keys()))
>>> low_price
['orange', 'banana']

For Loop

  1. 전통 For Loop
a = [10, 83, 90, 28]

for i in range(len(a)):
	print(data[i]) # 10, 83, 90, 28

** 이 방법은 딕셔너리에 적용 안된다.

  1. 일반 방법
data = {a : 10, b : 83, c : 90, d : 28}

for x in data:
	print(x) # a, b, c, d

key가 하니씩 출력된다. 이 때 x[data]를 이용해서 value 값도 가져올 수 있다.

  1. .keys() 사용
data = {a : 10, b : 83, c : 90, d : 28}

for x in data.keys():
	print(x) # a, b, c, d
 
  1. values() 사용
data = {a : 10, b : 83, c : 90, d : 28}

for x in data.values():
	print(x) # 10, 83, 90, 28
  1. items() 사용

tuple로 변환되므로 2가지 변수를 지정한다. 첫번째 변수에는 key, 두번째 변수에는 value가 들어가게 설정한다.

data = {a : 10, b : 83, c : 90, d : 28}

for x in data.items():
	print(x) # (a, 10) (b, 83) (c, 90) (d, 28)
    
for x, y in data.items():
	print(x, y) 
    # a	10 
      b 83	
      c 90
      d 28

※ [딕셔너리 순회] 예제는 모두 realpython.com에서 복사했습니다.

Reference
https://medium.com/better-programming/five-ways-to-loop-through-python-dictionaries-468db779744d
https://realpython.com/iterate-through-dictionary-python/

profile
Software Developer / 고통은 필연, 괴로움은 선택