# ex) 1년간 전체 여행자 수를 계산하기 위해 입력 전체의 합계를 내고 이 합계로 각 도시의 반문자 수를 나누는 정규화 함수가 필요
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)
#
it = read_visits('my_numbers.txt')
percentages = normalize(it)
print(percentages)
[]
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
it = read_visits('my_numbers.txt')
percentages = normalize_copy(it)
print(percentages)
assert sum(percentages) == 100.0
[11.538461538461538, 26.923076923076923, 61.53846153846154]
def normalize_func(get_iter):
total = sum(get_iter()) # 새 이터레이터
result = []
for value in get_iter(): # 새 이터레이터
percent = 100 * value / total
result.append(percent)
return result
def read_visits(data_path):
with open(data_path) as f:
for line in f:
yield int(line)
#
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
#새로운 컨데이터 타입을 원래의 normalize 함수에 넘기면 코드를 전혀 바꾸지 않아도 함수가 잘 작동한다.
visits = ReadVisits(path)
percentages = normalize(visits)
print(percentages)
assert sum(percentages) == 100.0
[11.538461538461538, 26.923076923076923, 61.53846153846154]
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 내장 모듈은 isinstance를 사용해 잠재적인 문제를 검사할 수 있는 Iterator 클래스를 제공한다.
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
visits = [15, 35, 80]
percentages = normalize_defensive(visits)
print(percentages)
assert sum(percentages) == 100.0
visits = ReadVisits(path)
percentages = normalize_defensive(visits)
assert sum(percentages) == 100.0
[11.538461538461538, 26.923076923076923, 61.53846153846154]
NameError Traceback (most recent call last)
in
2 assert sum(percentages) == 100.0
3
----> 4 visits = ReadVisits(path)
5 percentages = normalize_defensive(visits)
6 assert sum(percentages) == 100.0
NameError: name 'ReadVisits' is not defined
visits = [15, 35, 80]
it = iter(visits)
# 오류가 나는 부분. 오류를 보고 싶으면 커멘트를 해제할것
normalize_defensive(it)
TypeError Traceback (most recent call last)
in
2 it = iter(visits)
3 # 오류가 나는 부분. 오류를 보고 싶으면 커멘트를 해제할것
----> 4 normalize_defensive(it)
in normalize_defensive(numbers)
3 def normalize_defensive(numbers):
4 if isinstance(numbers, Iterator): # 반복 가능한 이터레이터인지 검사하는 다른 방법
----> 5 raise TypeError('컨테이너를 제공해야 합니다')
6 total = sum(numbers)
7 result = []
TypeError: 컨테이너를 제공해야 합니다