7

以下のコードがどのように機能するかを理解するのに苦労しています。これはhttp://docs.python.org/library/itertools.html#itertools.izip_longestからのもので、izip_longest イテレータに相当する純粋な Python です。私は特にセンチネル機能に戸惑っています。どのように機能しますか?

def izip_longest(*args, **kwds):
    # izip_longest('ABCD', 'xy', fillvalue='-') --> Ax By C- D-
    fillvalue = kwds.get('fillvalue')
    def sentinel(counter = ([fillvalue]*(len(args)-1)).pop):
        yield counter()         # yields the fillvalue, or raises IndexError
    fillers = repeat(fillvalue)
    iters = [chain(it, sentinel(), fillers) for it in args]
    try:
        for tup in izip(*iters):
            yield tup
    except IndexError:
        pass
4

3 に答える 3

6

わかりました、これを行うことができます。センチネルについて。この式([fillvalue]*(len(args)-1))は、 の反復可能オブジェクトごとに 1 つの埋め込み値argsから 1 を引いた値を含むリストを作成します。したがって、上記の例では['-']. 次に、そのリストcounterの機能が割り当てられます。それ自体は、反復ごとにそのリストから 1 つの項目をポップするジェネレーターです。返された各反復子を1 回だけ繰り返すことができ、常に yield になります。によって返されたすべてのイテレータによって生成されたアイテムの総数は です(それを明確にしてくれた Sven Marnach に感謝します。私はそれを誤解していました)。popsentinelsentinelfillvaluesentinellen(args) - 1

これをチェックしてください:

iters = [chain(it, sentinel(), fillers) for it in args]

それがコツです。itersの反復可能な各反復子を含むリストですargs。これらの各反復子は、次のことを行います。

  1. 対応する iterable from 内のすべてのアイテムを反復処理しargsます。
  2. センチネルを 1 回繰り返して、 を生成しfillvalueます。
  3. fillvalue永遠に繰り返す。

ここで、以前に確立されたようにlen(args)-1IndexError. イテラブルの 1 つが最も長いため、これで問題ありません。したがって、 が発生した時点で、IndexErrorで最も長い iterable の反復が終了したことを意味しargsます。

どういたしまして。

PS:これが理解できることを願っています。

于 2011-03-14T12:55:07.537 に答える
5

この関数sentinel()は、イテレータをfillvalue1回だけ返します。fillvalueによって返されるすべてのイテレータによって生成されるsの総数sentinel()は、に制限されます。n-1ここで、nはに渡されるイテレータの数ですizip_longest()。この数のsが使い果たされた後、によって返されるfillvalueイテレータをさらに反復すると、が発生します。sentinel()IndexError

この関数は、すべてのイテレータが使い果たされたかどうかを検出するために使用されます。各イテレータはchain()、によって返されるイテレータで編集されsentinel()ます。すべてのイテレータが使い果たされると、によって返されたイテレータが3回繰り返され、結果として、sentinel()が順番に終了します。nIndexErrorizip_longest()

これまでsentinel()、どのように機能するかではなく、何をするかを説明しました。がizip_longest()呼び出されると、の定義sentinel()が評価されます。sentinel()定義を評価している間、への呼び出しごとに1回、デフォルトの引数も評価されizip_longest()ます。コードはと同等です

fillvalue_list = [fillvalue] * (len(args)-1)
def sentinel():
    yield fillvalue_list.pop()

これを囲んでいるスコープの変数ではなくデフォルトの引数に格納することは、デフォルトの引数に含めることと同様に、単なる最適化です。.popこれは、によって返されるイテレータが繰り返されるたびに検索する手間が省けるためsentinel()です。

于 2011-03-14T12:58:14.347 に答える
2

の定義はsentinelとほぼ同等です

def sentinel():
    yield ([fillvalue] * (len(args) - 1)).pop()

popただし、バインドされたメソッド (関数オブジェクト) をデフォルトの引数として取得します。デフォルトの引数は関数定義時に評価されるため、 の呼び出しごとizip_longestに 1 回ではなく、 の呼び出しごとに 1 回評価されますsentinel。したがって、関数オブジェクトは[fillvalue] * (len(args) - 1)、呼び出しごとにこれを新たに構築するのではなく、リストを「記憶」します。

于 2011-03-14T12:55:31.533 に答える