파이썬의 생성기는 Yeld만 하는 게 아니에요.
Pythhonista 여러분, TypeHint라고 쓰여 있습니까?나는 기본적으로 쓸 줄 안다.이번에는 TypeHint에 생성기를 쓸 때 나온 의문과 조사 결과를 소개합니다.
일회용 코드가 아닌 파이썬 3이면5개 이상 사용했는데도 Type Hint를 쓰지 않은 사람이 있다면 꼭 뭘 쓰는지 토론해 보세요.TypeHint를 더하면 코드 이해가 쉬워지고 IDE의 예측 변환도 효과적으로 활용돼 개발 효율이 높아진다.TypeHint의 은혜를 최대한 누리는 VScode에 대한 환경 구축은 기사에서 참고하시기 바랍니다.
VScode와 Poetry에서 제작한 파이썬 개발 환경
원래 이른바 생성기
공식 문서에는 다음과 같은 기술이 있다.
generiterator의 함수를 되돌려줍니다.일반적인 함수와 비슷하지만 yeld식이 있다는 점에서는 다르다.이 공식은 값을 생성하는 배열에 사용되며 for 순환에서 사용할 수도 있고,next () 함수를 통해 값을 하나하나 추출할 수도 있습니다.
그렇구나.정보
generator iterator
yield가 국부 실행 상태 (국부 변수나 처리되지 않은try 문장 등 포함) 를 저장할 때마다 처리가 잠시 중단됩니다.생성기 균형기가 복구되면 중단된 위치를 가져옵니다. (일반 함수가 실행될 때마다 새로운 상태에서 시작됩니다.)이런 기록이 있다.뭐가 그렇게 좋으세요?StackOverflow에는 지연평가를 할 수 있다는 답변이 있었다.다음은 이 답변에서 소개한 검색 시스템의 구체적인 예를 소개한다.
SQLite로 사용자 리스트를 만들고 지정된 연령 이하의 사용자의 ID 일람을 검색하는 예를 고려해 보자.먼저 사용자 테이블을 만듭니다.
# ユーザテーブル作成
def init_users_table(n: int) -> None:
# n: ダミーユーザ数
conn = sqlite3.connect("sample_users.db")
cursor = conn.cursor()
# テーブル作成
cursor.execute("CREATE TABLE users(user_id, age)")
# ダミーユーザ追加
for _ in range(n):
user_id = str(uuid4())
age = randint(0, 100)
cursor.execute("INSERT INTO users VALUES (?, ?)", (user_id, age))
conn.commit()
conn.close()
이 표에서 검색을 하는데 목록에서 검색 결과를 받아들이면 다음과 같은 내용이 됩니까?def get_user_ids(age: int) -> List[str]:
conn = sqlite3.connect("sample_users.db")
cursor = conn.cursor()
cursor.execute("SELECT user_id FROM users WHERE age <= ?", (age,))
return cursor.fetchall()
목록의 모든 관련 레코드가 스토리지에 로드됩니다.이번 예에서 가상 사용자 수가 많지 않으면 메모리 오류가 발생하지 않을 것이다.그러나 얻은 열수가 늘어나면 메모리가 압박을 받는다.이럴 때 생성기가 유용할 거예요.
def get_user_ids_generator(age: int) -> Generator[str, None, None]:
conn = sqlite3.connect("sample_users.db")
cursor = conn.cursor()
cursor.execute("SELECT user_id FROM users WHERE age <= ?", (age,))
for user_id in cursor.fetchall():
yield user_id
TypeHint와 같이 목록 대신 생성기 이퀄라이저를 반환합니다.__next__
방법이라고 하면 생성기가 1회yield
를 실행한 후 다음yield
에서 처리를 중단하기 때문에 조건과 일치하는 모든 사용자 ID를 하나하나 꺼낼 수 있다.(for문 내부에서 부르는 것 같다__next__
.생성기와 균형기에 대한 상세한 내용은 아래의 글을 참고하였으니 적당히 참고하시기 바랍니다.
typing.Generator
그럼, 이것은 이 보도의 주제입니다.공식 문서에는 다음과 같은 기술이 있다.
Generator[YieldType, SendType, ReturnType]
내가 여기서 생각한 것은 "
YieldType
가 yield
값의 유형인 것을 알고 있다. SendType
는 무엇입니까? 그리고 ReturnType
생성기를 나에게 돌려줄 수 있지 않습니까?"그러니까구체적인 예는 다음과 같이 기술되어 있다.def echo_round() -> Generator[int, float, str]:
sent = yield 0
while sent >= 0:
sent = yield round(sent)
return 'Done'
이 코드를 볼 때의 의문을 정리했다return
왜 변수에 대입됩니까?yield
0 이상일 때는 언제?sent
때 어떻게 되나요?SendType
우선
return
.전 몰랐지만 생성기 이하의 가격을 줄 수 있을 것 같아요.# ジェネレータを作成
gen = make_generator()
# ジェネレータに値を渡す
gen.send(value)
"SendType
는 SendType
로 제공되는 값의 유형입니다."그러니까공식 문서에 소개된 카운터의 예를 보겠습니다.
def counter(maximum: int) -> Generator[int, int, None]:
i = 0
while i < maximum:
# sendされた値をvalとして受け取る
val = yield i
# sendされていれば、カウント(i)をvalにする
if val is not None:
i = val
# sendされていなければ、カウント(i)を1進める
else:
i += 1
it = counter(10)
print(next(it)) # 0
print(next(it)) # 1
print(it.send(8)) # 8
print(next(it)) # 9
그렇군요. .send
를 사용하여 생성기에 값을 입력하고 계수를 진행합니다.".send
다음.send
은 9가 아니라 8인가요?"그렇게 생각하지만 그렇지.똑같이 생각하는 사람은 아래의 검증을 참조하세요.
def counter_next(it: Generator[int, int, None]) -> None:
print("next")
print("-" * 10)
value = next(it)
print(f"value: {value}")
print("-" * 10)
print()
def counter_send(it: Generator[int, int, None], v: int) -> None:
print("send")
print("-" * 10)
value = it.send(v)
print(f"value: {value}")
print("-" * 10)
print()
def counter(maximum: int) -> Generator[int, int, None]:
i = 0
while i < maximum:
print(f"before count: {i}")
val = yield i
if val is not None:
i = val
else:
i += 1
print(f"after count: {i}")
it = counter(10)
counter_next(it)
counter_next(it)
counter_send(it, 8)
counter_next(it)
실행 결과next
----------
before count: 0
value: 0
----------
next
----------
after count: 1
before count: 1
value: 1
----------
send
----------
after count: 8
before count: 8
value: 8
----------
next
----------
after count: 9
before count: 9
value: 9
----------
발생기는 next
시 함수를 잠시 이탈하기 때문에 애프터와before는 상반된다.2차 이후yield
는 이전에 실행된next
의 후처리를 시작하여next
로 바뀌었다.따라서 실행yield
후 먼저 i가 증가next
한 후의 이미지입니다.ReturnType
다음은
yield
.공식 문서에는 다음과 같은 기록이 있다.생성기 함수에서returnvalue는
ReturnType
방법으로 발송되었습니다__next__()
.이 상황이 발생하거나 함수의 끝에 도달하면 값의 생성이 끝나고 생성기는 더 많은 값을 되돌려주지 않습니다.이걸 정리하면...
StopIteration(value)
에 있으면 return
의 예외StopIteration
하면 함수가 끝날 때와 같이 생성기가 더 높은 값을 되돌려주지 않습니다return
는 값StopIteration
을 가질 수 있고, 이 값의 유형은 return value
이다.def counter(maximum: int) -> Generator[int, int, int]:
i = 0
while i < maximum:
val = yield i
if val is not None:
if val >= 0:
i = val
else:
return -1
else:
i += 1
return 0
방금 카운터 예시에 추가ReturnType
return
까지 진행되면 반환0
다시 쓸 때.send
되돌아오기-1
부터 검증을 시작합니다.it = counter(3)
print(next(it)) # 0
print(next(it)) # 1
print(next(it)) # 2
print(next(it)) # StopIteration: 0 (例外発生)
예상한 대로 계수가 마지막0
에 발생했고 값에는 StopIteration
가 있었다.다음에 우리는 되돌아오는 0
의 상황을 검증할 것이다.print(next(it)) # 0
try:
it.send(-10) # 例外発生
except StopIteration:
print(traceback.format_exc()) # StopIteration: -1
print(next(it)) # StopIteration: 例外発生
이 역시 예상과 같은 수치-1
로 수치StopIteration
다.또 한 번0
이 발생한 후 StopIteration
로 꺼내면 똑같이 발생next()
하지만 수치는 아무것도 넣지 않는 것 같다.총결산
TypeHint의 발단에서 생성기를 조사했습니다.알았어. 예전에 몰랐던 생성기 기능이 재밌어.솔직히 말해서 나는'생성기에서
StopIteration
,SendType
같은 것을 사용할 때가 있나'라고 생각해 왔다. 그것은 내가 파이톤에 비동기 처리를 거의 쓰지 않았기 때문이다.앞으로도 이 기사에 언급되지 않은 ReturnType
를 포함한 비동기 처리를 배우고 싶다.
Reference
이 문제에 관하여(파이썬의 생성기는 Yeld만 하는 게 아니에요.), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://zenn.dev/alivelimb/articles/20220505-typing-generator텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)