銀月の符号

Python 使い見習いの日記・雑記

メモ、 Itertools Recipes

先日はコレの grouper の存在を知っておけば、もしくは体得していれば 10 分どころか 3 分でいけたのに。ということでリンク張っておく。

この節、地味にバージョンアップしているようで。それも増えただけでなく削除されたレシピもある。組み込み関数や itertools として採用され、格があがったものが消えているようだ。以下、勉強用に削除されたものを列挙(iteritems, no 以外はドキュメントのほうに載っているので不要かも)。新しい Python での実用性はないけれど、こうやって作ることができるという参考までに。

# coding: utf-8
from itertools import *

def compress(data, selectors):
    u"""compress('ABCDEF', [1,0,1,0,1,1]) --> A C E F
    
    Python 3.1 で itertools.compress として採用されたため
    レシピから削除されたものと思われる。
    """
    return (d for d, s in izip(data, selectors) if s)

def combinations_with_replacement(iterable, r):
    u"""combinations_with_replacement('ABC', 2) --> AA AB AC BB BC CC

    Python 3.1 で itertools.combinations_with_replacement として
    採用されたためレシピから削除されたものと思われる。
    """
    # number items returned:  (n+r-1)! / r! / (n-1)!
    pool = tuple(iterable)
    n = len(pool)
    if not n and r:
        return
    indices = [0] * r
    yield tuple(pool[i] for i in indices)
    while True:
        for i in reversed(range(r)):
            if indices[i] != n - 1:
                break
        else:
            return
        indices[i:] = [indices[i] + 1] * (r - i)
        yield tuple(pool[i] for i in indices)

def iteritems(mapping):
    u"""マップ型の (key, value) ペアにわたるイテレータを返す

    マップ型は iteritems メソッドを持っているので実用性がない。
    このためレシピから削除されたものと思われる。
    """
    return izip(mapping.iterkeys(), mapping.itervalues())

def all(seq, pred=None):
    u"""Returns True if pred(x) is true for every element in the iterable
    
    Python 2.5 で組み込み関数 all として採用されたため
    レシピから削除されたものと思われる。
    """
    for elem in ifilterfalse(pred, seq):
        return False
    return True

def any(seq, pred=None):
    u"""Returns True if pred(x) is true for at least one element in the iterable
    Python 2.5 で組み込み関数 any として採用されたため
    レシピから削除されたものと思われる。
    """
    for elem in ifilter(pred, seq):
        return True
    return False

def no(seq, pred=None):
    u"""Returns True if pred(x) is false for every element in the iterable
    
    Python 2.5 で採用された組み込み関数 all を使って、
    not all(seq) と表現できるためレシピから削除されたものと思われる。
    あと、 pred の初期値は bool にしておかないと意図した動作にならない。
    """
    return True not in imap(pred, seq)

組み込み関数として存在する enumerate がレシピから消えないのはなぜなんだろう? Python 2.5 以前で start 引数を使えるようにするのに使えるけれど。

import sys
if sys.version_info < (2, 6):
    import itertools
    def enumerate(iterable, start=0):
        return itertools.izip(itertools.count(start), iterable)

レシピ以外の文章も有益

ドキュメントには、 itertools として提供されているものと等価である Pure Python コードが付属しているのでそれもあらためて読みなおす予定。 itertools は Python 2.6 で順列関連のイテレータが増えているが、これらは Python 2.5 のレシピには載っていないので、これも。