前回はCPythonの講演とPyPyの講演を紹介しました。今回は,
Application:アプリケーション
Beyond Python Enhanced Generators:Python拡張ジェネレーターの先へ
動的にシーケンスを生成するものとしてジェネレーターがあります。本講演を紹介する前にPythonのジェネレーターについて簡単に紹介します。ジェネレーターは,
>>> def f():
... for i in range(3):
... yield i
>>> g = f()
>>> g.next()
0
>>> g.next()
1
>>> g.next()
2
>>> g.next()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
forループの中でyield文が実行される毎に,next()
メソッドを実行することで,yield
された値を受け取れます。
ジェネレーターを任意の場所で終了させるときは,StopIteration
を発生させます。また,StopIteration
が発生します。
yield文は,
>>> def f():
... msg = yield "send me message"
... print msg
... yield
>>> g = f()
>>> g.next()
'send me message'
>>> g.send("Hello")
Hello
send()というメソッドでジェネレーター内へメッセージを送り,
本講演はWeightlessの開発者でもあるErik Groeneveld氏によって行われました。なお,
Weightless
Weightlessは,
- compose:コルーチンに分解するモジュール
- observable:オブザーバーパターンによるコンポーネントの設定モジュール
- gio:ソケットをコルーチンに接続するモジュール
Weightlessは,
トランポリンモデル
一方でWeightlessのモデルと対比してトランポリンモデルも紹介されていました。筆者はトランポリン
トランポリンモデルを表す次のサンプルコードが紹介されていました。
def demo_coroutine():
"""coroutine a la COBOL"""
def coroutine_a(n = 0):
while n < 10:
n = yield b, n + 1
print ">", n
def coroutine_b(n = 0):
while n < 10:
n = yield a, n + 1
print "<", n
a = coroutine_a()
b = coroutine_b()
a.next()
b.next()
g = (a, 0)
while True: # トランポリン
try:
g = g[0].send(g[1])
except:
break
>>> demo_coroutine()
> 0
< 1
> 2
< 3
...
coroutine_
はcoroutine_
を,coroutine_
はcoroutine_
を交互に呼び出します。この2つのジェネレーターを交互に呼び出す制御部にあたるのがwhileループの部分です。
while True: # トランポリン
try:
g = g[0].send(g[1])
except:
break
g = g[0].send(g[1])
はそれぞれのジェネレーターを交互に呼び出します。a.
を実行するとcoroutine_
ではn = 0
がセットされ,print “>”, n
が呼び出され,yield b, (n + 1)
によりcoroutine_
が一時停止します。呼び出し元ではg = (b, 1)
が返されて,b.
が実行されます。それからb.
を実行すると,yield a, (n + 1)
でcoroutine_
が一時停止して,g = (a, 2)
が返されます。
このように,
composeとPEP 380
composeモジュールを使ってジェネレータの処理をサブジェネレーターへ委譲します。Pythonのジェネレーターは,
実際に簡単なサンプルコードを書いてみます。
>>> def f():
... yield "Hello"
... msg = yield
... yield msg
>>> def g():
... yield f()
>>> _r = g()
>>> _r
<generator object g at 0x10078ca00>
>>> r = _r.next()
>>> r
<generator object f at 0x10078c550>
>>> r.next()
'Hello'
>>> r.next()
>>> r.send("World")
'World'
composeを使うと,
>>> from weightless.core import compose
>>> r = compose(g())
>>> r
<generator object _compose at 0x10078c910>
>>> r.next()
'Hello'
>>> r.next()
>>> r.send("World")
'World'
g()
というジェネレーターが返すサブジェネレーターを意識せずに操作できます。委譲の考え方から,g()
ジェネレーターが何をするかを知る必要はないのでcompose
がサブジェネレーターを適切に扱ってくれると,g()
ジェネレーターとの結合度が低くなります。
そしてcomposeはデコレーターとして使うこともできます。
@compose
def g():
yield f()
また,yield from
式を使って次のように記述できるようになる予定です。
def g():
yield from f()
composeはyield from
とよく似た概念であり,