インデックス変数なしでN回何かを行うためのpythonの方法?


161

毎日Pythonがますます好きになっています。

今日、私は次のようなコードを書いていました:

for i in xrange(N):
    do_something()

私は何かN回しなければならなかった。しかし、毎回i(インデックス変数)の値に依存しませんでした。自分が使用したことのない変数(i)を作成していることに気付き、「無用なインデックス変数を必要とせずに、これを行うにはより確実にPython的な方法があるはずです。」

だから...問題は、この単純なタスクをより(pythonic)より美しい方法で行う方法を知っていますか?


7
_変数について学習しましたが、それ以外の場合は、Pythonicの実行方法を検討します。少なくともPythonでは、他の方法で単純なforループを実行したことはありません。あなたがそれを見て、「待って、それはひどいように見える」と言う特定のユースケースがあると私は確信していますが、一般に、xrangeが(私が見た限り)優先される方法です。
ウェインヴェルナー、2010年


5
注:xrangeはPython3には存在しません。range代わりに使用してください。
John Henckel、2016

回答:


110

ループするよりも少し速いアプローチxrange(N)は次のとおりです。

import itertools

for _ in itertools.repeat(None, N):
    do_something()

3
どれくらい速く?Python 3.1でもまだ違いはありますか?
Hamish Grubijan

15
@ハミッシュ:2.6での私のテストでは、32%速くなっています(N = 1000では23.2 us対17.6 us)しかし、それはとにかく本当に時間です。OPのコードは(私にとっては)すぐに読めるので、デフォルトで使用します。
マイクボーアーズ

3
速度について知っておくと良いでしょう。私は間違いなく、OPのコードがより読みやすくなるというマイクの感情を反映しています。
Wayne Werner

@ウェイン、私は習慣が本当に非常に強力だと思います-それに慣れているという事実を除いて、なぜ他の人はこのカウントを実行するたびに「0からN-1までカウントアップします[そしてカウントを完全に無視します]]。 -独立した操作」は、本質的に「次の操作をN回繰り返す」よりも明確です...?
Alex Martelli、

4
速度は本当に適切ですか?そのループで重要なことを行うと、選択した反復スタイルと同じくらいの時間が数百または数千時間かかる可能性が高いのではないでしょうか。
ヘニング2014

55

たとえば、この質問をしたときに学んだように、_変数を使用します。

# A long way to do integer exponentiation
num = 2
power = 3
product = 1
for _ in xrange(power):
    product *= num
print product

6
反対投票者ではなく、回答に詳細を追加する代わりに別の投稿を参照しているためかもしれません
Downgoat

5
@Downgoat:フィードバックをありがとう。とはいえ、このイディオムについて言うことはそれほど多くありません。別の投稿を参照する際の私のポイントは、検索によって答えが得られた可能性があることを指摘することでした。この質問には、他の質問の数倍の賛成票があることは皮肉なことです。
GreenMatt


10

関数は一流の市民なので、小さなラッパーを書くことができます(Alex Answersから)

def repeat(f, N):
    for _ in itertools.repeat(None, N): f()

次に、関数を引数として渡すことができます。


@ハミッシュ:ほとんど何もない。(Alexの回答のタイミングと同じ条件下で、ループごとに17.8 us、差は0.2 us)。
Mike Boers

9

_はxと同じです。ただし、これは、使用する予定のない識別子を示すために使用されるpythonイディオムです。Pythonでは、これらの識別子は、他の言語で変数が行うように、メモを取ったりスペースを割り当てたりしません。それは忘れがちです。それらはオブジェクトを指す名前であり、この場合は各反復で整数です。


8

さまざまな答えが非常にエレガント(特にAlex Martelliのもの)であることがわかりましたが、パフォーマンスを直接数値化したかったので、次のスクリプトを作成しました。

from itertools import repeat
N = 10000000

def payload(a):
    pass

def standard(N):
    for x in range(N):
        payload(None)

def underscore(N):
    for _ in range(N):
        payload(None)

def loopiter(N):
    for _ in repeat(None, N):
        payload(None)

def loopiter2(N):
    for _ in map(payload, repeat(None, N)):
        pass

if __name__ == '__main__':
    import timeit
    print("standard: ",timeit.timeit("standard({})".format(N),
        setup="from __main__ import standard", number=1))
    print("underscore: ",timeit.timeit("underscore({})".format(N),
        setup="from __main__ import underscore", number=1))
    print("loopiter: ",timeit.timeit("loopiter({})".format(N),
        setup="from __main__ import loopiter", number=1))
    print("loopiter2: ",timeit.timeit("loopiter2({})".format(N),
        setup="from __main__ import loopiter2", number=1))

Martelliのソリューションに基づいて構築されmap()、ペイロード関数を呼び出すために使用する代替ソリューションも思いつきました。OK、ペイロードを破棄するパラメータを自由に受け入れるようにしたので、少し騙しました。これを回避する方法があるかどうかわかりません。それにもかかわらず、ここに結果があります:

standard:  0.8398549720004667
underscore:  0.8413165839992871
loopiter:  0.7110594899968419
loopiter2:  0.5891903560004721

したがって、mapを使用すると、標準のforループよりも約30%、Martelliの場合よりも19%の改善が得られます。


4

do_somethingを関数として定義し、それをN回実行するとします。多分あなたは以下を試すことができます:

todos = [do_something] * N  
for doit in todos:  
    doit()

45
承知しました。関数を100万回呼び出すだけでなく、100万項目のリストも割り当てましょう。CPUが動作している場合、メモリにも少しストレスがかかりませんか?答えは明確に「役に立たない」とは特徴付けられない(それは異なる機能的なアプローチを示している)ため、反対票を投じることはできませんが、私は反対し、完全に反対します。
tzot

1
同じ関数値へのN個の参照のリストではないですか?
Nick McCurdy 2016年

fn() for fn in itertools.repeat(do_something, N)配列を事前に生成して保存する方が良いです...これは私の好みのイディオムです。
F1Rumors 2016年

1
@tzotなぜ降順の調子ですか?この人は答えを書くことに力を入れており、今は将来貢献するのをやめられるかもしれません。パフォーマンスに影響がある場合でも、これは有効なオプションであり、特にNが小さい場合、パフォーマンス/メモリへの影響は重要ではありません。
davidscolgan、2018年

パフォーマンスにこだわったPython開発者のパフォーマンスにいつも驚かされます:)私はそれが慣用的ではないことに同意しますが、Pythonを初めて読む人は、単にイテレータを使用する場合と同じように何が起こっているのかを理解できないかもしれません
Asfand Qazi

1

単純なwhileループはどうですか?

while times > 0:
    do_something()
    times -= 1

すでに変数があります。なぜそれを使わないのですか?


1
私の唯一の考えは、3行のコードと1行(?)のコードであるということです
AJP

2
@AJP-4行と2行のように
ArtOfWarfare 2013年

オーバーヘッド(比較-時間> 0)と減少(時間-= 1)をオーバーヘッドに
追加

@ F1Rumorsはそれを測定していませんが、PyPyのようなJITコンパイラーがそのような単純なwhileループに対してより遅いコードを生成するべきであるなら、私は驚くでしょう。
PhilippClaßen18年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.