방어적이_되어라

매일 공부(ML)·2022년 6월 22일
0

이어드림

목록 보기
82/146

인자에 대해 이터레이션할 때는 방어적이 돼라

객체가 들어있는 리스트를 함수가 파라미터로 받을 때 여러번 이터레이션 하는 것이 중요

def normalize(numbers):
    total = sum(numbers)
    result = []
    for value in numbers:
        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)

"""
이터레이터가 결과를 단 한 번만 만들어내므로 이미 StopIteration예외가 발생한 이터레이터나 제너레이터를 다시 하면 결과를 얻을 수 없다
"""
it = read_visits('my_numbers.txt')
percentages = normalize(it)
print(percentages) #[]

  • 해결

    • 이터레이터를 명시적 소진

    • 이터레이터의 전체 내용을 리스트화

    • 담아둔 리스트에 대해 원하는 수만큼 이터레이션 수행

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(oercentages) == 100.0

"""
메모리를 많이 사용한다.
"""

  • 해결책

    • 호출될 때마다 새로운 이터레이터를 반환하는 함수 받기

    • lambda활용


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

  • 더 나은 해결책

    • 이터레이터 프로토콜

    • 파이썬의 for루프나 연관식을 컨테이너 타입의 내용

    • iter(foo)호출

    • iter 내장함수는 foo.iter라는 특별 메서드 호출

    • next특별 메서드 정의

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)

visits = ReadVisits(path)
percentages = normalize(visits)
print(percentages)
assert sum(percentages) == 100.0

  • 다른 방법

    • collections.abc의 내장모듈인 isinstance사용
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 = ReadVisits(path)
percentages = normalize_defensive(visits)
assert sum(percentages) == 100.0
profile
성장을 도울 아카이빙 블로그

0개의 댓글