Yineleyici değişkeni olmayan aralık döngüsü için bir Python uygulamak mümkün mü?


188

Şunlar olmadan aşağıdakileri yapmak mümkün mü i?

for i in range(some_number):
    # do something

Sadece bir şeyi N kez yapmak istiyorsanız ve yineleyiciye ihtiyacınız yoksa.


21
Bu iyi bir soru! PyDev 'i'yi' kullanılmayan değişken 'için bir uyarı olarak bile işaretler. Aşağıdaki çözüm bu uyarıyı kaldırır.
Ashwin Nanjappa

@Ashwin Bu uyarıyı kaldırmak için \ @UnusedVariable kullanabilirsiniz. Bu yorumun geçmesi için 'at' sembolünden kaçmam gerektiğini unutmayın.
Raffi Khatchadourian

Ben de aynı soruya yöneliyorum. Pylint uyarılarıyla sinir bozucu. Elbette, @Raffi Khatchadourian'ın önerdiği gibi ek bir baskı ile uyarıları devre dışı bırakabilirsiniz. Pylint uyarılarından ve baskılama yorumlarından kaçınmak güzel olurdu .
tangoal

Yanıtlar:


111

Kafamın üstünden, hayır.

Bence yapabileceğiniz en iyi şey şöyle:

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

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

Ama bence ekstra ideğişkenle yaşayabilirsin .

İşte _gerçekte sadece başka bir değişken olan değişkeni kullanma seçeneği .

for _ in range(n):
    do_something()

_Etkileşimli bir python oturumunda döndürülen son sonucun atandığını unutmayın :

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

Bu nedenle, bu şekilde kullanmam. Ryan'ın belirttiği gibi bir deyimin farkında değilim. Tercümanı bozabilir.

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

Python dilbilgisine göre , kabul edilebilir bir değişken adıdır:

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

4
"Ama sanırım ekstra" i "ile yaşayabilirsin" Evet, bu sadece akademik bir nokta.
James McMahon

1
@ nemo, (n) aralığında _ için yapmayı deneyebilirsiniz: alfasayısal adlar kullanmak istemiyorsanız.
Bilinmiyor

_ Bu durumda bir değişken midir? Yoksa Python'da başka bir şey mi?
James McMahon

1
@nemo Evet, kabul edilebilir bir değişken adı. Yorumlayıcıda, otomatik olarak yaptığınız son ifadeye atanır.
Bilinmiyor

3
@kurczak Bir nokta var. Kullanılması _, dikkate alınmaması gerektiğini netleştirir. Bunu yapmanın bir anlamı olmadığını söylemek, kodunuzu yorumlamanın bir anlamı olmadığını söylemek gibidir - çünkü yine de aynısını yapar.
Lambda Peri

69

Arıyor olabilirsiniz

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

bu timesPython kez tekrarlamak için en hızlı yoldur .


2
Performansla ilgilenmiyordum, sadece ifadeyi yazmak için ters bir yol olup olmadığını merak ettim. Python'u yaklaşık 2 yıldır sporadik olarak kullanırken, hala eksik olduğum çok şey olduğunu hissediyorum. Itertools bunlardan biri, bilgi için teşekkür ederim.
James McMahon

5
Bu ilginç, bunun farkında değildim. Sadece itertools belgelerine bir göz attım; ama merak ediyorum neden bu sadece range veya xrange kullanmaktan daha hızlı?
si28719e

5
@blackkettle: daha hızlıdır, çünkü xrange maliyetinin ölçülebilir bir parçası olan geçerli yineleme dizinini (ve liste değil yineleyici veren Python 3 aralığını) döndürmesi gerekmez. @nemo, aralık olabildiğince optimize edilmiştir, ancak bir liste oluşturup geri döndürmek, bir yineleyiciden kaçınılmaz olarak daha ağır bir iştir (Py3'te, aralık Py2'nin xrange'i gibi bir yineleyici döndürür; geriye dönük uyumluluk böyle bir değişikliğe izin vermez Py2'de), özellikle de değişken bir değer döndürmesi gerekmeyen biri.
Alex Martelli

4
@Cristian, evet, her seferinde açıkça bir Python int hazırlıyor ve iade ediyor, inc. gc iş, ölçülebilir bir maliyeti var - dahili bir sayaç kullanmak önemli değil.
Alex Martelli

4
Şimdi anlıyorum. Fark, "algoritma" dan değil GC yükünden gelir. Bu arada, hızlı koşmak sürümüyle gelen timeit kriter ve hızlanma ~ 1.42 g idi.
Cristian Ciupitu

59

Kullanılmayan bir değere atama için genel deyim, onu adlandırmaktır _.

for _ in range(times):
    do_stuff()

18

_'İ kullanmanızı öneren herkesin söylemediği şey, _'nin gettext işlevlerinden birine kısayol olarak kullanılmasıdır , bu nedenle yazılımınızın birden fazla dilde kullanılabilir olmasını istiyorsanız, bunu kullanmaktan kaçınmalısınız. başka amaçlar için.

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

Bana göre bu kullanım _korkunç bir fikir gibi gözüküyor, onunla çelişmem.
KeithWM

9

İşte veri modelini ( Py3 bağlantısı ) kullanan (kötüye kullanma?) Rastgele bir fikir .

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

Standart kütüphanelerde böyle bir şey var mı acaba?


10
__nonzero__Yan etkiler gibi bir yönteme sahip olmak korkunç bir fikir.
ThiefMaster

2
Onun __call__yerine kullanırdım. while x():yazmak o kadar zor değil.
Jasmijn

1
Aynı zamanda isimden kaçınmak için bir argüman var Counter; elbette, ayrılmış değil veya yerleşik kapsamda, ama collections.Counterbir şeydir ve aynı isimde bir sınıf yapmak sürdürücü kafa karışıklığı riskine neden olur (bu zaten bunu riske atmaz).
ShadowRanger

7

Gettext ile ad çakışmasını önlemek için _11 (veya herhangi bir sayı veya başka bir geçersiz tanımlayıcı) kullanabilirsiniz. Alt çizgi + geçersiz tanımlayıcıyı her kullandığınızda, döngü için kullanılabilecek bir sahte ad alırsınız.


Güzel! PyDev sizinle aynı fikirde: Bu "Kullanılmayan değişken" sarı uyarısından kurtulur.
mike kemirgen

2

Yanıt, yineleyiciyi kullanmayla ilgili yaşadığınız soruna bağlı olabilir mi? olabilir

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

veya

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

loop(100, lambda a:a)

ama açıkçası böyle yaklaşımları kullanmanın bir anlamı yok


1
Not: Python (kesinlikle en azından CPython referans yorumlayıcısı değil, muhtemelen diğerlerinin çoğu değil) kuyruk yinelemesini optimize etmez, bu nedenle N değerinin mahallesindeki bir şeyle sınırlı olacaktır sys.getrecursionlimit()(bu, düşük dörde bir yerde varsayılan değerdir) basamaklı aralık (CPython); kullanmak sys.setrecursionlimitsınırı yükseltir, ancak sonunda C yığını sınırına ulaşırsınız ve yorumlayıcı bir yığın taşmasıyla ölür (sadece iyi bir RuntimeError/ değil RecursionError).
ShadowRanger


1

Gereksiz bir sayaç yerine, artık gereksiz bir listeniz var. En iyi çözüm, sözdizimi denetleyicilerine değişkeni kullanmadığınızı bildiğinizi söyleyen "_" ile başlayan bir değişken kullanmaktır.

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

0

Genellikle yukarıda verilen çözümlere katılıyorum. Yani:

  1. İçinde alt çizgi kullanma for-Loop'ta (2 ve daha fazla satır)
  2. Normal tanımlama while sayaç (3 ve daha fazla satır)
  3. __nonzero__Uygulama ile özel bir sınıf bildirme (çok daha fazla satır)

Biri bir nesneyi # 3'teki gibi tanımlayacaksa , anahtar kelimeyle protokolü uygulamanızı veya contextlib'i uygulamanızı öneririm .

Ayrıca başka bir çözüm öneriyorum. 3 astarlıdır ve üstün zerafet değildir, ancak itertools paketini kullanır ve bu nedenle ilgi çekici olabilir.

from itertools import (chain, repeat)

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

Bu örnekte 2 , döngüyü yineleme sayısıdır. zincir iki tekrar yineleyiciyi sarıyor, ilki sınırlı ama ikincisi sonsuz. Bunların gerçek yineleyici nesneler olduğunu unutmayın, bu nedenle sonsuz bellek gerektirmezler. Açıkçası bu çözüm # 1'den çok daha yavaştır . Bir fonksiyonun parçası olarak yazılmadığı sürece times değişkeni için temizleme gerekebilir .


2
chaingereksizdir, times = repeat(True, 2); while next(times, False):aynı şeyi yapar.
AChampion

0

Aşağıdakilerle biraz eğlendik, paylaşmak ilginç:

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()

Sonuçlar:

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

0

Eğer do_somethingbasit bir fonksiyonudur veya birine basit sarılmış olabilir map()kutu do_something range(some_number)süreleri:

# 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)

Argümanları iletmek do_somethingistiyorsanız, itertools repeatfunctarifinin iyi okuduğunu da görebilirsiniz:

Aynı argümanları iletmek için:

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)

Farklı argümanlar iletmek için:

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

deque(starmap(do_something, argses), 0)

-1

Eğer varsa , gerçekten bir isim ile bir şeyler koyarak (bir yineleme OP olarak değişken veya istenmeyen listesi veya zamanın gerçek istediği miktarda dönen istenmeyen jeneratör ya) Eğer gerçekten istiyorsa bunu yapabileceğini kaçınmak istiyorum:

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

Kullanılan hile, type('', (), {})boş ada sahip bir sınıfla sonuçlanan anonim bir sınıf oluşturmaktır , ancak yerel veya global ad alanına eklenmediğini (boş olmayan bir ad sağlansa bile). Daha sonra bu sınıfın bir üyesini, üyesi olduğu sınıfa erişilemediğinden erişilemeyen yineleme değişkeni olarak kullanırsınız.


Açıkçası bu kasıtlı olarak patolojiktir, bu yüzden eleştirmek noktanın yanındadır, ancak burada ek bir tuzağa düşeceğim. CPython'da, referans yorumlayıcısı olan sınıf tanımları doğal olarak döngüseldir (sınıf oluşturmak kaçınılmaz olarak referans sayımına dayalı olarak sınıfın deterministik olarak temizlenmesini önleyen bir referans döngüsü oluşturur). Bu, sınıfı devreye almak ve temizlemek için döngüsel GC'yi beklediğiniz anlamına gelir. Genellikle varsayılan olarak sık sık toplanan daha genç neslin bir parçası olarak toplanır, ancak yine de her döngü, deterministik olmayan ömür boyu ~ 1.5 KB çöp anlamına gelir.
ShadowRanger

Temel olarak, her döngüde (tipik olarak) deterministik olarak temizlenecek (geri döndüğünde ve eski değer temizlendiğinde) adlandırılmış bir değişkenden kaçınmak için, deterministik olmayan bir şekilde temizlenen ve kolayca belirlenebilen büyük bir adsız değişken oluşturursunuz daha uzun sürer.
ShadowRanger


-7

Ne dersin:

while range(some_number):
    #do something

3
Durum range(some_number)her zaman doğrudur , bu sonsuz bir döngü !
ölümcül

@deadly: Eh, some_numberküçük veya eşitse 0, sonsuz değildir, asla çalışmaz. :-) Ve sonsuz bir döngü için (özellikle Py2'de) oldukça verimsizdir, çünkü her test için taze list(Py2) veya rangenesne (Py3) oluşturur (tercümanın bakış açısından sabit değildir, yüklemesi gerekir rangeve some_numberher döngüyü arayın range, ardından sonucu test edin).
ShadowRanger
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.