groupby가 반환하는 각 그룹은 독립 목록이 아니라 같은 원본 반복자를 공유하는 iterator입니다. 바깥 groupby가 다음 그룹으로 진행하면 이전 그룹에서 아직 읽지 않은 값은 내부적으로 소비되어 이전 iterator로 다시 가져올 수 없습니다.

나중에도 그룹 값이 필요하면 바깥 반복을 다음 그룹으로 넘기기 전에 list(group)로 바로 저장합니다.

그룹을 얻었다고 내용이 자동 저장되지는 않습니다

itertools.groupby는 (key, group) 쌍을 하나씩 내놓습니다. 여기서 group은 리스트가 아니라 한 번씩 값을 꺼내는 iterator입니다. Python 3.12 공식 문서는 각 그룹이 원래 iterable을 바깥 groupby와 공유한다고 설명합니다. 바깥 반복이 다음 키로 이동하려면 현재 그룹의 남은 항목도 지나가야 하므로, 이전 그룹 iterator를 따로 보관해도 독립된 값 사본이 생기지 않습니다. 출력이나 합계를 그 자리에서 처리하면 괜찮지만, 나중에 다시 읽으려는 경우에는 보관 방식을 먼저 정해야 합니다.

그룹 iterator만 모으면 나중에 비어 보일 수 있습니다

입력이 A, A, B 순서라고 하겠습니다. 아래 코드는 groupby의 쌍을 목록에 담아 둡니다. 바깥 반복이 끝나는 동안 그룹들은 공유 입력의 다음 구간으로 차례로 진행합니다. 그 뒤 각 그룹 iterator를 읽으면 이미 소비된 값이므로 빈 목록으로 나옵니다. 반면 (key, list(group))를 바깥 반복 안에서 만들면 현재 그룹의 값을 그 자리에서 새 목록으로 복사해 두므로 이후에도 내용을 읽을 수 있습니다.

Python 3.12에서 그룹 iterator를 늦게 읽는 경우와 즉시 복사하는 경우를 비교합니다.
from itertools import groupby

rows = [("A", 1), ("A", 2), ("B", 3)]
late = list(groupby(rows, key=lambda row: row[0]))
print([(key, list(group)) for key, group in late])

saved = [(key, list(group))
         for key, group in groupby(rows, key=lambda row: row[0])]
print(saved)
그룹의 실제 값이 필요할 때는 소비 시점을 앞당깁니다
작성 방식값을 꺼내는 때나중에 보이는 결과
late에 그룹 iterator만 저장바깥 groupby가 끝난 뒤공유 입력이 이미 진행되어 그룹이 비어 있을 수 있음
(key, list(group))로 저장현재 그룹을 받은 즉시목록에 요소가 보존됨
현재 그룹에서 합계·출력바깥 반복 중별도 저장 없이 필요한 결과 계산

결과를 나중에 다시 읽으려는지, 현재 한 번만 처리하면 되는지에 따라 방법을 선택하세요. 예를 들어 그룹별 개수만 필요하다면 각 그룹 안에서 sum(1 for _ in group)처럼 바로 계산할 수 있습니다. 원본 요소를 보관해야 한다면 list(group)로 복사합니다. 그룹이 매우 크면 목록 복사가 메모리를 사용하므로, 저장 대신 스트리밍 계산이 더 적합할 수 있습니다. 하지만 그룹 iterator를 나중에도 재사용할 수 있다고 생각하고 바깥 groupby를 계속 진행하는 방식은 피해야 합니다.

필요한 만큼만 즉시 소비합니다

  1. groupby 입력과 key 함수를 정합니다.
  2. 바깥 반복에서 (key, group)을 받습니다.
  3. 값을 나중에 다시 써야 하면 현재 위치에서 list(group)를 만듭니다.
  4. 한 번 처리하면 충분하다면 그룹을 그 자리에서 합산하거나 출력합니다.
  5. 다음 그룹으로 넘어간 뒤 이전 iterator가 되살아난다고 가정하지 않습니다.

groupby를 먼저 list(...)로 감싸 쌍 전체를 저장하는 것은 그룹 데이터 전체를 복사하는 것과 다릅니다. 목록에는 키와 iterator 객체가 들어갈 뿐, 각 그룹 원소가 독립된 리스트로 바뀌지 않습니다. 이런 차이는 iterator를 반환하는 함수 전반에서 중요합니다. iterator는 소비할 때 값을 제공하는 진행 상태이므로, 같은 iterator를 두 변수가 가리켜도 두 개의 복사본이 생기지 않습니다. 별도의 결과가 필요하면 목록이나 튜플 같은 자료구조로 원하는 시점에 변환하세요.

바깥 반복과 그룹 반복의 소비를 따로 추적합니다

흔한 실수는 [(key, group) for key, group in groupby(...)]가 모든 그룹 요소까지 저장한다고 생각하는 것입니다. 이 코드는 그룹 iterator의 참조를 모을 뿐입니다. 또 현재 그룹을 일부만 읽은 뒤 바깥 반복을 진행하면, 나머지 그룹 요소는 다음 경계를 찾는 과정에서 소비될 수 있습니다. 공식 itertools 라이브러리 문서는 나중에 쓸 그룹 데이터를 리스트로 저장하라고 안내합니다. HOWTO도 다음 그룹을 요청하기 전에 현재 그룹을 소비해야 한다고 설명합니다. 코드 추적 문제에서는 바깥 반복 횟수와 각 group의 소비 위치를 별도로 표시하면 빈 결과가 나오는 이유를 찾을 수 있습니다.

그룹 전체를 목록으로 복사하면 그 그룹의 값을 보관하지만, 입력 전체를 한 번에 복사한 것은 아닙니다. 반대로 그룹 크기에 제한이 없는데 모든 그룹을 리스트로 저장하면 메모리 사용량도 입력 크기에 따라 늘어납니다. 따라서 ‘항상 list로 바꾼다’가 아니라 ‘나중에 필요한 그룹만 복사한다’가 정확한 방법입니다. 각 그룹에서 통계를 바로 계산할 수 있다면 iterator의 한 번 소비 특성을 그대로 활용할 수 있습니다.

정답을 가리고 확인해 보세요

이 글을 위해 만든 학습 예시이며 공식 기출문제나 판매 카드의 인용이 아닙니다.

groupby의 (key, group) 쌍을 바깥 반복이 모두 끝날 때까지 저장해 두면 각 group의 원소도 독립적으로 보관되나요? 나중에 필요하다면 어떤 식으로 저장해야 하나요?

정답과 해설 확인아닙니다. group은 공유 입력을 소비하는 iterator입니다. 바깥 반복에서 그룹을 받은 즉시 list(group)로 복사해야 합니다.

groupby의 반환 그룹은 동일한 원본 iterable을 공유하며, 바깥 groupby를 진행하면 이전 그룹이 더는 독립적으로 남지 않습니다. 공식 문서는 나중에 필요한 그룹을 list로 저장하는 방법을 안내합니다.