def normalize(numbers):
total = sum(numbers)
result = []
for value in numbers:
percent = 100 * value / total
result.append(percent)
return result
visits = [15, 35, 80]
percentages = normalize(visits)
print(percentages)
assert sum(percentages) == 100.0
>>>
[11.538461538461538, 26.923076923076923, 61.53846153846154]
⇒
def read_visits(data_path):
with open(data_path) as f:
for line in f:
yield int(line)
def normalize(numbers):
total = sum(numbers)
result = []
for value in numbers:
percent = 100 * value / total
result.append(percent)
return result
it = read_visits('my_numbers.txt')
percentages = normalize(it)
print(percentages)
>>>
[]
-
이터레이터가 결과를 한번만 만들어내서
stopIteration
예외 발생했거나 제너레이터를 다시 이터레이션하면 결과가 없음 -
이터레이터 없어진 예
it = read_visits('my_numbers.txt') print(list(it)) print(list(it)) # 이미 모든 원소를 다 소진했다 >>> [15, 35, 80] []
-
이터레이터를 명시적으로 소진시키기
- 예(이터레이터 복사)
def normalize_copy(numbers): numbers_copy = list(numbers) # 이터레이터 복사 total = sum(numbers_copy) result = [] for value in numbers_copy: percent = 100 * value / total result.append(percent) return result
-
해당 예제 단점
- 이터레이터를 복사하여 사용하므로 메모리 공간 낭비 발생 가능성이 높음
-
단점 우회 방안
- 호출될 때마다 새로 이터레이터 반환하는 함수를 받도록 하기
def normalize_func(get_iter): total = sum(**get_iter()**) # 새 이터레이터 result = [] for value in **get_iter()**: # 새 이터레이터 percent = 100 * value / total result.append(percent) return result path = 'my_numbers.txt' percentages = normalize_func(lambda: read_visits(path)) print(percentages) assert sum(percentages) == 100.0 >>> [11.538461538461538, 26.923076923076923, 61.53846153846154]
- 이터레이터 프로토콜을 구현한 새로운 컨테이너 클래스 제공
class ReadVisits: def __init__(self, data_path): self.data_path = data_path def __iter__(self): with open(self.data_path) as f: for line in f: yield int(line) def normalize(numbers): total = sum(numbers) result = [] for value in numbers: percent = 100 * value / total result.append(percent) return result visits = ReadVisits(path) percentages = normalize(visits) print(percentages) assert sum(percentages) == 100.0 >>> [11.538461538461538, 26.923076923076923, 61.53846153846154]
sum
메소드가ReadVisits
의__iter__
를 호출하여 새로운 이터레이터 객체 할당for
루프에서도 이전 이터레이터와 다른 이터레이터 새로 할당- iter 내장 함수 VS 컨테이너 타입
- iter 내장 함수: 전달 받은 이터레이터 그대로 반환
- 컨테이너 타입: 새로운 이터레이터 객체 반환
- iter 내장 함수 VS 컨테이너 타입
- 입력 인자로부터 전달 받은 이터레이션을 검사하여 이터레이션할 수 없는 인자인 경우 TypeError 발생시킬 수 있음
def normalize_defensive(numbers): if iter(numbers) is numbers: # 이터레이터# -- 나쁨! raise TypeError('컨테이너를 제공해야 합니다') total = sum(numbers) result = [] for value in numbers: percent = 100 * value / total result.append(percent) return result
collections.abc
내장 모듈 사용한 검사 방법
from collections.abc import Iterator def normalize_defensive(numbers): if isinstance(numbers, Iterator): # 반복 가능한 이터레이터인지 검사하는 다른 방법 raise TypeError('컨테이너를 제공해야 합니다') total = sum(numbers) result = [] for value in numbers: percent = 100 * value / total result.append(percent) return result