- [BW 31] 인자에 대해 이터레이션할 때는 방어적이 돼라2024년 10월 31일
- 31514
- 작성자
- 2024.10.31.:01
입력 전체의 합계를 내고 각 원소를 나누는 정규화 함수가 있다고 하자.
def normalize(numbers): total = sum(numbers) result = [] for v in numbers: percent = 100 * v / total result.append(percent) return result
이 함수에 데이터가 들어 있는 리스트를 입력하면 잘 작동한다.
visits = [15, 35, 80] percetages = normalize(visits) print(percetages) >>> [11.538461538461538, 26.923076923076923, 61.53846153846154]
코드의 확장성을 높이기 위해 데이터를 파일에서 읽어온다고 해보자.
데이터가 커지면 많은 메모리를 소모하고, 함수를 재사용해야 할 수도 있으므로 제네레이터를 정의한다.
def read_visits(data_path): with open(data_path) as f: for line in f: yield int(line)
놀랍게도 normalize 함수에 read_visits가 반환한 값을 전달하면 아무 결과도 나오지 않는다.
it = read_visits('numbers.txt') print(normalize(it)) >>> []
이런 현상이 나타나는 이유는 이터레이터가 결과를 단 한 번만 만들어내기 때문이다.
it = read_visits('numbers.txt') print(list(it)) print(list(it)) >>> [15, 35, 80] []
하지만 이미 소진된 이터레이터에 대해 이터레이션을 수행해도 아무런 오류가 발생하지 않아서 출력이 없는 이터레이터인지, 이미 소진된건지 구분할 수 없다.
이 문제를 해결하기 위해 입력 이터레이터를 명시적으로 소진시키고 이터레이터의 전체 내용을 리스트에 넣을 수 있다.
def normalize(numbers): numbers = list(numbers) # 명시적으로 소진 total = sum(numbers) result = [] for v in numbers: percent = 100 * v / total result.append(percent) return result
역시 명시적으로 소진하는 과정도 메모리 소모가 크다.
이외에도 호출될 때마다 새로 이터레이터를 반환하는 함수를 받는 것, 이터레이터 프로토콜을 구현한 새로운 컨테이너 클래스 제공하는 것의 방법이 있는데, 읽어보니 그냥 애초부터 제네레이터로 받지 않고 리스트로 받는 게 더 좋을 거 같다.
물론 성능도 중요하지만 그 코드를 다른 사람이 읽었을 때도 이해하기 쉬워야된다고 생각한다.
'Book > 파이썬 코딩의 기술' 카테고리의 다른 글
[BW 33] yield from을 사용해 여러 제너레이터를 합성하라 (1) 2024.11.08 [BW 32] 긴 리스트 컴프리헨션보다는 제네레이터 식을 사용하라 (2) 2024.11.01 [BW 30] 리스트를 반환하기보다는 제너레이터를 사용하라 (2) 2024.10.30 [BW 29] 대입식을 사용해 컴프리헨션 안에서 반복 작업을 피하라 (0) 2024.10.29 [BW 28] 컴프리헨션 내부에 제어 하위 식을 세 개 이상 사용하지 말라 (0) 2024.10.28 다음글이전글이전 글이 없습니다.댓글
스킨 업데이트 안내
현재 이용하고 계신 스킨의 버전보다 더 높은 최신 버전이 감지 되었습니다. 최신버전 스킨 파일을 다운로드 받을 수 있는 페이지로 이동하시겠습니까?
("아니오" 를 선택할 시 30일 동안 최신 버전이 감지되어도 모달 창이 표시되지 않습니다.)