web-dev-qa-db-ja.com

「For」ループの最初の反復

最初のループの繰り返しでいくつかの関数を実行するエレガントなPythonの方法があるかどうかを問い合わせたいです。私が考えることができる唯一の可能性は次のとおりです。

first = True
for member in something.get():
    if first:
        root.copy(member)
        first = False
    else:
        somewhereElse.copy(member)
    foo(member)
62
Rince

Head-Tailデザインパターンにはいくつかの選択肢があります。

seq= something.get()
root.copy( seq[0] )
foo( seq[0] )
for member in seq[1:]:
    somewhereElse.copy(member)
    foo( member )

またはこれ

seq_iter= iter( something.get() )
head = seq_iter.next()
root.copy( head )
foo( head )
for member in seq_iter:
    somewhereElse.copy( member )
    foo( member )

「冗長なfoo(member)」コードのため、これはどういうわけか「DRY」ではないと人々は泣き言を言う。それはばかげた主張です。それが当てはまる場合、すべての関数は一度しか使用できません。参照を1つしか持てない場合、関数を定義する意味は何ですか?

37
S.Lott

このような何かが動作するはずです。

for i, member in enumerate(something.get()):
    if i == 0:
         # Do thing
    # Code for everything

ただし、コードを考えて、この方法で行う必要があるかどうかを確認することを強くお勧めします。これは、一種の「ダーティー」だからです。前もって特別な処理を必要とする要素を取得し、ループ内の他のすべての処理を定期的に実行する方が良いでしょう。

このようにしないとわかる唯一の理由は、ジェネレータ式から取得する大きなリスト(メモリに収まらないため、前もってフェッチしたくない)、または同様の状況です。

61
Daniel Bruce

どうですか:

my_array = something.get()
for member in my_array:
    if my_array.index(member) == 0:
        root.copy(member)
    else:
        somewhereElse.copy(member)
    foo(member)

または多分:

for index, member in enumerate(something.get()):
    if index == 0:
        root.copy(member)
    else:
        somewhereElse.copy(member)
    foo(member)

index-method のドキュメント。

11
dummy

私はこれは非常にエレガントだと思うが、それが何をするのか複雑すぎるかもしれない...

from itertools import chain, repeat, izip
for place, member in izip(chain([root], repeat(somewhereElse)), something.get()):
    place.copy(member)
    foo(member)
6
fortran

ここでは、 "pertty"に見えるPythonのイディオムを使用できます。おそらく、質問をするときにあなたが提案したフォームを使用するでしょうが、エレガントではありませんが、コードがより明確に残るようにするためです。

def copy_iter():
    yield root.copy
    while True:
        yield somewhereElse.copy

for member, copy in Zip(something.get(), copy_iter()):
    copy(member)
    foo(member)

(申し訳ありません-編集する前に最初に投稿したフォームは機能しませんでした。実際に「コピー」オブジェクトのイテレータを取得するのを忘れていました)

5
jsbueno

これは動作します:

for number, member in enumerate(something.get()):
    if not number:
        root.copy(member)
    else:
        somewhereElse.copy(member)
    foo(member)

ただし、ほとんどの場合、whatever[1:]を繰り返し処理し、ループの外側でルート処理を行うことをお勧めします。それは通常より読みやすいです。もちろん、ユースケースに依存します。

5
balpha

最初のS.Lottソリューションが最善だと思いますが、かなり最近のpython(> = 2.6バージョン)これにより、最初の要素と連続する要素に対して異なる処理を実行できます。また、1番目、2番目、3番目の要素に対して個別の操作を実行するように簡単に変更できます。

from itertools import izip_longest

seq = [1, 2, 3, 4, 5]

def headfunc(value):
    # do something
    print "1st value: %s" % value

def tailfunc(value):
    # do something else
    print "this is another value: %s" % value

def foo(value):
    print "perform this at ANY iteration."

for member, func in izip_longest(seq, [headfunc], fillvalue=tailfunc):
    func(member)
    foo(member)
3
Alan Franzoni

iterを使用して、最初の要素を消費するのはどうですか?

編集: OPの質問に戻ると、すべての要素に対して実行する共通操作があり、最初の要素に対して実行する操作と、残りの要素に対して実行する操作があります。

単一の関数呼び出しである場合は、2回だけ記述します。それは世界を終わらせません。より複雑な場合は、デコレータを使用して、「最初の」関数と「残りの」関数を共通の操作でラップできます。

def common(item):
    print "common (x**2):", item**2

def wrap_common(func):
    """Wraps `func` with a common operation"""
    def wrapped(item):
        func(item)
        common(item)
    return wrapped

@wrap_common
def first(item):
    """Performed on first item"""
    print "first:", item+2

@wrap_common
def rest(item):
    """Performed on rest of items"""
    print "rest:", item+5

items = iter(range(5))
first(items.next())

for item in items:
    rest(item)

出力:

first: 2
common (x**2): 0
rest: 6
common (x**2): 1
rest: 7
common (x**2): 4
rest: 8
common (x**2): 9
rest: 9
common (x**2): 16

または、スライスを行うことができます:

first(items[0])
for item in items[1:]:
    rest(item)
3
Ryan Ginstrom

Something.get()が何かを反復処理する場合は、次の方法でも実行できます。

root.copy(something.get())

for member in something.get():
  #  the rest of the loop
3
Eli Bendersky

私はPythonを知りませんが、あなたの例のほとんど正確なパターンを使用します。
私がやることは、if条件を最も頻繁にすることなので、通常はif( first == false )をチェックします
なぜ?長いループの場合、firstは1回だけtrueになり、他のすべての時間にfalseになります。つまり、最初のループを除くすべてのループで、プログラムは条件をチェックしてelse部分にジャンプします。
最初にfalseであるかどうかをチェックすることにより、else部分へのジャンプは1つだけになります。これで効率が上がるかどうかはわかりませんが、とにかく、内なるオタクと安心するためにそれを行います。

PS:はい、if部分に入ると、実行を継続するためにelseを飛び越えなければならないことを知っているので、おそらくそれを行う方法は役に立たないでしょうが、いい感じです。 :D

1
Petruza

ループの前にroot.copy(something.get())できませんか?

編集:申し訳ありませんが、2番目のビットを見逃しました。しかし、あなたは一般的なアイデアを得る。それ以外の場合は、0を列挙して確認しますか?

EDIT2:わかりました、愚かな2番目のアイデアを取り除きました。

1
Skilldrick

あなたの質問は矛盾しています。実際には、最初/後続のイテレーションで何か別のことを行うと言っているのに、「最初のイテレーションでのみ何かをする」と言います。これは私がそれを試みる方法です:

copyfn = root.copy
for member in something.get():
    copyfn(member)
    foo(member)
    copyfn = somewhereElse.copy
0
aaa90210