イテレータ変数なしでPython for rangeループを実装することは可能ですか?


187

なしでフォローすることは可能iですか?

for i in range(some_number):
    # do something

何かをN回行いたいだけで、イテレータは必要ない場合。


21
これは良い質問です!PyDevは、「i」に「未使用の変数」の警告としてフラグを立てます。以下の解決策はこの警告を削除します。
アシュウィンナンジャッパ

@Ashwin \ @UnusedVariableを使用して、その警告を削除できます。このコメントを通過させるには、アットマークをエスケープする必要があることに注意してください。
Raffi Khatchadourian、2011年

私はあなたに同じ質問をします。これは、pylint警告に悩まされます。もちろん、@ Raffi Khatchadourianが提案するような追加の抑制によって警告を無効にすることができます。パイリントの警告抑制コメントを避けたほうがいいでしょう。
タンゴアル

回答:


110

頭のてっぺんからだよ。

あなたができる最善のことは次のようなものだと思います:

def loop(f,n):
    for i in xrange(n): f()

loop(lambda: <insert expression here>, 5)

しかし、私はあなたが余分なi変数で生きることができると思います。

_これが変数を使用するオプションです。これは実際には別の変数です。

for _ in range(n):
    do_something()

_インタラクティブpythonセッションで返された最後の結果が割り当てられることに注意してください。

>>> 1+2
3
>>> _
3

このため、この方法では使用しません。私はライアンが述べたような慣用句を知りません。それはあなたの通訳を台無しにすることができます。

>>> for _ in xrange(10): pass
...
>>> _
9
>>> 1+2
3
>>> _
9

そして、Python grammarによれば、これは受け入れ可能な変数名です:

identifier ::= (letter|"_") (letter | digit | "_")*

4
「しかし、私はあなたが余分な「i」と一緒に暮らすことができると思います。ええ、それは単なる学術的なポイントです。
James McMahon、

1
@ nemo、range(n)で_を試すことができます。英数字の名前を使用したくない場合。
不明

その場合、_は変数ですか?それともPythonの別のことですか?
James McMahon、

1
@nemoはい、許容できる変数名です。インタプリタでは、最後に作成した式が自動的に割り当てられます。
不明

3
@kurczakポイントがあります。を使用_すると、無視する必要があることが明確になります。これを行う意味がないと言うことは、コードをコメントする意味がないと言っているようなものです。
Lambda Fairy

69

あなたが探している可能性があります

for _ in itertools.repeat(None, times): ...

これはtimes、Pythonで時間を反復する最も速い方法です。


2
私はパフォーマンスには関心がなかったので、ステートメントを簡潔にする方法があるかどうかだけに興味を持っていました。Pythonを散発的に約2年間使用してきましたが、今でもまだ足りないことがたくさんあると感じています。Itertoolsはそれらの1つです。情報をありがとうございます。
James McMahon、

5
それは興味深いです、私はそれを知りませんでした。私はitertoolsのドキュメントを見ただけです。しかし、なぜこれがrangeやxrangeを使用するよりも速いのでしょうか?
si28719e 2009年

5
@blackkettle:現在の反復インデックスを返す必要がないため、処理が高速です。これは、xrange(およびPython 3の範囲であり、リストではなくイテレータを提供します)のコストの測定可能な部分です。@nemo、範囲はできる限り最適化されていますが、リストを作成して返す必要があることは、イテレータよりも必然的に重い作業です(Py3では、範囲はPy2のxrangeのようなイテレータを返します。後方互換性のため、このような変更は許可されません。 Py2)では、特に変化する値を返す必要がないものです。
Alex Martelli、

4
@Cristian、はい、毎回Python intを明確に準備して返す。gcは機能しますが、測定可能なコストがあります- 内部でカウンターを使用することは問題ではありません。
Alex Martelli

4
今、私は分かる。違いは、「アルゴリズム」ではなく、GCのオーバーヘッドに由来します。ちなみに、私は簡単なtimeitベンチマークを実行し、スピードアップは〜1.42xでした。
クリスティアンCiupitu 2009

59

使用されない値に割り当てるための一般的な慣用法は、名前を付けること_です。

for _ in range(times):
    do_stuff()

18

何を使用するためにあなたを示唆みんな_は_が頻繁の1へのショートカットとして使用されていることであると言っていないのgettextあなたはそれを使用して避けてオフにしている最高のそのようにしたい場合は、あなたのソフトウェアは複数の言語で利用できるようにするために、機能他の目的のため。

import gettext
gettext.bindtextdomain('myapplication', '/path/to/my/language/directory')
gettext.textdomain('myapplication')
_ = gettext.gettext
# ...
print _('This is a translatable string.')

私にとって、この使用法は_ひどい考えのように思えますが、それと矛盾することはありません。
KeithWM

9

データモデルPy3リンク)を利用する(乱用する)ランダムなアイデアを次に示します。

class Counter(object):
    def __init__(self, val):
        self.val = val

    def __nonzero__(self):
        self.val -= 1
        return self.val >= 0
    __bool__ = __nonzero__  # Alias to Py3 name to make code work unchanged on Py2 and Py3

x = Counter(5)
while x:
    # Do something
    pass

標準ライブラリにこのようなものがあるのだろうか?


10
__nonzero__副作用のあるようなやり方は恐ろしい考えだと思います。
ThiefMaster 2012

2
__call__代わりに使用します。while x():書くのはそれほど難しくありません。
Jasmijn

1
名前を回避するための引数もありますCounter。確かに、それは予約されていないか、組み込みのスコープでcollections.Counterはありませんが、同じ名前のクラスを作成すると、メンテナの混乱を招く可能性があります(これが既に危険を冒しているわけではありません)。
ShadowRanger、

7

_11(または任意の数または別の無効な識別子)を使用して、gettextでの名前の衝突を防ぐことができます。アンダースコア+無効な識別子を使用すると、forループで使用できるダミーの名前が取得されます。


いいね!PyDevはあなたに同意します。これにより、「未使用の変数」という黄色の警告が解消されます。
マイクげっ歯類

2

答えは、イテレータの使用でどのような問題があるかによって異なりますか?使用されるかもしれません

i = 100
while i:
    print i
    i-=1

または

def loop(N, doSomething):
    if not N:
        return
    print doSomething(N)
    loop(N-1, doSomething)

loop(100, lambda a:a)

しかし率直に言って、私はそのようなアプローチを使用する意味がありません


1
注:Python(確かにCPythonリファレンスインタープリターではなく、おそらく他のほとんどではない)は末尾再帰を最適化しないため、Nはの値の近くにあるものに制限されますsys.getrecursionlimit()(デフォルトでは下位4 CPythonの桁範囲); を使用sys.setrecursionlimitすると制限が引き上げられますが、最終的にはCスタックの制限に達し、インタープリターはスタックオーバーフローで停止します(単にnice RuntimeError/ を上げるだけではありませんRecursionError)。
ShadowRanger


1

不必要なカウンターの代わりに、今あなたは不必要なリストを持っています。最善の解決策は、「_」で始まる変数を使用することです。これは、変数を使用していないことを構文チェッカーに通知します。

x = range(5)
while x:
  x.pop()
  print "Work!"

0

私は一般的に上記の解決策に同意します。つまり:

  1. for-loopでのアンダースコアの使用(2行以上)
  2. 通常のwhileカウンターの定義(3行以上)
  3. __nonzero__実装のあるカスタムクラスの宣言(多くの行)

#3のようにオブジェクトを定義する場合キーワードを使用しプロトコルを実装するか、contextlibを適用することをお勧めします。

さらに、私はさらに別の解決策を提案します。それは3ライナーであり、最高の優雅さではありませんが、itertoolsパッケージを使用しているため、興味深いかもしれません。

from itertools import (chain, repeat)

times = chain(repeat(True, 2), repeat(False))
while next(times):
    print 'do stuff!'

これらの例では、2はループを繰り返す回数です。chainは2つの反復反復子をラップしています。最初の反復子は制限されていますが、2番目は無限です。これらは真のイテレータオブジェクトであるため、無限のメモリを必要としないことに注意してください。明らかに、これはソリューション#1よりはるかに遅いです。関数の一部として書かれていない限り、時間変数のクリーンアップが必要になる場合があります。


2
chain不要です、times = repeat(True, 2); while next(times, False):同じことをします。
AChampion、2015年

0

私たちは次のことを楽しんでいます。

class RepeatFunction:
    def __init__(self,n=1): self.n = n
    def __call__(self,Func):
        for i in xrange(self.n):
            Func()
        return Func


#----usage
k = 0

@RepeatFunction(7)                       #decorator for repeating function
def Job():
    global k
    print k
    k += 1

print '---------'
Job()

結果:

0
1
2
3
4
5
6
---------
7

0

場合はdo_something、単純な機能であるまたは1つ、シンプルでラップできるmap()do_something range(some_number)回:

# Py2 version - map is eager, so it can be used alone
map(do_something, xrange(some_number))

# Py3 version - map is lazy, so it must be consumed to do the work at all;
# wrapping in list() would be equivalent to Py2, but if you don't use the return
# value, it's wastefully creating a temporary, possibly huge, list of junk.
# collections.deque with maxlen 0 can efficiently run a generator to exhaustion without
# storing any of the results; the itertools consume recipe uses it for that purpose.
from collections import deque

deque(map(do_something, range(some_number)), 0)

に引数を渡したい場合do_somethingは、itertools レシピを見つけることもできますrepeatfunc

同じ引数を渡すには:

from collections import deque
from itertools import repeat, starmap

args = (..., my args here, ...)

# Same as Py3 map above, you must consume starmap (it's a lazy generator, even on Py2)
deque(starmap(do_something, repeat(args, some_number)), 0)

異なる引数を渡すには:

argses = [(1, 2), (3, 4), ...]

deque(starmap(do_something, argses), 0)

-1

あなたは場合本当にあなたが本当に望んでいた場合、あなたがそれを行うことができます(反復OPのように、変数、もしくは不要なリストまたは不要な発電機どちらかが時間の本当の希望額を返す)名前で何かを入れないようにしたいです:

for type('', (), {}).x in range(somenumber):
    dosomething()

使用されるトリックは、type('', (), {})名前が空のクラスになる匿名クラスを作成することですが、ローカルまたはグローバルの名前空間に挿入されないことに注意してください(空でない名前が指定されている場合でも)。次に、そのクラスのメンバーを、それがメンバーであるクラスに到達できないため到達できない反復変数として使用します。


明らかにこれは意図的に病的なものであるため、批判することは要点を外していますが、ここで追加の落とし穴に注意します。CPython(参照インタープリター)では、クラス定義は自然に循環します(クラスを作成すると、参照カウントに基づくクラスの確定的なクリーンアップを防ぐ参照サイクルが不可避に作成されます)。つまり、クラスを開始してクリーンアップするためのサイクリックGCを待機しています。これは通常、若い世代の一部として収集されます。デフォルトでは頻繁に収集されますが、それでも、各ループは最大1.5 KBのガベージと非確定的ライフタイムを意味します。
ShadowRangerは

基本的に、各ループで(通常は)確定的にクリーンアップされる名前付き変数(リバウンドで古い値がクリーンアップされる)を回避するために、非確定的にクリーンアップされる無名の巨大な変数を作成します。長続きする。
ShadowRangerは


-7

何について:

while range(some_number):
    #do something

3
条件range(some_number)が常に真であるため、これは無限ループです。
致命的な

@deadly:まあ、some_number以下の場合0、無限ではなく、実行されることはありません。:-)そして、それは無限ループ(特にPy2)に対しては非効率的です。これは、各テストに対して新しいlist(Py2)またはrangeオブジェクト(Py3)を作成するためです(インタープリターの観点からは定数ではないため、ロードrangeしてsome_numberすべてのループ、を呼び出しrange、結果をテストします)。
ShadowRangerは
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.