cara pythonic untuk melakukan sesuatu N kali tanpa variabel indeks?

161

Setiap hari aku semakin mencintai python.

Hari ini, saya menulis beberapa kode seperti:

for i in xrange(N):
    do_something()

Saya harus melakukan sesuatu N kali. Tetapi setiap waktu tidak tergantung pada nilai i(variabel indeks). Saya menyadari bahwa saya sedang membuat variabel yang tidak pernah saya gunakan ( i), dan saya pikir "Pasti ada cara yang lebih pythonic untuk melakukan ini tanpa perlu untuk variabel indeks yang tidak berguna."

Jadi ... pertanyaannya adalah: apakah Anda tahu bagaimana melakukan tugas sederhana ini dengan cara yang lebih (pythonic) yang indah?

Manuel Aráoz
sumber
7
Saya baru saja belajar tentang variabel _, tetapi jika tidak saya akan mempertimbangkan cara Anda melakukannya Pythonic. Saya tidak berpikir saya pernah melihat loop sederhana dilakukan dengan cara lain, setidaknya dengan python. Meskipun saya yakin ada kasus penggunaan khusus di mana Anda melihatnya dan berkata "Tunggu, itu terlihat mengerikan" - tetapi secara umum, xrange adalah cara yang disukai (sejauh yang saya lihat).
Wayne Werner
Kemungkinan duplikat Apakah mungkin untuk mengimplementasikan Python untuk rentang loop tanpa variabel iterator?
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
5
CATATAN: xrange tidak ada di Python3. Gunakan rangesebagai gantinya.
John Henckel

Jawaban:

110

Pendekatan yang sedikit lebih cepat daripada melakukan looping xrange(N)adalah:

import itertools

for _ in itertools.repeat(None, N):
    do_something()
Alex Martelli
sumber
3
Seberapa cepat? Apakah masih ada perbedaan dalam Python 3.1?
Hamish Grubijan
15
@ Hamish: Tes saya dengan 2,6 mengatakan 32% lebih cepat (23,2 kita vs 17,6 kita untuk N = 1000). Tapi itu waktu yang benar-benar waktu. Saya akan default ke kode OP karena lebih mudah dibaca (untuk saya).
Mike Boers
3
Itu bagus untuk mengetahui tentang kecepatan. Saya tentu saja menggemakan sentimen Mike tentang kode OP yang lebih mudah dibaca.
Wayne Werner
@Wayne, saya kira kebiasaan benar-benar sangat kuat - kecuali kenyataan bahwa Anda sudah terbiasa, mengapa lagi "menghitung dari 0 hingga N-1 [[dan benar-benar mengabaikan hitungan]] setiap kali melakukan penghitungan ini operasi independen "secara intrinsik lebih jelas daripada" ulangi N kali operasi berikut "...?
Alex Martelli
4
apakah Anda yakin kecepatannya benar-benar relevan? Bukankah begitu Jika Anda melakukan sesuatu yang signifikan dalam lingkaran itu, sangat mungkin akan memakan waktu ratusan atau ribuan waktu sebanyak gaya iterasi yang Anda pilih?
Henning
55

Gunakan variabel _, seperti yang saya pelajari ketika saya mengajukan pertanyaan ini , misalnya:

# A long way to do integer exponentiation
num = 2
power = 3
product = 1
for _ in xrange(power):
    product *= num
print product
GreenMatt
sumber
6
Bukan downvoter tapi itu mungkin karena Anda merujuk ke posting lain alih-alih menambahkan lebih detail dalam jawabannya
Downgoat
5
@Downgoat: Terima kasih atas umpan baliknya. Yang mengatakan, tidak banyak yang bisa dikatakan tentang idiom ini. Maksud saya dalam merujuk ke posting lain adalah untuk menunjukkan bahwa pencarian mungkin telah menghasilkan jawabannya. Saya merasa ironis bahwa pertanyaan ini memiliki beberapa kali upvotes seperti yang lainnya.
GreenMatt
39

Saya hanya menggunakan for _ in range(n), langsung ke intinya. Ini akan menghasilkan seluruh daftar untuk angka besar di Python 2, tetapi jika Anda menggunakan Python 3 itu tidak masalah.

L̲̳o̲̳̳n̲̳̳g̲̳̳p̲̳o̲̳̳k̲̳̳e̲̳̳
sumber
10

karena fungsi adalah warga negara kelas satu, Anda dapat menulis pembungkus kecil (dari jawaban Alex)

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

maka Anda dapat melewati fungsi sebagai argumen.

Anycorn
sumber
@ Hamish: Hampir tidak ada. (17,8 us per loop dalam kondisi yang sama dengan timing untuk jawaban Alex, untuk perbedaan 0,2 us).
Mike Boers
9

_ Adalah hal yang sama dengan x. Namun itu adalah idiom python yang digunakan untuk menunjukkan pengidentifikasi yang tidak ingin Anda gunakan. Dalam python pengidentifikasi ini tidak membutuhkan memor atau mengalokasikan ruang seperti variabel lakukan dalam bahasa lain. Mudah untuk melupakannya. Mereka hanya nama yang menunjuk ke objek, dalam hal ini bilangan bulat pada setiap iterasi.

Khorkrak
sumber
8

Saya menemukan berbagai jawaban yang sangat elegan (terutama jawaban Alex Martelli) tetapi saya ingin mengukur sendiri kinerjanya, jadi saya membuat skrip berikut:

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

Saya juga datang dengan solusi alternatif yang dibangun di atas Martelli dan digunakan map()untuk memanggil fungsi payload. OK, saya sedikit curang karena saya mengambil kebebasan untuk membuat payload menerima parameter yang dibuang: Saya tidak tahu apakah ada cara untuk mengatasi ini. Namun demikian, berikut hasilnya:

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

jadi menggunakan peta menghasilkan peningkatan sekitar 30% di atas standar untuk loop dan tambahan 19% di atas Martelli.

Jepang
sumber
4

Anggap Anda telah mendefinisikan do_something sebagai fungsi, dan Anda ingin melakukannya N kali. Mungkin Anda bisa mencoba yang berikut:

todos = [do_something] * N  
for doit in todos:  
    doit()
Cox Chen
sumber
45
Tentu. Jangan hanya memanggil fungsi satu juta kali, mari kita alokasikan daftar sejuta item juga. Jika CPU berfungsi, bukankah seharusnya memori sedikit stres? Jawabannya tidak dapat dikategorikan sebagai "tidak berguna" (menunjukkan pendekatan yang berbeda dan berfungsi) jadi saya tidak bisa mengundurkan diri, tetapi saya tidak setuju dan saya benar-benar menentangnya.
tzot
1
Bukankah itu hanya daftar referensi N dengan nilai fungsi yang sama?
Nick McCurdy
agak lebih baik untuk dilakukan fn() for fn in itertools.repeat(do_something, N)dan menyimpan pra-menghasilkan array ... ini adalah idiom pilihan saya.
F1Rumors
1
@tzot Mengapa nada merendahkan? Orang ini berupaya menuliskan jawaban dan sekarang mungkin tidak disarankan untuk berkontribusi di masa depan. Bahkan jika itu memiliki implikasi kinerja, itu adalah opsi yang berfungsi dan terutama jika N kecil implikasi kinerja / memori tidak signifikan.
davidscolgan
Saya selalu terkejut melihat bagaimana kinerja pengembang Python terobsesi :) Meskipun saya setuju bahwa itu bukan idiomatik, dan seseorang yang baru membaca Python mungkin tidak mengerti apa yang terjadi sejelas ketika hanya menggunakan iterator
Asfand Qazi
1

Bagaimana dengan loop sementara sederhana?

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

Anda sudah memiliki variabel; mengapa tidak menggunakannya?

Carlos Ramirez
sumber
1
Satu-satunya pemikiran saya adalah bahwa itu adalah 3 baris kode versus satu (?)
AJP
2
@AJP - Lebih mirip 4 baris vs 2 baris
ArtOfWarfare
menambahkan perbandingan (kali> 0) dan penurunan (kali - = 1) ke overhead ... jadi lebih lambat dari pada for loop ...
F1Rumors
@ F1Rumors Belum mengukurnya, tapi saya akan terkejut jika kompiler JIT seperti PyPy harus menghasilkan kode yang lebih lambat untuk loop sementara yang sederhana.
Philipp Claßen