bir dizin değişkeni olmadan N kez bir şey yapmak için pythonic yolu?


161

Her gün python'u daha çok seviyorum.

Bugün, gibi bir kod yazıyordum:

for i in xrange(N):
    do_something()

N kere bir şeyler yapmak zorunda kaldım. Ancak her seferinde i(index değişkeni) değerine bağlı değildi . Hiç kullanmadığım bir değişken yarattığımı fark ettim ( i) ve "Bu işe yaramaz indeks değişkenine ihtiyaç duymadan bunu yapmanın daha pitonik bir yolu var." Diye düşündüm.

Yani ... soru şu: bu basit görevi daha güzel (pitonik) nasıl yapacağınızı biliyor musunuz?


7
_ Değişkenini yeni öğrendim, aksi halde Pythonic'i nasıl yaptığınızı düşünürdüm. En azından python'da başka bir yol için basit bir döngü gördüğümü sanmıyorum. Her ne kadar ona bakıp "Bekleyin, korkunç görünüyor" diyebileceğiniz özel kullanım durumları olduğundan eminim - ancak genel olarak, xrange (gördüğüm kadarıyla) tercih edilen yoldur.
Wayne Werner


5
Not: xrange Python3 içinde yok. rangeBunun yerine kullanın .
John Henckel

Yanıtlar:


110

Döngüden biraz daha hızlı bir yaklaşım xrange(N):

import itertools

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

3
Ne kadar hızlı? Python 3.1'de hala bir fark var mı?
Hamish Grubijan

15
@Hamish: 2.6 ile yaptığım test% 32 daha hızlı diyor (N = 1000 için 23.2 bize vs 17.6 bize). Ama bu gerçekten de çok zaman. (Bana göre) hemen okunabilir çünkü ben OP kodunu varsayılan.
Mike Boers

3
Hızı bilmek güzel. Mike'ın OP'nin kodunun daha okunabilir olması konusundaki görüşlerini kesinlikle tekrarlıyorum.
Wayne Werner

@Wayne, sanırım alışkanlık gerçekten çok güçlü - buna alışkın olmanız dışında, neden bu sayıyı her seferinde "0'dan N-1'e [[ve sayıyı tamamen görmezden gelinir]? bağımsız çalışma "özünde" aşağıdaki işlemi N kez tekrarla "dan daha net olabilir ...?
Alex Martelli

4
hızın gerçekten alakalı olduğundan emin misiniz? Bu döngüde önemli bir şey yaparsanız, seçtiğiniz yineleme tarzı kadar yüzlerce veya binlerce zaman alacaktır.
Henning

55

Bu soruyu sorduğumda öğrendiğim gibi _ değişkenini kullanın, örneğin:

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

6
Downvoter değil ama bunun cevabına daha fazla ayrıntı eklemek yerine başka bir gönderiye atıfta bulunmanız olabilir
Downgoat

5
@Downgoat: Geri bildiriminiz için teşekkürler. Bununla birlikte, bu deyim hakkında söylenecek çok şey yok. Başka bir gönderiye atıfta bulunmamın anlamı, bir aramanın yanıtı üretmiş olabileceğini göstermekti. Bu sorunun diğer soruların birkaç katına sahip olduğunu ironik buluyorum.
GreenMatt

39

Sadece kullanıyorum for _ in range(n), doğrudan konuya değindim. Python 2'de büyük sayılar için tüm listeyi oluşturacak, ancak Python 3 kullanıyorsanız sorun değil.


10

işlev birinci sınıf vatandaş olduğundan, küçük bir paket yazabilirsiniz (Alex cevaplarından)

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

işlevi bağımsız değişken olarak iletebilirsiniz.


@Hamish: Neredeyse hiçbir şey. (0,2 ABD'lik bir fark için Alex'in cevabının zamanlamasıyla aynı koşullar altında döngü başına 17,8 ABD).
Mike Boers

9

_, X ile aynı şeydir. Ancak, kullanmak istemediğiniz bir tanımlayıcıyı belirtmek için kullanılan bir python deyimi. Python'da bu tanımlayıcılar, diğer dillerdeki değişkenler gibi bellek almaz veya yer ayırmaz. Bunu unutmak kolay. Bunlar sadece nesnelere işaret eden isimler, bu durumda her yinelemede bir tamsayı.


8

Çeşitli cevapları gerçekten zarif buldum (özellikle Alex Martelli'nin) ama performansı ilk elden ölçmek istedim, bu yüzden aşağıdaki senaryoyu pişirdim:

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

Ayrıca Martelli'nin çözümüne dayanan map()ve faydalı yük fonksiyonunu çağırmak için kullanılan alternatif bir çözüm buldum . Tamam, yükü atılan bir parametreyi kabul etme özgürlüğünü aldığım için biraz hile yaptım: Bunun bir yolu olup olmadığını bilmiyorum. Bununla birlikte, sonuçlar şunlardır:

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

bu nedenle harita kullanmak döngü standardı üzerinde yaklaşık% 30 ve Martelli'lere göre% 19 daha fazla iyileşme sağlar.


4

Do_something öğesini bir işlev olarak tanımladığınızı ve bunu N kez gerçekleştirmek istediğinizi varsayın . Belki aşağıdakileri deneyebilirsiniz:

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

45
Elbette. Fonksiyonu sadece milyonlarca kez çağırmayalım, bir milyon öğenin listesini de tahsis edelim. CPU çalışıyorsa, bellek de biraz gerilmemeli mi? Cevap kesinlikle “yararlı değil” (farklı, işlevsel bir yaklaşım gösteriyor) olarak nitelendirilemez, bu yüzden aşağı indiremem, ama katılmıyorum ve buna tamamen karşıyım.
tzot

1
Aynı işlev değerine ilişkin N referanslarının bir listesi değil mi?
Nick McCurdy

fn() for fn in itertools.repeat(do_something, N)dizi oluşturmak ve kaydetmek için daha iyi ... bu benim tercih edilen deyim.
F1Rumors

1
@tzot Neden küçümseyen ton? Bu kişi bir cevap yazmak için çaba harcadı ve şimdi geleceğe katkıda bulunmaktan vazgeçebilir. Performans sonuçları olsa bile, bu bir çalışma seçeneğidir ve özellikle N küçükse performans / bellek sonuçları önemli değildir.
davidscolgan

Performansın Python geliştiricilerinin takıntılı olmasına her zaman şaşırdım :) İdiyomatik olmadığını kabul etsem de ve Python'da yeni okuyan biri, sadece bir yineleyici kullanırken olduğu gibi net bir şekilde neler olduğunu anlamayabilir
Asfand Qazi

1

Basit bir while döngüsü ne olacak?

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

Değişken zaten var; neden kullanmıyorsun?


1
Benim tek düşünmek 3 satır kod karşısında bir (?)
AJP

2
@AJP - Daha fazla 4 satır vs 2 satır
ArtOfWarfare

ek
yüklere

@ F1Rumors Ölçmedim, ama PyPy gibi JIT derleyicilerinin böyle basit bir while döngüsü için daha yavaş kod üretmesi gerekiyorsa şaşırırdım.
Philipp Claßen
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.