エキスパートPythonプログラミング

気付けば今年も後ひと月ちょっと、ここのところ会社滞在時間が延びてるなぁ。。。
Anacondaとかvirt-installとかPythonのコードを眺める機会も増えてきた。
まだまだ訳分からないけど、ちょっとずつでも読み進められるようになろう。

シーケンスの要素とインデックスを同時に

enumerate関数を使って。

>>> seq = ["one", "two", "three"]
>>> for i, element in enumerate(seq):
...     seq[i] = '%d: %s' % (i, element)
... 
>>> seq
['0: one', '1: two', '2: three']
>>> 

同じことを関数とリスト内包表記で実現できてしまうらしい

>>> def _treatment(pos, element):
...     return '%d: %s' % (pos, element)
... 
>>> seq = ["one", "two", "three"]
>>> [_treatment(i, el) for (i, el) in enumerate(seq)]
['0: one', '1: two', '2: three']
>>> 

おお〜、すごい。関数化してあるので、なんか読みやすい。

ジェネレータ

の前にイテレータ
次の要素を返すnext()とイテレータ自身を返す__iter__()を持つクラスとして実現できる。
イテレータクラスのオブジェクトをforループの要素に渡すと以下の順で実行される

  1. __init__
  2. __iter__
  3. next() : StopIterationが出るまで繰り返し

pdbモジュールを使ってステップ実行すると上記順で実行されているのが見える。

で、肝心のジェネレータ。
イテレータプロトコルに対応したもので、必要なデータを必要な時に生成して返してくれるので
大量のデータを処理したりする時にも省メモリ
関数(メソッド)をジェネレータにするには、復帰値を返す処理に yield ステートメントを使う

[kobakoba0723@fc15-x64 ~]$ cat generate.py 
def power(seq):
    print("seq in power: ", seq)
    for value in seq:
        print('powering %s' % value)
        yield value ** 2


def adder(gen_obj):
    print("gen_obj in adder: ", gen_obj)
    for value in gen_obj:
        print('adding to %s' % value)
        if value % 2 == 0:
            yield value + 3
        else:
            yield value + 2


elements = [1, 4, 7, 9, 12, 19]
res = adder(power(elements))
print next(res)
print next(res)
[kobakoba0723@fc15-x64 ~]$ python generate.py 
('gen_obj in adder: ', <generator object power at 0x1e7d640>)
('seq in power: ', [1, 4, 7, 9, 12, 19])
powering 1
adding to 1
3
powering 4
adding to 16
19
[kobakoba0723@fc15-x64 ~]$ 

ジェネレータもイテレータプロトコルの一種なのでnext()を実行すると値の生成/送出が行われる。
ジェネレータ関数を入れ子にしてあるので、adder()はジェネレータオブジェクトを受け取る。
メインの処理で、nextを実行するとpower()のyieldが実行され、adder()のジェネレータオブジェクトから値が送出される
for文は、StopIterationが挙がるまでnext()を実行するので、"print next(res)"を7回実行すると、
adder()のfor文でStopIteration例外が発生しプログラムが終了する

[kobakoba0723@fc15-x64 ~]$ python generate.py 
('gen_obj in adder: ', <generator object power at 0xa83640>)
('seq in power: ', [1, 4, 7, 9, 12, 19])
powering 1
adding to 1
3
... snip ...
powering 19
adding to 361
363
Traceback (most recent call last):
  File "generate.py", line 26, in <module>
    print next(res)
StopIteration
[kobakoba0723@fc15-x64 ~]$ 

ジェネレータには、next()の他に

  • send():yield に値を渡すことができ、yieldはsend()から渡された値を返す
  • throw():任意の例外をyield に対して渡す
  • close():GeneratorExit例外を yield に渡す

コルーチン

複数の箇所で実行を一時停止したり、再開したり出来る機能をもった関数
PEP342に例が載っているから後で読んでみよう。
それと、本にはエコーサーバのサーバ側のコードが載ってるから、
クライアントのコードを書いて試してみよう。socketをどうやって使うか、からだな、まずは

itertools

一般的な使用パターンのイテレータを作れるようにしたモジュール。
標準ライブラリリファレンス 9.7 itertools 効率的なループ実行のためのイテレータ生成関数
以下を覚えておけばよいらしい

  • islice:シーケンスのサブグループに対するイテレータを返す
  • tee:1つのシーケンスから複数のイテレータを返す
  • groupby:シーケンス内の連続した要素をグループ化する(イテレータを返すのか???)

エキスパートPythonプログラミング

エキスパートPythonプログラミング

ここから下は、generate.pyのトレース

generate.py のトレース
[kobakoba0723@fc15-x64 ~]$ python -m pdb generate.py 
> /home/kobakoba0723/generate.py(1)<module>()
-> def power(seq):
(Pdb) n
> /home/kobakoba0723/generate.py(8)<module>()
-> def adder(gen_obj):
(Pdb) n
> /home/kobakoba0723/generate.py(18)<module>()
-> elements = [1, 4, 7, 9, 12, 19]
(Pdb) 
> /home/kobakoba0723/generate.py(19)<module>()
-> res = adder(power(elements))
(Pdb) 
> /home/kobakoba0723/generate.py(20)<module>()
-> print next(res)
(Pdb) 
[kobakoba0723@fc15-x64 ~]$ python -m pdb generate.py 
> /home/kobakoba0723/generate.py(1)<module>()
-> def power(seq):
(Pdb) s
> /home/kobakoba0723/generate.py(8)<module>()
-> def adder(gen_obj):
(Pdb) s
> /home/kobakoba0723/generate.py(18)<module>()
-> elements = [1, 4, 7, 9, 12, 19]
(Pdb) 
> /home/kobakoba0723/generate.py(19)<module>()
-> res = adder(power(elements))
(Pdb) 
> /home/kobakoba0723/generate.py(20)<module>()
-> print next(res)
(Pdb) 
[kobakoba0723@fc15-x64 ~]$ python -m pdb generate.py 
> /home/kobakoba0723/generate.py(1)<module>()
-> def power(seq):
(Pdb) n
> /home/kobakoba0723/generate.py(8)<module>()
-> def adder(gen_obj):
(Pdb) 
> /home/kobakoba0723/generate.py(18)<module>()
-> elements = [1, 4, 7, 9, 12, 19]
(Pdb) 
> /home/kobakoba0723/generate.py(19)<module>()
-> res = adder(power(elements))
(Pdb) 
> /home/kobakoba0723/generate.py(20)<module>()
-> print next(res)
(Pdb) s
--Call--
> /home/kobakoba0723/generate.py(8)adder()
-> def adder(gen_obj):
(Pdb) 
> /home/kobakoba0723/generate.py(9)adder()
-> print("gen_obj in adder: ", gen_obj)
(Pdb) 
('gen_obj in adder: ', <generator object power at 0x172b870>)
> /home/kobakoba0723/generate.py(10)adder()
-> for value in gen_obj:
(Pdb) 
--Call--
> /home/kobakoba0723/generate.py(1)power()
-> def power(seq):
(Pdb) 
> /home/kobakoba0723/generate.py(2)power()
-> print("seq in power: ", seq)
(Pdb) 
('seq in power: ', [1, 4, 7, 9, 12, 19])
> /home/kobakoba0723/generate.py(3)power()
-> for value in seq:
(Pdb) 
> /home/kobakoba0723/generate.py(4)power()
-> print('powering %s' % value)
(Pdb) 
powering 1
> /home/kobakoba0723/generate.py(5)power()
-> yield value ** 2
(Pdb) 
--Return--
> /home/kobakoba0723/generate.py(5)power()->1
-> yield value ** 2
(Pdb) 
> /home/kobakoba0723/generate.py(11)adder()
-> print('adding to %s' % value)
(Pdb) 
adding to 1
> /home/kobakoba0723/generate.py(12)adder()
-> if value % 2 == 0:
(Pdb) 
> /home/kobakoba0723/generate.py(15)adder()
-> yield value + 2
(Pdb) 
--Return--
> /home/kobakoba0723/generate.py(15)adder()->3
-> yield value + 2
(Pdb) 
3
> /home/kobakoba0723/generate.py(21)<module>()
-> print next(res)
(Pdb) 
--Call--
> /home/kobakoba0723/generate.py(15)adder()->3
-> yield value + 2
(Pdb) 
> /home/kobakoba0723/generate.py(10)adder()->3
-> for value in gen_obj:
(Pdb) 
--Call--
> /home/kobakoba0723/generate.py(5)power()->1
-> yield value ** 2
(Pdb) 
> /home/kobakoba0723/generate.py(3)power()->1
-> for value in seq:
(Pdb) 
> /home/kobakoba0723/generate.py(4)power()->1
-> print('powering %s' % value)
(Pdb) 
powering 4
> /home/kobakoba0723/generate.py(5)power()->1
-> yield value ** 2
(Pdb) 
--Return--
> /home/kobakoba0723/generate.py(5)power()->16
-> yield value ** 2
(Pdb) 
> /home/kobakoba0723/generate.py(11)adder()->3
-> print('adding to %s' % value)
(Pdb) 
adding to 16
> /home/kobakoba0723/generate.py(12)adder()->3
-> if value % 2 == 0:
(Pdb) 
> /home/kobakoba0723/generate.py(13)adder()->3
-> yield value + 3
(Pdb) 
--Return--
> /home/kobakoba0723/generate.py(13)adder()->19
-> yield value + 3
(Pdb) 
19
--Return--
> /home/kobakoba0723/generate.py(21)<module>()->None
-> print next(res)
(Pdb) 
--Return--
> <string>(1)<module>()->None
(Pdb) 
The program finished and will be restarted