هل يجب عليك دائمًا تفضيل xrange() على range()؟
سؤال
لما و لما لا؟
المحلول
للأداء، خاصة عند التكرار على نطاق واسع، xrange()
عادة ما يكون أفضل.ومع ذلك، لا تزال هناك بعض الحالات التي قد تفضلها range()
:
في بيثون 3،
range()
يفعل ماxrange()
تستخدم للقيام وxrange()
غير موجود.إذا كنت تريد كتابة تعليمات برمجية تعمل على كل من Python 2 وPython 3، فلا يمكنك استخدامهاxrange()
.range()
يمكن أن يكون في الواقع أسرع في بعض الحالات - على سبيل المثال.إذا تم التكرار على نفس التسلسل عدة مرات.xrange()
يجب إعادة بناء الكائن الصحيح في كل مرة، ولكنrange()
سيكون لها كائنات عددية حقيقية.(سيكون أداؤه دائمًا أسوأ من حيث الذاكرة)xrange()
لا يمكن استخدامه في جميع الحالات التي تتطلب قائمة حقيقية.على سبيل المثال، لا يدعم الشرائح أو أي من طرق القائمة.
[عدل] هناك منشوران يذكران كيفية القيام بذلك range()
سيتم ترقيته بواسطة أداة 2to3.للتسجيل، إليك نتيجة تشغيل الأداة على بعض نماذج استخدامات range()
و xrange()
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)
كما ترون، عند استخدامه في حلقة for أو فهم، أو عندما يتم تغليفه بالفعل بـ list()، يتم ترك النطاق دون تغيير.
نصائح أخرى
لا، كلاهما له استخداماته:
يستخدم xrange()
عند التكرار، لأنه يحفظ الذاكرة.يقول:
for x in xrange(1, one_zillion):
بدلا من:
for x in range(1, one_zillion):
ومن ناحية أخرى، استخدم range()
إذا كنت تريد فعلا قائمة الأرقام.
multiples_of_seven = range(7,100,7)
print "Multiples of seven < 100: ", multiples_of_seven
يجب أن تفضل range()
زيادة xrange()
فقط عندما تحتاج إلى قائمة فعلية.على سبيل المثال، عندما تريد تعديل القائمة التي تم إرجاعها بواسطة range()
, أو عندما ترغب في تقطيعها.للتكرار أو حتى الفهرسة العادية فقط، xrange()
سوف تعمل بشكل جيد (وعادة ما تكون أكثر كفاءة).هناك نقطة حيث range()
أسرع قليلا من xrange()
بالنسبة للقوائم الصغيرة جدًا، ولكن اعتمادًا على أجهزتك وتفاصيل أخرى متنوعة، يمكن أن يكون التعادل نتيجة للطول 1 أو 2؛لا شيء يدعو للقلق.يفضل xrange()
.
أحد الاختلافات الأخرى هو أن xrange() لا يمكنه دعم أرقام أكبر من أرقام C ints، لذلك إذا كنت تريد الحصول على نطاق باستخدام دعم الأرقام الكبيرة المدمج في python، فيجب عليك استخدام range().
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 هذه المشكلة:
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)
xrange()
أكثر كفاءة لأنه بدلاً من إنشاء قائمة بالكائنات، فإنه يقوم فقط بإنشاء كائن واحد في كل مرة.بدلاً من 100 عدد صحيح، وكل نفقاتها، والقائمة التي يجب وضعها فيها، لديك عدد صحيح واحد فقط في كل مرة.توليد أسرع، واستخدام أفضل للذاكرة، وتعليمات برمجية أكثر كفاءة.
ما لم أكن بحاجة على وجه التحديد إلى قائمة لشيء ما، فأنا أفضّل ذلك دائمًا xrange()
تقوم الدالة range() بإرجاع قائمة، بينما تقوم الدالة xrange() بإرجاع كائن xrange.
xrange() أسرع قليلاً وأكثر كفاءة في الذاكرة.لكن المكسب ليس كبيرا جدا.
بالطبع، لا يتم إهدار الذاكرة الإضافية التي تستخدمها القائمة فحسب، بل تحتوي القوائم على وظائف أكثر (شريحة، تكرار، إدراج، ...).يمكن العثور على الاختلافات الدقيقة في توثيق.ليس هناك قاعدة صارمة، استخدم ما هو مطلوب.
لا يزال إصدار Python 3.0 قيد التطوير، لكن نطاق IIRC () سيكون مشابهًا جدًا لـ xrange () لـ 2.X ويمكن استخدام القائمة () لإنشاء القوائم.
أود فقط أن أقول إنه ليس من الصعب حقًا الحصول على كائن xrange مع وظيفة الشريحة والفهرسة.لقد كتبت بعض التعليمات البرمجية التي تعمل بشكل جيد جدًا وهي بنفس سرعة xrange عندما يكون ذلك مهمًا (التكرارات).
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)
بصراحة، أعتقد أن المشكلة برمتها سخيفة نوعًا ما ويجب على xrange القيام بكل هذا على أي حال...
مثال جيد ورد في الكتاب: بيثون العملي بقلم ماجنوس لي هيتلاند
>>> zip(range(5), xrange(100000000))
[(0, 0), (1, 1), (2, 2), (3, 3), (4, 4)]
لا أوصي باستخدام النطاق بدلاً من Xrange في المثال السابق - على الرغم من الحاجة إلى الأرقام الخمسة الأولى فقط ، فإن النطاق يحسب جميع الأرقام ، وقد يستغرق ذلك الكثير من الوقت.مع xrange، لا يمثل هذا مشكلة لأنه يحسب فقط تلك الأرقام المطلوبة.
نعم قرأت إجابة @ براين:في بايثون 3، النطاق () هو مولد على أي حال وxrange () غير موجود.
اذهب مع النطاق لهذه الأسباب:
1) سيختفي xrange في إصدارات Python الأحدث.وهذا يمنحك سهولة التوافق في المستقبل.
2) سوف يأخذ النطاق الكفاءات المرتبطة بـ xrange.
حسنًا، الجميع هنا لديهم رأي مختلف فيما يتعلق بالمفاضلات والمزايا بين xrange مقابل النطاق.إنها صحيحة في الغالب، xrange عبارة عن مكرر، ويتجسد النطاق وينشئ قائمة فعلية.في أغلب الحالات، لن تلاحظ حقًا وجود فرق بين الاثنين.(يمكنك استخدام الخريطة مع النطاق ولكن ليس مع xrange، ولكنها تستهلك المزيد من الذاكرة.)
ومع ذلك، ما أعتقد أنكم تريدون سماعه هو أن الخيار المفضل هو xrange.نظرًا لأن النطاق في Python 3 عبارة عن مكرر، فإن أداة تحويل التعليمات البرمجية 2to3 ستحول جميع استخدامات xrange إلى range بشكل صحيح، وستظهر خطأ أو تحذيرًا لاستخدامات النطاق.إذا كنت تريد التأكد من تحويل التعليمات البرمجية الخاصة بك بسهولة في المستقبل، فستستخدم xrange فقط، وlist(xrange) عندما تكون متأكدًا من أنك تريد قائمة.لقد تعلمت هذا خلال سباق CPython في PyCon هذا العام (2008) في شيكاغو.
range()
:range(1, 10)
تقوم بإرجاع قائمة من 1 إلى 10 أرقام مع الاحتفاظ بالقائمة بأكملها في الذاكرة.xrange()
:يحبrange()
, ، ولكن بدلاً من إرجاع قائمة، يتم إرجاع كائن يقوم بإنشاء الأرقام الموجودة في النطاق عند الطلب.بالنسبة للحلقات، هذا أسرع قليلاً منrange()
وأكثر كفاءة في الذاكرة.xrange()
كائن مثل المكرر ويولد الأرقام حسب الطلب (تقييم كسول).
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()
يفعل نفس الشيء كما xrange()
تستخدم للقيام به في بيثون 3 وليس هناك مصطلح xrange()
موجودة في بيثون 3.range()
يمكن أن يكون أسرع في الواقع في بعض السيناريوهات إذا قمت بالتكرار على نفس التسلسل عدة مرات. xrange()
يجب إعادة بناء الكائن الصحيح في كل مرة، ولكن range()
سيكون لها كائنات عددية حقيقية.
بينما xrange
أسرع من range
في معظم الظروف، يكون الفرق في الأداء ضئيلًا جدًا.يقارن البرنامج الصغير أدناه التكرار عبر ملف range
و 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)
النتائج أدناه تظهر ذلك xrange
هو في الواقع أسرع، ولكن ليس بما يكفي للعرق.
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
لذلك استخدم بكل الوسائل xrange
, ، ولكن ما لم تكن تستخدم أجهزة مقيدة، فلا تقلق كثيرًا بشأن ذلك.