Haruskah Anda selalu menyukai xrange () di atas rentang ()?


460

Mengapa atau mengapa tidak?


36
Bisakah seseorang menjelaskan secara singkat perbedaan antara 2 untuk kita yang bukan-python? Mungkin sesuatu seperti "xrange () tidak semua rentang () tidak, tetapi juga mendukung X, Y, dan Z"
Outlaw Programmer

87
range (n) membuat daftar yang berisi semua bilangan bulat 0..n-1. Ini adalah masalah jika Anda melakukan range (1000000), karena Anda akan berakhir dengan daftar> 4Mb. xrange berurusan dengan ini dengan mengembalikan objek yang berpura-pura menjadi daftar, tetapi hanya menghitung angka yang dibutuhkan dari indeks yang diminta, dan mengembalikannya.
Brian


4
Pada dasarnya, sedangkan range(1000)a list, xrange(1000)adalah objek yang bertindak seperti generator(meskipun tentu bukan satu). Juga, xrangelebih cepat. Anda dapat import timeit from timeitdan kemudian membuat metode yang hanya memiliki for i in xrange: passdan yang lain untuk range, kemudian lakukan timeit(method1)dan timeit(method2)dan, lihatlah, xrange kadang-kadang hampir dua kali lebih cepat (saat itulah Anda tidak memerlukan daftar). (Bagi saya, untuk i in xrange(1000):passvs untuk i in range(1000):passmengambil 13.316725969314575vs 21.190124988555908detik masing-masing - itu banyak.)
dylnmc

Tes kinerja lain memberi xrange(100)20% lebih cepat dari range(100).
Evgeni Sergeev

Jawaban:


443

Untuk kinerja, terutama ketika Anda melakukan iterasi dalam rentang yang luas, xrange()biasanya lebih baik. Namun, masih ada beberapa kasus mengapa Anda lebih suka range():

  • Dalam python 3, range()lakukan apa yang xrange()dulu dilakukan dan xrange()tidak ada. Jika Anda ingin menulis kode yang akan berjalan di Python 2 dan Python 3, Anda tidak bisa menggunakan xrange().

  • range()sebenarnya bisa lebih cepat dalam beberapa kasus - mis. jika mengulangi urutan yang sama beberapa kali. xrange()harus merekonstruksi objek integer setiap kali, tetapi range()akan memiliki objek integer nyata. (Akan selalu berperforma lebih buruk dalam hal memori)

  • xrange()tidak dapat digunakan dalam semua kasus di mana daftar nyata diperlukan. Misalnya, tidak mendukung irisan, atau metode daftar apa pun.

[Sunting] Ada beberapa posting yang menyebutkan bagaimana range()akan ditingkatkan oleh alat 2to3. Sebagai catatan, inilah output menjalankan alat pada beberapa penggunaan sampel range()danxrange()

RefactoringTool: Skipping implicit fixer: buffer
RefactoringTool: Skipping implicit fixer: idioms
RefactoringTool: Skipping implicit fixer: ws_comma
--- range_test.py (original)
+++ range_test.py (refactored)
@@ -1,7 +1,7 @@

 for x in range(20):
-    a=range(20)
+    a=list(range(20))
     b=list(range(20))
     c=[x for x in range(20)]
     d=(x for x in range(20))
-    e=xrange(20)
+    e=range(20)

Seperti yang Anda lihat, ketika digunakan dalam for for loop atau pemahaman, atau di mana sudah dibungkus dengan list (), rentang dibiarkan tidak berubah.


5
Apa yang Anda maksud dengan "range akan menjadi iterator"? Bukankah ini seharusnya "generator"?
Michael Mior

4
Tidak. Generator mengacu pada jenis iterator tertentu, dan yang baru rangebukanlah iterator.
user2357112 mendukung Monica

Peluru kedua Anda benar-benar tidak masuk akal. Anda mengatakan bahwa Anda tidak dapat menggunakan objek beberapa kali; tentu kamu bisa! coba xr = xrange(1,11)di baris berikutnya for i in xr: print " ".join(format(i*j,"3d") for j in xr)dan voila! Anda memiliki tabel waktu hingga sepuluh. Ini berfungsi sama seperti r = range(1,11)dan for i in r: print " ".join(format(i*j,"3d") for j in r)... semuanya adalah objek di Python2. Saya pikir apa yang ingin Anda katakan adalah bahwa Anda dapat melakukan pemahaman berbasis indeks (jika itu masuk akal) dengan lebih baik rangesebagai lawan xrange. Rentang berguna sangat jarang, saya pikir
dylnmc

(Tidak cukup ruang) Meskipun, saya melakukan berpikir bahwa rangedapat berguna jika Anda ingin menggunakan listdalam satu lingkaran dan kemudian mengubah indeks tertentu berdasarkan kondisi tertentu atau append hal yang daftar itu, maka rangepasti lebih baik. Namun, xrangeini lebih cepat dan menggunakan lebih sedikit memori, jadi untuk sebagian besar aplikasi loop, tampaknya menjadi yang terbaik. Ada beberapa contoh - kembali ke pertanyaan si penanya - jarang tetapi ada, di mana rangeakan lebih baik. Mungkin tidak sesering yang saya pikirkan, tapi saya tentu menggunakan xrange95% dari waktu.
dylnmc

129

Tidak, keduanya memiliki kegunaan:

Gunakan xrange()saat iterasi, karena menghemat memori. Mengatakan:

for x in xrange(1, one_zillion):

daripada:

for x in range(1, one_zillion):

Di sisi lain, gunakan range()jika Anda benar-benar menginginkan daftar angka.

multiples_of_seven = range(7,100,7)
print "Multiples of seven < 100: ", multiples_of_seven

42

Anda harus mendukung range()lebih xrange()hanya ketika Anda memerlukan daftar yang sebenarnya. Misalnya, ketika Anda ingin memodifikasi daftar yang dikembalikan oleh range(), atau ketika Anda ingin mengirisnya. Untuk iterasi atau bahkan hanya pengindeksan normal, xrange()akan berfungsi dengan baik (dan biasanya jauh lebih efisien). Ada titik di mana range()sedikit lebih cepat daripada xrange()untuk daftar yang sangat kecil, tetapi tergantung pada perangkat keras Anda dan berbagai detail lainnya, titik impas bisa mencapai panjang 1 atau 2; bukan sesuatu yang perlu dikhawatirkan. Lebih suka xrange().


30

Satu perbedaan lainnya adalah bahwa xrange () tidak dapat mendukung angka yang lebih besar dari int C, jadi jika Anda ingin memiliki rentang menggunakan dukungan python yang dibangun dalam jumlah besar, Anda harus menggunakan rentang ().

Python 2.7.3 (default, Jul 13 2012, 22:29:01) 
[GCC 4.7.1] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> range(123456787676676767676676,123456787676676767676679)
[123456787676676767676676L, 123456787676676767676677L, 123456787676676767676678L]
>>> xrange(123456787676676767676676,123456787676676767676679)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
OverflowError: Python int too large to convert to C long

Python 3 tidak memiliki masalah ini:

Python 3.2.3 (default, Jul 14 2012, 01:01:48) 
[GCC 4.7.1] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> range(123456787676676767676676,123456787676676767676679)
range(123456787676676767676676, 123456787676676767676679)

13

xrange()lebih efisien karena daripada menghasilkan daftar objek, itu hanya menghasilkan satu objek pada suatu waktu. Alih-alih 100 bilangan bulat, dan semua overhead mereka, dan daftar untuk memasukkannya, Anda hanya memiliki satu bilangan bulat pada suatu waktu. Generasi yang lebih cepat, penggunaan memori yang lebih baik, kode yang lebih efisien.

Kecuali saya secara khusus membutuhkan daftar untuk sesuatu, saya selalu mendukung xrange()


8

range () mengembalikan daftar, xrange () mengembalikan objek xrange.

xrange () sedikit lebih cepat, dan memori lebih efisien. Tetapi keuntungannya tidak terlalu besar.

Memori tambahan yang digunakan oleh suatu daftar tentu saja tidak hanya sia-sia, daftar memiliki lebih banyak fungsi (iris, ulangi, masukkan, ...). Perbedaan yang tepat dapat ditemukan dalam dokumentasi . Tidak ada aturan pengecut, gunakan apa yang dibutuhkan.

Python 3.0 masih dalam pengembangan, tetapi rentang IIRC () akan sangat mirip dengan xrange () dari 2.X dan daftar (rentang ()) dapat digunakan untuk menghasilkan daftar.


5

Saya hanya ingin mengatakan bahwa itu BENAR-BENAR tidak sulit untuk mendapatkan objek xrange dengan fungsi slice dan pengindeksan. Saya telah menulis beberapa kode yang bekerja dengan sangat baik dan secepat xrange ketika ia menghitung (iterasi).

from __future__ import division

def read_xrange(xrange_object):
    # returns the xrange object's start, stop, and step
    start = xrange_object[0]
    if len(xrange_object) > 1:
       step = xrange_object[1] - xrange_object[0]
    else:
        step = 1
    stop = xrange_object[-1] + step
    return start, stop, step

class Xrange(object):
    ''' creates an xrange-like object that supports slicing and indexing.
    ex: a = Xrange(20)
    a.index(10)
    will work

    Also a[:5]
    will return another Xrange object with the specified attributes

    Also allows for the conversion from an existing xrange object
    '''
    def __init__(self, *inputs):
        # allow inputs of xrange objects
        if len(inputs) == 1:
            test, = inputs
            if type(test) == xrange:
                self.xrange = test
                self.start, self.stop, self.step = read_xrange(test)
                return

        # or create one from start, stop, step
        self.start, self.step = 0, None
        if len(inputs) == 1:
            self.stop, = inputs
        elif len(inputs) == 2:
            self.start, self.stop = inputs
        elif len(inputs) == 3:
            self.start, self.stop, self.step = inputs
        else:
            raise ValueError(inputs)

        self.xrange = xrange(self.start, self.stop, self.step)

    def __iter__(self):
        return iter(self.xrange)

    def __getitem__(self, item):
        if type(item) is int:
            if item < 0:
                item += len(self)

            return self.xrange[item]

        if type(item) is slice:
            # get the indexes, and then convert to the number
            start, stop, step = item.start, item.stop, item.step
            start = start if start != None else 0 # convert start = None to start = 0
            if start < 0:
                start += start
            start = self[start]
            if start < 0: raise IndexError(item)
            step = (self.step if self.step != None else 1) * (step if step != None else 1)
            stop = stop if stop is not None else self.xrange[-1]
            if stop < 0:
                stop += stop

            stop = self[stop]
            stop = stop

            if stop > self.stop:
                raise IndexError
            if start < self.start:
                raise IndexError
            return Xrange(start, stop, step)

    def index(self, value):
        error = ValueError('object.index({0}): {0} not in object'.format(value))
        index = (value - self.start)/self.step
        if index % 1 != 0:
            raise error
        index = int(index)


        try:
            self.xrange[index]
        except (IndexError, TypeError):
            raise error
        return index

    def __len__(self):
        return len(self.xrange)

Jujur, saya pikir seluruh masalah agak konyol dan xrange harus melakukan semua ini ...


Ya setuju; dari teknologi yang sama sekali berbeda, periksa pekerjaan di lodash untuk membuatnya malas: github.com/lodash/lodash/issues/274 . Mengiris dll harus tetap semalas mungkin dan jika tidak, baru kemudian verifikasi.
Rob Grant

4

Contoh yang baik diberikan dalam buku: Praktis Python Oleh Magnus Lie Hetland

>>> zip(range(5), xrange(100000000))
[(0, 0), (1, 1), (2, 2), (3, 3), (4, 4)]

Saya tidak akan merekomendasikan menggunakan rentang daripada xrange dalam contoh sebelumnya — meskipun hanya lima angka pertama yang dibutuhkan, rentang menghitung semua angka, dan itu mungkin membutuhkan banyak waktu. Dengan xrange, ini bukan masalah karena hanya menghitung angka-angka yang dibutuhkan.

Ya saya membaca jawaban @ Brian: Dalam python 3, range () adalah generator dan xrange () tidak ada.


3

Ikuti rentang karena alasan-alasan berikut:

1) xrange akan hilang dalam versi Python yang lebih baru. Ini memberi Anda kompatibilitas di masa depan yang mudah.

2) kisaran akan mengambil efisiensi yang terkait dengan xrange.


13
Jangan lakukan ini. xrange () akan hilang, tetapi juga banyak hal lainnya. Alat yang akan Anda gunakan untuk menerjemahkan kode Python 2.x Anda menjadi kode Python 3.x akan secara otomatis menerjemahkan xrange () ke range (), tetapi range () akan diterjemahkan ke daftar yang kurang efisien (range ()).
Thomas Wouters

10
Thomas: Ini sebenarnya sedikit lebih pintar dari itu. Ini akan menerjemahkan rentang () dalam situasi di mana ia jelas tidak membutuhkan daftar nyata (misalnya dalam for for loop, atau pemahaman) untuk hanya rentang biasa (). Hanya kasus di mana ia ditugaskan ke variabel, atau digunakan secara langsung harus dibungkus dengan daftar ()
Brian

2

Oke, semua orang di sini berbeda pendapat tentang tradeoffs dan kelebihan xrange versus range. Mereka sebagian besar benar, xrange adalah iterator, dan rentang menyempurnakan dan membuat daftar aktual. Untuk sebagian besar kasus, Anda tidak akan benar-benar melihat perbedaan di antara keduanya. (Anda dapat menggunakan peta dengan jangkauan tetapi tidak dengan xrange, tetapi menggunakan lebih banyak memori.)

Apa yang menurut saya ingin Anda dengar, adalah bahwa pilihan yang lebih disukai adalah xrange. Karena range dalam Python 3 adalah iterator, alat konversi kode 2to3 akan dengan benar mengkonversi semua penggunaan xrange ke range, dan akan membuang kesalahan atau peringatan untuk penggunaan range. Jika Anda ingin memastikan untuk dengan mudah mengkonversi kode Anda di masa depan, Anda akan menggunakan xrange saja, dan daftar (xrange) ketika Anda yakin bahwa Anda menginginkan daftar. Saya belajar ini selama sprint CPython di PyCon tahun ini (2008) di Chicago.


8
Itu tidak benar. Kode seperti "untuk x dalam rentang (20)" akan dibiarkan sebagai rentang, dan kode seperti "x = rentang (20)" akan dikonversi menjadi "x = daftar (rentang (20))" - tidak ada kesalahan. Lebih lanjut, jika Anda ingin menulis kode yang akan berjalan di bawah 2.6 dan 3.0, range () adalah satu-satunya pilihan Anda tanpa menambahkan fungsi kompatibilitas.
Brian

2
  • range(): range(1, 10) mengembalikan daftar dari 1 hingga 10 angka & menahan seluruh daftar dalam memori.
  • xrange(): Suka range(), tetapi bukannya mengembalikan daftar, mengembalikan objek yang menghasilkan angka dalam kisaran sesuai permintaan. Untuk perulangan, ini lebih cepat range()dan lebih efisien daripada memori. xrange()objek seperti iterator dan menghasilkan angka sesuai permintaan (Evaluasi Malas).
In [1]: range(1,10)
Out[1]: [1, 2, 3, 4, 5, 6, 7, 8, 9]

In [2]: xrange(10)
Out[2]: xrange(10)

In [3]: print xrange.__doc__
Out[3]: xrange([start,] stop[, step]) -> xrange object

range()melakukan hal yang sama seperti xrange()dulu di Python 3 dan tidak xrange()ada istilah yang ada di Python 3. range()sebenarnya bisa lebih cepat dalam beberapa skenario jika Anda mengulangi urutan yang sama beberapa kali. xrange()harus merekonstruksi objek integer setiap kali, tetapi range()akan memiliki objek integer nyata.


2

Sementara xrangelebih cepat darirange di sebagian besar keadaan, perbedaan kinerja sangat minim. Program kecil di bawah ini membandingkan iterasi dengan a rangedan xrange:

import timeit
# Try various list sizes.
for list_len in [1, 10, 100, 1000, 10000, 100000, 1000000]:
  # Time doing a range and an xrange.
  rtime = timeit.timeit('a=0;\nfor n in range(%d): a += n'%list_len, number=1000)
  xrtime = timeit.timeit('a=0;\nfor n in xrange(%d): a += n'%list_len, number=1000)
  # Print the result
  print "Loop list of len %d: range=%.4f, xrange=%.4f"%(list_len, rtime, xrtime)

Hasil di bawah ini menunjukkan bahwa xrangememang lebih cepat, tetapi tidak cukup untuk berkeringat.

Loop list of len 1: range=0.0003, xrange=0.0003
Loop list of len 10: range=0.0013, xrange=0.0011
Loop list of len 100: range=0.0068, xrange=0.0034
Loop list of len 1000: range=0.0609, xrange=0.0438
Loop list of len 10000: range=0.5527, xrange=0.5266
Loop list of len 100000: range=10.1666, xrange=7.8481
Loop list of len 1000000: range=168.3425, xrange=155.8719

Jadi, tentu saja gunakan xrange, tetapi kecuali Anda menggunakan perangkat keras terbatas, jangan terlalu khawatir tentang hal itu.


Anda list_lentidak sedang digunakan dan Anda hanya menjalankan kode ini untuk daftar panjang 100.
Mark

Saya merekomendasikan untuk benar-benar mengubah panjang daftar:rtime = timeit.timeit('a=0;\nfor n in range(%d): a += n' % list_len, number=1000)
Mark

Wow, ini akan menjadi minggu yang baik, terima kasih, sudah pasti. Meski begitu, bukan perbedaan besar.
speedplane
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.