yield
についてラクス Python Tips LT会
2021/02/25 nikkie
ふだん yield
使ってますか?
ガッツリ💪
たまに😃
初耳!👂
関わっているPythonコミュニティ
毎月第2水曜の みんなのPython勉強会
Pythonのカンファレンス PyCon JP の アーカイブ
PyCon JP 2021座長です。スタッフ募集中!📣
yield
についてPython Tipsと聞いて、浮かんだネタの1つが yield
でした
yield
とは
yield
よき
yield
こんなことまで
yield
別の使い方
yield
についてyield
とは
yield
よき
yield
こんなことまで
yield
別の使い方(Appendix)
yield
の簡単な例
def easy_generator():
yield "👩"
yield "🐯"
yield "🐟"
yield
を使った 関数 (用語集)
例:先の関数 easy_generator
もジェネレータ
ジェネレータ関数の 返り値 (用語集)
>>> g = easy_generator() # gはジェネレータイテレータ
>>> type(g)
<class 'generator'>
next
で次の要素を取得next
は、イテレータ の __next__()
メソッドを呼び出し、次の要素を取得(ドキュメント)
>>> next(g) # ジェネレータイテレータの次の要素
'👩'
yield
で一時停止next(g)
で 2行目の yield
で値が返され、一時停止
def easy_generator():
yield "👩" # 👈
yield "🐯"
yield "🐟"
>>> next(g) # 次の要素
'🐯'
def easy_generator():
yield "👩"
yield "🐯" # 👈
yield "🐟"
>>> next(g) # 次の要素
'🐟'
def easy_generator():
yield "👩"
yield "🐯"
yield "🐟" # 👈
StopIteration
例外(ドキュメント)
>>> next(g)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
for
文で繰り返せるジェネレータ イテレータ
>>> for item in easy_generator():
... print(item)
...
👩
🐯
🐟
tips: StopIteration
例外は繰り返しの仕組みに関係(PyCon JP 2017 トーク)
yield
についてyield
とは
yield
よき
yield
こんなことまで
yield
別の使い方(Appendix)
yield
何がいいの?リストも for
文で繰り返せる
def return_list():
return ["👩", "🐯", "🐟"]
>>> for item in return_list():
... print(item)
...
👩
🐯
🐟
yield
の比較(Appendixで実験)リストの場合はすべての要素をメモリに保持する
長くないリストならいいのですが、長くなると・・😢
yield
は すべてメモリに展開しない 👈 tips!
一時停止により、一度に1つの要素 を処理
リストで全要素をメモリに保持するのにかかる時間が yield
では発生しない
yield
でファイル読み込み
def practical_generator(file_path):
with open(file_path) as fh:
for row in fh:
yield row
たくさんの行をもつファイル(example.txt
)があります
Kumiko
Haduki
Sapphire
Reina
:
yield
でファイル読み込み
>>> g = practical_generator("example.txt")
>>> for member in g:
... print(member.rstrip()) # 右側に付く \n を除く
...
Kumiko
Haduki
yield
についてyield
とは
yield
よき
yield
こんなことまで
yield
別の使い方(Appendix)
next(g)
(g.__next__()
)は、一時停止していた yield
の後から再開
g.send()
で 値を送って、再開 させられる
yield
は送られた値を受け取れる(value = yield "🐯"
)
def send_example_generator():
value = "🐯"
while True:
value = yield value
if not value:
break
else:
value = "🐟"
send
メソッド
>>> g = send_example_generator()
>>> g.send(None) # 開始するときはNoneを送る(next(g)でも開始)
'🐯'
def send_example_generator():
value = "🐯"
while True:
value = yield value # 初期値 🐯 が返った
if not value:
break
else:
value = "🐟"
send
メソッド
>>> g.send(1)
'🐟'
def send_example_generator():
value = "🐯"
while True:
value = yield value # valueに1が代入された
if not value:
break
else:
value = "🐟"
send
メソッド
>>> g.send("False")
'🐟'
def send_example_generator():
value = "🐯"
while True:
value = yield value # valueに"False"が代入された
if not value: # bool(value)がFalseならジェネレータ実行は終了
break
else:
value = "🐟"
send
メソッド
>>> bool([])
False
>>> g.send([]) # ジェネレータ実行を止める
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
yield
についてyield
を使った関数= ジェネレータ
一時停止 & (値を送って) 再開
ジェネレータの返り値はジェネレータ イテレータ
リストを使って繰り返す場合と比べると、全要素をメモリに展開しないため 省メモリ・省時間
Python界隈では 自己紹介のエイリアス が「お前、誰よ」
第2水曜 みんなのPython勉強会 ・PyCon JP アーカイブ
StopIteration
例外は 繰り返しの仕組み に関係
コンテキストマネージャ= with
と一緒に使えるオブジェクト
Enjoy development with yield
!
References、Appendix が続きます(よろしければどうぞ!)
自身を持ってコードを書こう by 陶山さん(2020/07 Python Charity Talks)
Generators, coroutines, and nanoservices by Reuven M. Lerner (PyCon Africa 2020)
When to Use a List Comprehension in Python (Choose Generators for Large Datasets)
The Enters and Exits of Context Managers by Mason Egger(2021/02 ChiPy)
Pythonはどうやってlen関数で長さを手にいれているの? by 清水川さん(PyCon JP 2017)
yield
についてyield
別の使い方(本編に入り切らなかった話題)
yield
のtips
大量の行のファイルを扱う実験
yield
についてyield
とは
yield
よき
yield
こんなことまで
yield
別の使い方
with
と一緒に使える
@contextlib.contextmanager
def contextmanager_generator():
# withのブロックに入る前の処理(__enter__)
yield # 値を返したときは as で受け取れる
# withのブロックを抜けた直後の処理(__exit__)
with
の中でだけ、Config
の属性を書き換え(18章)
@contextlib.contextmanager
def using_config(name, value):
old_value = getattr(Config, name)
setattr(Config, name, value)
try:
yield
finally:
setattr(Config, name, old_value)
yield
のtipsyield from <イテレータ>
ref: PEP 380 Python 3.3〜
ファイル読み込みの例を次で書き変えます
yield
でファイル読み込みの例を書き換え
def practical_generator(file_path):
with open(file_path) as fh:
# for row in fh:
# yield row
yield from fh
>>> g = easy_generator()
>>> for item in g:
... print(item)
...
👩
🐯
🐟
>>> for item in g:
... print(item)
...
itertools.tee
(ドキュメント)
一つの iterable から n 個の独立したイテレータを返します。
>>> import itertools
>>> g = easy_generator()
>>> g1, g2 = itertools.tee(g)
>>> for item in g1:
... print(item)
...
👩
🐯
🐟
>>> for item in g2:
... print(item)
...
👩
🐯
🐟
yield
10行を繰り返して行の数が多いファイルを用意
$ wc -l many_names_1b.txt # 10億行!(6.4GB)
1000000000 many_names_1b.txt
MacBook Pro
プロセサ 2.3 GHz Intel Core i5、4コア
メモリ 16GB
Python 3.9.0
$ time python compare_speed.py
real 21m47.461s
user 5m50.540s
sys 10m31.005s
yield
を使った場合
$ time python compare_speed.py
real 3m42.741s
user 3m40.157s
sys 0m2.286s
time
コマンドの結果の見方user:プログラム自体の処理時間
sys:プログラムを処理するために、OSが処理をした時間
user を比べると、リストのほうが長い -> 全ての要素を保持する処理の時間と考えられる
sys を比べると、大きな差 -> プログラム処理で巨大なリストを扱うためにOSの処理が必要になったと考えている
大量の行のファイルの読み込み、yield
を試してみては?