ما الفرق بين وظائف النطاق و xrange في Python 2.X؟
-
01-07-2019 - |
سؤال
من الواضح أن xrange أسرع ولكن ليس لدي أي فكرة عن سبب كونه أسرع (ولا يوجد دليل إلى جانب القصص المتناقلة حتى الآن على أنه أسرع) أو ما هو المختلف إلى جانب ذلك
for i in range(0, 20):
for i in xrange(0, 20):
المحلول
range بإنشاء قائمة، لذلك إذا قمت بذلك range(1, 10000000)
يقوم بإنشاء قائمة في الذاكرة مع 9999999
عناصر.
xrange
هو كائن تسلسل يتم تقييمه بتكاسل.
يجب إضافته من تلميح @ Thiago، أنه في python3، النطاق يعادل نطاق python's xrange
نصائح أخرى
range بإنشاء قائمة، لذلك إذا قمت بذلك
range(1, 10000000)
يقوم بإنشاء قائمة في الذاكرة مع9999999
عناصر.
xrange
هو مولد، لذلكهو كائن تسلسلهوالذي يقيم بتكاسل.
هذا صحيح، لكن في بايثون 3، .range()
سيتم تنفيذه بواسطة Python 2 .xrange()
.إذا كنت تريد إنشاء القائمة بالفعل، فسوف يتعين عليك القيام بما يلي:
list(range(1,100))
تذكر، استخدم timeit
وحدة لاختبار أي من المقتطفات الصغيرة من التعليمات البرمجية تكون أسرع!
$ python -m timeit 'for i in range(1000000):' ' pass'
10 loops, best of 3: 90.5 msec per loop
$ python -m timeit 'for i in xrange(1000000):' ' pass'
10 loops, best of 3: 51.1 msec per loop
أنا شخصياً أستخدمه دائمًا .range()
, ، إلا إذا كنت أتعامل مع حقًا قوائم ضخمة -- كما ترون، من حيث الوقت، بالنسبة لقائمة تضم مليون إدخال، فإن الحمل الإضافي هو 0.04 ثانية فقط.وكما يشير كوري، في بايثون 3.0 .xrange()
سوف تذهب بعيدا و .range()
سوف أعطيك سلوك مكرر لطيف على أي حال.
xrange
يقوم فقط بتخزين معلمات النطاق وإنشاء الأرقام عند الطلب.ومع ذلك، فإن تطبيق لغة C لـ Python يقيد وسيطاتها حاليًا على لغة C الطويلة:
xrange(2**32-1, 2**32+1) # When long is 32 bits, OverflowError: Python int too large to convert to C long
range(2**32-1, 2**32+1) # OK --> [4294967295L, 4294967296L]
لاحظ أنه في Python 3.0 لا يوجد سوى range
ويتصرف مثل 2.x xrange
ولكن بدون القيود على الحد الأدنى والحد الأقصى لنقاط النهاية.
يُرجع xrange مُكرِّرًا ويحتفظ برقم واحد فقط في الذاكرة في كل مرة.يحتفظ النطاق بقائمة كاملة من الأرقام في الذاكرة.
قم بقضاء بعض الوقت مع مرجع المكتبة.كلما كنت أكثر دراية بها، كلما تمكنت من العثور على إجابات لأسئلة مثل هذه بشكل أسرع.من المهم بشكل خاص الفصول القليلة الأولى حول الكائنات والأنواع المضمنة.
تتمثل ميزة نوع XRange في أن كائن Xrange سيأخذ دائمًا نفس القدر من الذاكرة ، بغض النظر عن حجم النطاق الذي يمثله.لا توجد مزايا أداء متسقة.
هناك طريقة أخرى للعثور على معلومات سريعة حول بنية Python وهي سلسلة المستندات ووظيفة المساعدة:
print xrange.__doc__ # def doc(x): print x.__doc__ is super useful
help(xrange)
يقوم range بإنشاء قائمة، لذلك إذا قمت بإجراء range(1, 10000000) فإنه يقوم بإنشاء قائمة في الذاكرة تحتوي على 10000000 عنصر.xrange هو مولد، لذلك يتم تقييمه بتكاسل.
وهذا يجلب لك ميزتين:
- يمكنك تكرار قوائم أطول دون الحصول على
MemoryError
. - نظرًا لأنه يحل كل رقم بتكاسل، إذا قمت بإيقاف التكرار مبكرًا، فلن تضيع الوقت في إنشاء القائمة بأكملها.
لقد صدمت لا أحد يقرأ وثيقة:
هذه الوظيفة مشابهة جدًا لـ
range()
, ، ولكن ترجعxrange
كائن بدلاً من القائمة.هذا هو نوع تسلسل غير شفاف ينتج عنه نفس القيم مثل القائمة المقابلة، دون تخزينها جميعًا في وقت واحد.ميزةxrange()
زيادةrange()
هو الحد الأدنى (منذxrange()
لا يزال يتعين عليه إنشاء القيم عند طلبها) إلا عند استخدام نطاق كبير جدًا على جهاز متعطش للذاكرة أو عندما لا يتم استخدام جميع عناصر النطاق مطلقًا (مثل عندما يتم إنهاء الحلقة عادةً بـbreak
).
إنه لأسباب التحسين.
سيقوم range() بإنشاء قائمة بالقيم من البداية إلى النهاية (0 ..20 في مثالك).ستصبح هذه عملية مكلفة على نطاقات كبيرة جدًا.
من ناحية أخرى، يعتبر xrange() أكثر تحسينًا.سيحسب القيمة التالية فقط عند الحاجة (عبر كائن تسلسل xrange) ولا يقوم بإنشاء قائمة بجميع القيم كما يفعل range().
سوف تجد ميزة xrange
زيادة range
في هذا المثال البسيط:
import timeit
t1 = timeit.default_timer()
a = 0
for i in xrange(1, 100000000):
pass
t2 = timeit.default_timer()
print "time taken: ", (t2-t1) # 4.49153590202 seconds
t1 = timeit.default_timer()
a = 0
for i in range(1, 100000000):
pass
t2 = timeit.default_timer()
print "time taken: ", (t2-t1) # 7.04547905922 seconds
المثال أعلاه لا يعكس أي شيء أفضل بكثير في حالة xrange
.
انظر الآن إلى الحالة التالية حيث range
هو حقا بطيء حقا، مقارنة ب xrange
.
import timeit
t1 = timeit.default_timer()
a = 0
for i in xrange(1, 100000000):
if i == 10000:
break
t2 = timeit.default_timer()
print "time taken: ", (t2-t1) # 0.000764846801758 seconds
t1 = timeit.default_timer()
a = 0
for i in range(1, 100000000):
if i == 10000:
break
t2 = timeit.default_timer()
print "time taken: ", (t2-t1) # 2.78506207466 seconds
مع range
, ، فهو يقوم بالفعل بإنشاء قائمة من 0 إلى 100000000 (تستغرق وقتًا طويلاً)، ولكن xrange
هو مولد وهو يولد أرقامًا فقط بناءً على الحاجة، أي إذا استمر التكرار.
في Python-3، تم تنفيذ range
الوظيفة هي نفس وظيفة xrange
في Python-2، بينما تخلصوا من ذلك xrange
في بيثون-3
ترميز سعيد!!
يتراوح(): يُرجع النطاق (1، 10) قائمة من 1 إلى 10 أرقام ويحتفظ بالقائمة بأكملها في الذاكرة.
xrange(): مثل 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__
xrange([start,] stop[, step]) -> xrange object
range(x,y)
إرجاع قائمة بكل رقم بين x وy إذا كنت تستخدم a for
حلقة، ثم range
أبطأ.في الحقيقة، range
لديه نطاق مؤشر أكبر. range(x.y)
سيتم طباعة قائمة بجميع الأرقام الموجودة بين x و y
xrange(x,y)
عائدات xrange(x,y)
ولكن إذا كنت تستخدم for
حلقة، ثم xrange
أسرع. xrange
لديه نطاق فهرس أصغر. xrange
لن تتم طباعتها فقط xrange(x,y)
لكنه سيظل يحتفظ بجميع الأرقام الموجودة فيه.
[In] range(1,10)
[Out] [1, 2, 3, 4, 5, 6, 7, 8, 9]
[In] xrange(1,10)
[Out] xrange(1,10)
إذا كنت تستخدم أ for
حلقة، ثم أنها ستعمل
[In] for i in range(1,10):
print i
[Out] 1
2
3
4
5
6
7
8
9
[In] for i in xrange(1,10):
print i
[Out] 1
2
3
4
5
6
7
8
9
لا يوجد فرق كبير عند استخدام الحلقات، على الرغم من وجود فرق عند طباعتها فقط!
في بيثون 2.x
النطاق (x) تقوم بإرجاع قائمة تم إنشاؤها في الذاكرة باستخدام عناصر x.
>>> a = range(5)
>>> a
[0, 1, 2, 3, 4]
xrange(x) تقوم بإرجاع كائن xrange وهو كائن منشئ يقوم بإنشاء الأرقام عند الطلب.يتم حسابها أثناء الحلقة (التقييم البطيء).
بالنسبة للتكرار، يعد هذا أسرع قليلاً من النطاق () وأكثر كفاءة في الذاكرة.
>>> b = xrange(5)
>>> b
xrange(5)
عند اختبار النطاق مقابل xrange في حلقة (أعلم أنه يجب علي استخدام الوقت هو, ، ولكن تم حذف هذا بسرعة من الذاكرة باستخدام مثال بسيط لفهم القائمة) لقد وجدت ما يلي:
import time
for x in range(1, 10):
t = time.time()
[v*10 for v in range(1, 10000)]
print "range: %.4f" % ((time.time()-t)*100)
t = time.time()
[v*10 for v in xrange(1, 10000)]
print "xrange: %.4f" % ((time.time()-t)*100)
الذي يعطي:
$python range_tests.py
range: 0.4273
xrange: 0.3733
range: 0.3881
xrange: 0.3507
range: 0.3712
xrange: 0.3565
range: 0.4031
xrange: 0.3558
range: 0.3714
xrange: 0.3520
range: 0.3834
xrange: 0.3546
range: 0.3717
xrange: 0.3511
range: 0.3745
xrange: 0.3523
range: 0.3858
xrange: 0.3997 <- garbage collection?
أو باستخدام xrange في حلقة for:
range: 0.4172
xrange: 0.3701
range: 0.3840
xrange: 0.3547
range: 0.3830
xrange: 0.3862 <- garbage collection?
range: 0.4019
xrange: 0.3532
range: 0.3738
xrange: 0.3726
range: 0.3762
xrange: 0.3533
range: 0.3710
xrange: 0.3509
range: 0.3738
xrange: 0.3512
range: 0.3703
xrange: 0.3509
هل يتم اختبار المقتطف الخاص بي بشكل صحيح؟هل هناك أي تعليقات على المثيل الأبطأ لـ xrange؟أو مثال أفضل :-)
تشير بعض الإجابات الأخرى إلى أن Python 3 أزال 2.x range
وأعيد تسميتها بـ 2.x's xrange
ل range
.ومع ذلك، إلا إذا كنت تستخدم الإصدار 3.0 أو 3.1 (وهو ما لا ينبغي لأحد أن يستخدمه)، فهو في الواقع نوع مختلف إلى حد ما.
مثل المستندات 3.1 يقول:
كائنات النطاق لها سلوك قليل جدًا:إنهم يدعمون فقط الفهرسة والتكرار و
len
وظيفة.
لكن في 3.2+ range
هو تسلسل كامل - فهو يدعم الشرائح الموسعة، وجميع أساليب collections.abc.Sequence
مع نفس الدلالات مثل أ list
.*
وعلى الأقل في CPython وPyPy (التطبيقان الوحيدان 3.2+ الموجودان حاليًا)، فإن لديها أيضًا تطبيقات في الوقت الثابت لـ index
و count
الأساليب و in
عامل التشغيل (طالما أنك تمرر الأعداد الصحيحة فقط).وهذا يعني الكتابة 123456 in r
معقول في 3.2+، بينما في 2.7 أو 3.1 سيكون فكرة فظيعة.
* حقيقة ان issubclass(xrange, collections.Sequence)
عائدات True
في 2.6-2.7 و3.0-3.1 هو حشرة تم إصلاح ذلك في 3.2 ولم يتم نقله إلى الخلف.
يعمل كل من xrange() و range() في لغة python بنفس الطريقة التي يعمل بها المستخدم، ولكن الفرق يأتي عندما نتحدث عن كيفية تخصيص الذاكرة في استخدام كلتا الدالتين.
عندما نستخدم النطاق () فإننا نخصص ذاكرة لجميع المتغيرات التي ينشئها، لذلك لا ينصح باستخدامها مع أرقام أكبر.من المتغيرات التي سيتم إنشاؤها.
من ناحية أخرى، يقوم xrange() بإنشاء قيمة معينة فقط في المرة الواحدة ولا يمكن استخدامه إلا مع حلقة for لطباعة جميع القيم المطلوبة.
range ينشئ القائمة بأكملها ويعيدها.لا يقوم xrange بذلك - فهو يقوم بإنشاء الأرقام الموجودة في القائمة عند الطلب.
اقرأ المنشور التالي للمقارنة بين النطاق وxrange مع التحليل الرسومي.
يستخدم xrange مُكرِّرًا (يُنشئ قيمًا سريعة)، بينما يُرجع النطاق قائمة.
ماذا؟
range
إرجاع قائمة ثابتة في وقت التشغيل.
xrange
يعود object
(الذي يعمل كمولد، على الرغم من أنه ليس كذلك بالتأكيد) والذي يتم إنشاء القيم منه عند الحاجة.
متى تستخدم أي؟
- يستخدم
xrange
إذا كنت تريد إنشاء قائمة بنطاق ضخم، مثلاً مليار، خاصة عندما يكون لديك "نظام حساس للذاكرة" مثل الهاتف الخلوي. - يستخدم
range
إذا كنت تريد التكرار على القائمة عدة مرات.
ملاحظة:بايثون 3.x range
الوظيفة == بيثون 2.x xrange
وظيفة.
فيما يتعلق بمتطلبات المسح الضوئي/الطباعة للعناصر 0-N، يعمل النطاق وxrange على النحو التالي.
range() - ينشئ قائمة جديدة في الذاكرة ويأخذ العناصر بأكملها من 0 إلى N (إجمالي N+1) ويطبعها.xrange() - يقوم بإنشاء مثيل مكرر يقوم بمسح العناصر ويحتفظ فقط بالعنصر الحالي الذي تمت مواجهته في الذاكرة، وبالتالي استخدام نفس مقدار الذاكرة طوال الوقت.
في حالة وجود العنصر المطلوب إلى حد ما في بداية القائمة فقط، فإنه يوفر قدرًا كبيرًا من الوقت والذاكرة.
يقل الفرق بالنسبة للوسائط الأصغر حجمًا range(..)
/ xrange(..)
:
$ python -m timeit "for i in xrange(10111):" " for k in range(100):" " pass"
10 loops, best of 3: 59.4 msec per loop
$ python -m timeit "for i in xrange(10111):" " for k in xrange(100):" " pass"
10 loops, best of 3: 46.9 msec per loop
في هذه الحالة xrange(100)
هو فقط حوالي 20٪ أكثر كفاءة.
لقد أوضح الجميع ذلك بشكل كبير.لكنني أردت أن أرى ذلك بنفسي.أستخدم بايثون3.لذلك، قمت بفتح مراقب الموارد (في نظام التشغيل Windows!)، وقمت أولاً بتنفيذ الأمر التالي أولاً:
a=0
for i in range(1,100000):
a=a+i
ثم تحقق من التغيير في الذاكرة "قيد الاستخدام".لقد كانت ضئيلة.ثم قمت بتشغيل الكود التالي:
for i in list(range(1,100000)):
a=a+i
واستغرق الأمر جزءًا كبيرًا من الذاكرة للاستخدام على الفور.ولقد اقتنعت.يمكنك تجربتها بنفسك.
إذا كنت تستخدم Python 2X، فاستبدل "range()" بـ "xrange()" في الكود الأول و"list(range())" بـ "range()".
يتراوح يعود أ قائمة بينما com.xrange يعود com.xrange الكائن الذي يأخذ نفس الذاكرة بغض النظر عن حجم النطاق، كما في هذه الحالة، يتم إنشاء عنصر واحد فقط ويكون متاحًا لكل تكرار بينما في حالة استخدام النطاق، يتم إنشاء جميع العناصر مرة واحدة وتكون متاحة في الذاكرة.
النطاق: - النطاق سوف يملأ كل شيء مرة واحدة. مما يعني أن كل رقم من النطاق سوف يشغل الذاكرة.
xrange: -xrange هو شيء يشبه المولد، وسيظهر في الصورة عندما تريد نطاق الأرقام ولكنك لا تريد تخزينها، كما هو الحال عندما تريد استخدامها في حلقة، لذا فإن الذاكرة فعالة.
من مستندات المساعدة.
بايثون 2.7.12
>>> print range.__doc__
range(stop) -> list of integers
range(start, stop[, step]) -> list of integers
Return a list containing an arithmetic progression of integers.
range(i, j) returns [i, i+1, i+2, ..., j-1]; start (!) defaults to 0.
When step is given, it specifies the increment (or decrement).
For example, range(4) returns [0, 1, 2, 3]. The end point is omitted!
These are exactly the valid indices for a list of 4 elements.
>>> print xrange.__doc__
xrange(stop) -> xrange object
xrange(start, stop[, step]) -> xrange object
Like range(), but instead of returning a list, returns an object that
generates the numbers in the range on demand. For looping, this is
slightly faster than range() and more memory efficient.
بايثون 3.5.2
>>> print(range.__doc__)
range(stop) -> range object
range(start, stop[, step]) -> range object
Return an object that produces a sequence of integers from start (inclusive)
to stop (exclusive) by step. range(i, j) produces i, i+1, i+2, ..., j-1.
start defaults to 0, and stop is omitted! range(4) produces 0, 1, 2, 3.
These are exactly the valid indices for a list of 4 elements.
When step is given, it specifies the increment (or decrement).
>>> print(xrange.__doc__)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'xrange' is not defined
الفرق واضح.في بايثون 2.x، range
إرجاع قائمة، xrange
تقوم بإرجاع كائن xrange وهو قابل للتكرار.
في بايثون 3.x، range
يصبح xrange
من بايثون 2.x، و xrange
تم حذفه.
بالإضافة إلى ذلك، إذا قمت بذلك list(xrange(...))
سيكون معادلاً ل range(...)
.
لذا list
بطيء.
أيضًا xrange
حقا لا يكمل التسلسل بالكامل
ولهذا السبب فهي ليست قائمة، إنها xrange
هدف
انظر الى هذا بريد للعثور على الفرق بين النطاق وxrange:
يقتبس:
range
إرجاع ما تعتقده بالضبط:قائمة أعداد صحيحة متتالية ، بطول محدد يبدأ بـ 0.xrange
, ، ومع ذلك ، يعيد "كائن xrange", ، والذي يعمل إلى حد كبير مثل المكرر