파이썬 클린 코드 - 제네레이터 개요
제네레이터 개요
대규모의 구매 기록에서 최저 판매가, 최고 판매가, 평균 판매가를 계산하고 싶다고 하자.
<purchase_data> , <price>
이런 구매 기록을 받아 필요한 지표를 구해주는 객체를 만든다고 할때, 최솟값이나 최댓값 같은 지표는 min(), max() 같은 내장함수로 쉽게 구할 수 있다. 하지만 어떤 지표는 모든 기록을 다 계산해서 얻을 수 있다. 구현하는 방법을 생각해보자면 간단하게 for 루프에서 각 단계에서 각 지표를 업데이트 하는 방법을 생각해보자.
class PurchasesStats:
def __init__(self, purchases):
self.purchases = iter(purchases)
self.min_price: float = None
self.max_price: float = None
self._total_purchases_price: float = 0.0
self._total_purchases = 0
self._initialize()
def _initialize():
try:
first_value = next(self.purchases)
except StopIteration:
raise ValueError("no values provided")
self.min_price = self.max_price = first_value
self._update_avg(first_value)
def process(self):
for purchase_value in self.purchases:
self._update_min(purchase_value)
self._update_max(purchase_value)
self._update_avg(purchase_value)
return self
def _update_min(self, new_value: float):
if new_value < self.min_price:
self.min_price = new_value
def _update_max(self, new_value: float):
if new_value > self > max_price:
self.max_price = new_value
@property
def avg_price(self):
return self._total_purchases_price / self._total_purchases
def _update_avg(self, new_value: float):
self._total_purchases_price += new_value
self._total_purchases += 1
def __str__(self):
return (
f"{self.__class__.__name}({self.min_price},"
f"{self.max_price, {self.avg_price}})"
)
이제 이 모든 정보를 로드해서 담아서 반환해주는 함수를 만들어 보기
리스트에 불러오기
def _load_purchases(file_name):
purchases = []
with open(filename) as f:
for line in f:
*_, price_raw = line.partition(",")
purchases.append(float(price_raw))
return purchases
정확한 결과를 반환하지만, 파일에서 모든 정보를 읽어 list에 저장하기 떄문에
로드하는데 시간이 오래 걸리며 메모리 용량을 넘을 수도 있다.
결과는 정확함. 하지만 _load_purchases에서 다 읽어와 list에 저장하고
다 읽어온 데이터를 다시 for문으로 돌리기 때문에 비효율적
-
load해서 list에 저장하는 단계에서 시간과 메모리 둘 다 많이 필요함.
-
앞에서 계산하는 코드는 한번에 하나의 데이터만을 사용함
-> 굳이 모든 데이터를 모두 읽어 메모리에 보관할 이유 x
해결책: 제네레이터를 만들어 필요한 값을 그때 그때 만들기
또한 앞에서 계산하는 코드는 한 번에 하나의 데이터만을 사용하고 있다.
그러므로 굳이 모든 데이터를 한 번에 모두 읽어 메모리에 보관할 이유가 없다.
해결책은 제네레이터를 만드는 것이다. 파일의 전체 내용을 리스트에 보관하는 대신
필요한 값을 그때 그때 가져오는 것이다. 다음과 같이 코드를 수정한다.
제네레이터로 효율적
def load_purchases(file_name):
with open(filename) as f:
for line in f:
*_, price_raw = line.partition(",")
yield float(price_raw)
메모리를 많이 필요하던 리스트 사라지고 return도 사라짐
이때의 load_purchases 함수를 제네레이터 함수, 또는 단순히 제네레이터라고 함.
-> 파이썬에선 어떤 함수라도 yield 키워드를 쓰면 제네레이터 함수가 된다.
yield가 포함된 이 함수를 호출하면 제네레이터의 인스턴스를 만듬
>>> load_purchases("purchases.csv")
<generator object load_purchases at 0x000001A888D422C8>
모든 제네레이터 객체는 이터러블입니다. (이터러블은 뒤에서 더 다룹니다.)
여기서 중요한 것은 제네레이터로 바꿨지만
함수의 사용 코드가 그대로 라는것!
즉 이터러블을 사용하면 for 루프의 다형성을 보장하는 강력한 추상화가 가능함!
-> 이터러블 인터페이스를 따르면 투명하게 객체의 요소를 반복하는 것이 가능하다
제네레이터 표현식
제네레이터는 이터레이터이므로 리스트나 튜플, 세트처럼 많은 메모리를 필요로 하는 이터러블이나 컨테이너의 대안이 될 수 있음.
제네레이터 컴프리헨션이라고 불러야한다는 주장도 있지만 제네레이터 표현식이 일반적으로 쓰임
[x** for x in range(10)] #리스트 컴프리헨션
(x**2 for x in range(10)) # 제네레이터 표현식(컴프리헨션)
sum(x**2 for x in range(10)) # 이터러블 연산이 가능한 함수에 제네레이터 표현식을 직접 전달 가능함.
Author And Source
이 문제에 관하여(파이썬 클린 코드 - 제네레이터 개요), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://velog.io/@hayaseleu/파이썬-클린-코드-제네레이터저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)