web-dev-qa-db-ja.com

itertools "grouper"オブジェクトをリストに変える方法

Pythonでitertools.groupbyを使用する方法を学びたいので、文字の各グループのサイズを見つけたかったのです。最初に、単一の長さを見つけられるかどうかを確認しようとしましたグループ:

from itertools import groupby
len(list(list( groupby("cccccaaaaatttttsssssss") )[0][1]))

と私は毎回0を取得します。

少し調べてみたところ、他の人がこのようにしていたことがわかりました。

from itertools import groupby
for key,grouper in groupby("cccccaaaaatttttsssssss"):
    print key,len(list(grouper))

それは素晴らしい作品です。私が混乱しているのは、なぜ後者のコードは機能するが、前者は機能しないのですか?元のコードで実行しようとしていたように、n番目のグループのみを取得したい場合、どうすればよいですか?

11
cafemolecular

最初のアプローチが機能しない理由は、リストを作成するとグループが「消費」されるためです

_list(groupby("cccccaaaaatttttsssssss"))
_

the groupby docs から引用するには

返されたグループは、それ自体がgroupby()と基本のイテラブルを共有するイテレータです。ソースが共有されているため、groupby()オブジェクトが拡張されると、前のグループは表示されなくなります。

それを段階に分解してみましょう。

_from itertools import groupby

a = list(groupby("cccccaaaaatttttsssssss"))
print(a)
b = a[0][1]
print(b)
print('So far, so good')
print(list(b))
print('What?!')
_

出力

_[('c', <itertools._grouper object at 0xb715104c>), ('a', <itertools._grouper object at 0xb715108c>), ('t', <itertools._grouper object at 0xb71510cc>), ('s', <itertools._grouper object at 0xb715110c>)]
<itertools._grouper object at 0xb715104c>
So far, so good
[]
What?!
_

_itertools._grouper object at 0xb715104c_は、groupbyによって返された「親」イテレータと内容を共有しているため空です。これらの項目は、最初のlist呼び出しが親に対して反復されたためになくなりました。

単純なジェネレータ式など、イテレータを2回繰り返してみても何も変わりません。

_g = (c for c in 'python')
print(list(g))
print(list(g))
_

出力

_['p', 'y', 't', 'h', 'o', 'n']
[]
_

ところで、実際にその内容が必要ない場合に、groupbyグループの長さを取得する別の方法があります。長さを見つけるためだけにリストを作成するよりも少し安価です(そしてRAMの使用量が少ない)。

_from itertools import groupby

for k, g in groupby("cccccaaaaatttttsssssss"):
    print(k, sum(1 for _ in g))
_

出力

_c 5
a 5
t 5
s 7
_
13
PM 2Ring