ทำไมไม่มีฟังก์ชั่น xrange ใน Python3


273

เมื่อเร็ว ๆ นี้ฉันเริ่มใช้ Python3 และมันก็ไม่มีความเจ็บปวด xrange

ตัวอย่างง่ายๆ:

1) Python2:

from time import time as t
def count():
  st = t()
  [x for x in xrange(10000000) if x%4 == 0]
  et = t()
  print et-st
count()

2) Python3:

from time import time as t

def xrange(x):

    return iter(range(x))

def count():
    st = t()
    [x for x in xrange(10000000) if x%4 == 0]
    et = t()
    print (et-st)
count()

ผลลัพธ์ที่ได้ตามลำดับ:

1) 1.53888392448 2) 3.215819835662842

ทำไมถึงเป็นอย่างนั้น? ฉันหมายความว่าทำไม xrange ถึงถูกลบ? มันเป็นเครื่องมือที่ยอดเยี่ยมในการเรียนรู้ สำหรับผู้เริ่มต้นเช่นเดียวกับตัวฉันเหมือนกับที่เราทุกคนเคยมีอยู่ในบางจุด ทำไมลบมันออก? ใครบางคนสามารถชี้ให้ฉันไปที่ PEP ที่เหมาะสมฉันไม่สามารถหาได้

ไชโย


231
rangeใน Python 3.x xrangeมาจาก Python 2.x ในความเป็นจริงแล้ว Python 2.x rangeถูกลบไปแล้ว
Anorov

27
PS timeคุณไม่ควรเวลากับ นอกเหนือจากการใช้งานง่ายขึ้นและยากที่จะเข้าใจผิดและทดสอบซ้ำสำหรับคุณtimeitดูแลทุกสิ่งที่คุณจำไม่ได้หรือรู้วิธีดูแล (เช่นปิดการใช้งาน GC) และอาจใช้ นาฬิกาที่มีความละเอียดมากกว่าพันเท่า
abarnert

7
นอกจากนี้ทำไมคุณทดสอบเวลาในการกรองrangeบนx%4 == 0? ทำไมไม่ลองทดสอบlist(xrange())เทียบกับlist(range())มันมีงานนอกโลกให้น้อยที่สุดเท่าที่จะทำได้? (ตัวอย่างเช่นคุณจะรู้ได้อย่างไรว่า 3.x ไม่ได้ทำงานx%4ช้ากว่ากัน) สำหรับเรื่องนี้เหตุใดคุณจึงสร้างอาคารขนาดใหญ่listที่เกี่ยวข้องกับการจัดสรรหน่วยความจำจำนวนมาก (ซึ่งนอกเหนือจากการช้าก็เป็นตัวแปรที่เหลือเชื่อเช่นกัน) ?
abarnert

5
ดูdocs.python.org/3.0/whatsnew/3.0.html , ส่วน "Views and Iterators แทนที่จะเป็นรายการ": "range () ตอนนี้ทำงานเหมือน xrange () ที่ใช้ทำงานยกเว้นจะทำงานกับค่าของขนาดที่กำหนดเอง ไม่มีอยู่แล้ว." ดังนั้นตอนนี้ range จะส่งคืนตัววนซ้ำ iter(range)ซ้ำซ้อน
ToolmakerSteve

9
ขออภัยที่ทราบว่าการอ้างอิงเอกสารการเปลี่ยนแปลงนั้นไม่ทำให้เห็นได้อย่างชัดเจน สำหรับคนอื่นที่สับสนและไม่ต้องการอ่านคำตอบที่ยอมรับมานานและความคิดเห็นทั้งหมด: ไม่ว่าคุณจะใช้ xrange ใน python 2 ให้ใช้ range ใน python 3 ทำอะไรที่ xrange ใช้ทำซึ่งก็คือ ส่งคืนตัววนซ้ำ list(range(..))หากคุณต้องการผลในรายการให้ทำ นั่นเท่ากับช่วงของ python 2 หรือจะบอกว่าเป็นอีกวิธีหนึ่ง: xrange ถูกเปลี่ยนชื่อเป็น range เพราะมันเป็นค่าเริ่มต้นที่ดีกว่า ไม่จำเป็นต้องมีทั้งคู่ทำlist(range)ถ้าคุณต้องการรายการจริงๆ .
ToolmakerSteve

คำตอบ:


175

บางวัดประสิทธิภาพการทำงานโดยใช้แทนการพยายามที่จะทำมันด้วยตนเองtimeittime

ครั้งแรกของ Apple 2.7.2 64 บิต:

In [37]: %timeit collections.deque((x for x in xrange(10000000) if x%4 == 0), maxlen=0)
1 loops, best of 3: 1.05 s per loop

ตอนนี้ python.org 3.3.0 64- บิต:

In [83]: %timeit collections.deque((x for x in range(10000000) if x%4 == 0), maxlen=0)
1 loops, best of 3: 1.32 s per loop

In [84]: %timeit collections.deque((x for x in xrange(10000000) if x%4 == 0), maxlen=0)
1 loops, best of 3: 1.31 s per loop

In [85]: %timeit collections.deque((x for x in iter(range(10000000)) if x%4 == 0), maxlen=0) 
1 loops, best of 3: 1.33 s per loop

เห็นได้ชัดว่า 3.x rangeช้ากว่า 2.x xrangeเล็กน้อย และxrangeฟังก์ชั่นของ OP ไม่มีส่วนเกี่ยวข้องกับมัน (ไม่น่าแปลกใจเนื่องจากการโทรครั้งเดียวไปยัง__iter__สล็อตไม่น่าจะปรากฏให้เห็นท่ามกลางการโทร 10,000,000 ครั้งเพื่อทุกสิ่งที่เกิดขึ้นในวง แต่มีคนนำมาขึ้นเป็นความเป็นไปได้)

แต่มันช้ากว่า 30% เท่านั้น OP ได้รับ 2x ช้าแค่ไหน ถ้าฉันทำซ้ำการทดสอบเดียวกันกับ Python 32- บิตฉันจะได้ 1.58 กับ 3.12 ดังนั้นฉันเดาว่านี่เป็นอีกกรณีหนึ่งที่ 3.x ได้รับการปรับให้เหมาะกับประสิทธิภาพ 64 บิตในรูปแบบที่สร้างความเสียหาย 32 บิต

แต่มันสำคัญจริงๆหรือ ลองดูสิด้วย 3.3.0 64- บิตอีกครั้ง:

In [86]: %timeit [x for x in range(10000000) if x%4 == 0]
1 loops, best of 3: 3.65 s per loop

ดังนั้นการสร้างlistใช้เวลานานกว่าการทำซ้ำทั้งหมดมากกว่าสองเท่า

และสำหรับ "ใช้ทรัพยากรมากกว่า Python 2.6+" มากขึ้นจากการทดสอบของฉันดูเหมือนว่า 3.x rangeมีขนาดเท่ากันกับ 2.x อย่างแน่นอนxrangeและถึงแม้ว่ามันจะใหญ่เป็น 10 เท่าสร้างรายการที่ไม่จำเป็น ยังคงมีปัญหามากกว่า 10,000,000 เท่ามากกว่าสิ่งใดที่การทำซ้ำของช่วงสามารถทำได้

แล้วforลูปที่ชัดเจนแทนที่จะเป็นลูป C ด้านในdequeล่ะ

In [87]: def consume(x):
   ....:     for i in x:
   ....:         pass
In [88]: %timeit consume(x for x in range(10000000) if x%4 == 0)
1 loops, best of 3: 1.85 s per loop

ดังนั้นเกือบจะเป็นเวลามากที่สูญเสียไปในforคำสั่งในขณะที่การทำงานจริงของ iteratingrange

หากคุณกังวลเกี่ยวกับการปรับการวนซ้ำของวัตถุให้เหมาะสมที่สุดคุณอาจกำลังมองหาที่ผิด


ในขณะเดียวกันคุณถามว่าทำไมxrangeถูกลบไม่ว่ากี่ครั้งที่มีคนบอกคุณในสิ่งเดียวกัน แต่ฉันจะทำซ้ำอีกครั้ง: มันไม่ถูกลบออก: มันถูกเปลี่ยนชื่อเป็นrangeและ 2.x rangeคือสิ่งที่ถูกลบออก

นี่คือหลักฐานบางอย่างที่ 3.3 rangeวัตถุเป็นทายาทสายตรงของ 2.x xrangeวัตถุ (และไม่ได้ของ 2.x rangeฟังก์ชั่น): แหล่งที่มากับ3.3rangeและ2.7 xrangeคุณสามารถดูประวัติการเปลี่ยนแปลง (เชื่อว่าฉันเชื่อว่าการเปลี่ยนแปลงที่แทนที่อินสแตนซ์สุดท้ายของสตริง "xrange" ที่ใดก็ได้ในไฟล์)

ดังนั้นทำไมช้า?

ทีนี้พวกเขาได้เพิ่มฟีเจอร์ใหม่มากมาย ส่วนอีกพวกเขาทำการเปลี่ยนแปลงทุกอย่างทั่วสถานที่ (โดยเฉพาะในการทำซ้ำ) ที่มีผลข้างเคียงเล็กน้อย และมีงานจำนวนมากในการเพิ่มประสิทธิภาพกรณีสำคัญต่าง ๆ อย่างมากแม้ว่าบางครั้งมันจะมองแง่ร้ายกรณีที่สำคัญน้อยกว่าเล็กน้อย เพิ่มสิ่งนี้เข้าด้วยกันและฉันไม่แปลกใจเลยที่การทำซ้ำให้rangeเร็วที่สุดเท่าที่จะทำได้ตอนนี้ช้าลงเล็กน้อย เป็นหนึ่งในกรณีที่สำคัญน้อยกว่านั้นซึ่งไม่มีใครสนใจพอที่จะให้ความสำคัญ ไม่มีใครน่าจะมีกรณีการใช้งานจริงซึ่งความแตกต่างของประสิทธิภาพนี้เป็นฮอตสปอตในรหัสของพวกเขา


แต่มันช้ากว่า 30% เท่านั้น ยังช้ากว่า แต่เป็นคำตอบที่ยอดเยี่ยมสิ่งที่คิด ไม่ตอบคำถามของฉันแม้ว่า: ทำไม xrange ถูกลบออก? คิดแบบนี้ - ถ้าคุณมีแอพที่ขึ้นอยู่กับประสิทธิภาพโดยอิงจากการประมวลผลหลายตัวรู้ว่าคุณต้องใช้คิวมากแค่ไหนในแต่ละครั้ง 30% จะสร้างความแตกต่างหรือไม่? คุณจะเห็นว่าคุณพูดว่ามันไม่สำคัญ แต่ทุกครั้งที่ฉันใช้ช่วงฉันได้ยินเสียงของแฟน ๆ ที่น่าเวทนาซึ่งหมายความว่า cpu นั้นแย่ที่สุดในขณะที่ xrange ไม่ทำ คิดว่าคุณเกี่ยวกับมัน;)
catalesia

9
@catalesia: rangeอีกครั้งมันก็ไม่ได้เอาออกมันก็เปลี่ยนเพียง rangeวัตถุใน 3.3 เป็นทายาทสายตรงของxrangeวัตถุใน 2.7 ไม่ได้ของrangeฟังก์ชั่นใน 2.7 มันเหมือนกับการถามขณะที่itertools.imapถูกลบออกmapไป ไม่มีคำตอบเพราะไม่มีสิ่งใดเกิดขึ้น
abarnert

1
@catalesia: การเปลี่ยนแปลงประสิทธิภาพเล็กน้อยไม่น่าจะเป็นผลมาจากการตัดสินใจออกแบบโดยตรงที่จะทำให้ช่วงช้าลง แต่ผลข้างเคียงของการเปลี่ยนแปลง 4 ปีทั่ว Python ที่ทำให้หลายอย่างเร็วขึ้นบางสิ่งช้าลงเล็กน้อย (และบางสิ่งบางอย่าง เร็วกว่าใน x86_64 แต่ช้ากว่าใน x86 หรือเร็วกว่าในบางกรณีการใช้งาน แต่จะช้ากว่าในบางกรณี ฯลฯ ) ไม่มีใครน่าเป็นห่วงเกี่ยวกับความแตกต่าง 30% ไม่ว่าจะใช้เวลานานแค่ไหนในการย้ำrangeในขณะที่ไม่ทำอะไรเลย
abarnert

1
"ไม่มีใครน่าเป็นห่วงเกี่ยวกับความแตกต่าง 30% ไม่ว่าจะใช้ระยะเวลาเท่าใดในขณะที่ไม่ต้องทำอะไรเลย "
catalesia

18
@catalesia: ใช่แน่นอน แต่คุณคิดว่านั่นหมายถึงสิ่งที่ตรงกันข้ามกับที่กล่าวไว้ มันไม่ใช่กรณีใช้งานที่ใคร ๆ ก็จะสนใจไม่มีใครสังเกตเห็นว่ามันช้าลง 30% แล้วอะไรล่ะ หากคุณพบโปรแกรมในชีวิตจริงที่ทำงานช้ากว่าใน Python 3.3 มากกว่าใน 2.7 (หรือ 2.6) ด้วยเหตุนี้ผู้คนจะใส่ใจ หากคุณทำไม่ได้พวกเขาก็จะไม่ทำเช่นนั้น
abarnert

141

ช่วง Python3 ของเป็น xrange Python2 ของ ไม่จำเป็นต้องปิดล้อมรอบ ในการรับรายการจริงใน Python3 คุณต้องใช้list(range(...))

หากคุณต้องการบางสิ่งที่ทำงานร่วมกับ Python2 และ Python3 ลองสิ่งนี้

try:
    xrange
except NameError:
    xrange = range

1
บางครั้งคุณต้องใช้รหัสที่ใช้ได้ทั้ง Python 2 และ 3 นี่เป็นทางออกที่ดี
Greg Glockner

3
ปัญหาคือด้วยรหัสนี้ที่ใช้ทั้งสองrangeและxrangeจะทำงานแตกต่างกัน ยังไม่พอที่จะทำเช่นนี้เราต้องแน่ใจว่าจะไม่คิดว่าrangeจะส่งคืนรายการ (เช่นเดียวกับใน python 2)
LangeHaare

คุณสามารถใช้ xrange ได้จากโครงการนี้ มีfuturizeเครื่องมือในการแปลงซอร์สโค้ดของคุณโดยอัตโนมัติ: python-future.org/ …
guettli

17

งูหลาม 3 rangeประเภททำงานเช่นเดียวกับงูหลาม xrange2 ฉันไม่แน่ใจว่าทำไมคุณถึงเห็นการชะลอตัวเนื่องจากตัววนซ้ำที่ส่งคืนโดยxrangeฟังก์ชันของคุณเป็นสิ่งที่คุณจะได้รับหากคุณวนซ้ำrangeโดยตรง

ฉันไม่สามารถทำซ้ำการชะลอตัวในระบบของฉัน นี่คือวิธีที่ฉันทดสอบ:

Python 2 ด้วยxrange:

Python 2.7.3 (default, Apr 10 2012, 23:24:47) [MSC v.1500 64 bit (AMD64)] on win32
Type "copyright", "credits" or "license()" for more information.
>>> import timeit
>>> timeit.timeit("[x for x in xrange(1000000) if x%4]",number=100)
18.631936646865853

Python 3 ซึ่งrangeเร็วขึ้นเล็กน้อย:

Python 3.3.0 (v3.3.0:bd8afb90ebf2, Sep 29 2012, 10:57:17) [MSC v.1600 64 bit (AMD64)] on win32
Type "copyright", "credits" or "license()" for more information.
>>> import timeit
>>> timeit.timeit("[x for x in range(1000000) if x%4]",number=100)
17.31399508687869

ฉันเพิ่งเรียนรู้ว่าrangeประเภทของ Python 3 มีคุณสมบัติที่เป็นระเบียบอื่น ๆ เช่นการสนับสนุนการแบ่งส่วน: range(10,100,2)[5:25:5]เป็นrange(20, 60, 10)!


บางทีการชะลอตัวอาจมาจากการค้นหาใหม่xrangeหลายครั้งหรือทำเพียงครั้งเดียว?
askewchan

ตัววนซ้ำเพิ่มความเร็วจริงหรือไม่? ฉันคิดว่ามันแค่บันทึกความทรงจำ
askewchan

3
@catalesia ผมคิดว่าจุดที่นี่คือการที่xrangeได้รับไม่ได้เอาออกเพียงแค่เปลี่ยนชื่อ
askewchan

1
@Blckknght: ไชโย แต่ก็ยังคงมีคำอธิบายที่ชอบของ: "ตั้งค่าตัวอักษรและความเข้าใจ [19] [20] [เสร็จ] {x} หมายถึงชุด ([x]); {x, y} หมายถึงชุด ([ x, y]). {F (x) สำหรับ x ใน S ถ้า P (x)} หมายถึง set (F (x) สำหรับ x ใน S ถ้า P (x)). NB. {range (x)} หมายถึง set ( [range (x)], ไม่ได้ตั้งค่า (range (x)) ไม่มีตัวอักษรสำหรับชุดที่ว่างเปล่าให้ใช้ set () (หรือ {1} & {2} :-) ไม่มีตัวอักษรที่หลากหลาย ไม่ค่อยจำเป็น "
catalesia

3
ชนะที่ยิ่งใหญ่ที่สุดใน 3.x เท่าที่ผมกังวลคือค่าคงที่เวลาrange __contains__มือใหม่ใช้ในการเขียน300000 in xrange(1000000)และทำให้มันย้ำทั้งหมดxrange(หรืออย่างน้อย 30% แรก) ดังนั้นเราต้องอธิบายว่าทำไมมันถึงเป็นความคิดที่ไม่ดีแม้ว่ามันจะดูไพเราะก็ตาม ตอนนี้มันเป็น pythonic
abarnert

1

วิธีหนึ่งในการแก้ไขรหัส python2 ของคุณคือ:

import sys

if sys.version_info >= (3, 0):
    def xrange(*args, **kwargs):
        return iter(range(*args, **kwargs))

1
จุดอยู่ใน python3 xrange ไม่ได้ถูกกำหนดดังนั้นรหัสดั้งเดิมที่ใช้การแบ่ง xrange
andrew pate

ไม่เพียงแค่นิยามrange = xrangeตามที่อยู่ในความคิดเห็นโดย
@John

@ mimi.vx ไม่แน่ใจว่า range = xrange จะทำงานใน Python3 เพราะไม่ได้กำหนด xrange ความคิดเห็นของฉันอ้างถึงกรณีที่คุณมีรหัสเดิมที่มีการเรียก xrange และคุณพยายามทำให้มันทำงานภายใต้ python3
แอนดรู pate

1
อ๊ะฉันไม่ดี .. xrange = range... ฉันเปลี่ยนข้อความไปแล้ว
mimi.vx

range is aiterator และนี่ก็เป็นความคิดที่แย่มากแม้ว่ามันจะไม่ได้ทำเพราะมันจะต้องทำการแกะทั้งช่วงออกมาก่อนและเสียข้อดีของการใช้ตัววนซ้ำสำหรับสิ่งนี้ ดังนั้นคำตอบที่ถูกต้องไม่ใช่ "range = xrange" "xrange = range" ของมัน
Shayne

0

xrange จาก Python 2 เป็นตัวกำเนิดและใช้ตัววนซ้ำในขณะที่ range เป็นเพียงฟังก์ชัน ใน Python3 ฉันไม่รู้ว่าทำไมถูกปล่อยออกจาก xrange


ไม่ช่วงไม่ใช่ตัวเชื่อมต่อ คุณไม่สามารถทำสิ่งต่อไป () ด้วยโครงสร้างนี้ สำหรับข้อมูลเพิ่มเติมคุณสามารถตรวจสอบได้ที่นี่treyhunner.com/2018/02/python-range-is-not-an-iterator
Michel Fernandes

ขอบคุณมากสำหรับคำชี้แจง แต่ผมจะย้ำความตั้งใจที่จะแสดงความคิดเห็นเดิมและที่เป็นที่ PY3 range()เป็นเทียบเท่าของ xrange()PY2 และใน PY3 xrange()นั้นซ้ำซ้อน
Stephen Rauch

-2

comp: ~ $ python Python 2.7.6 (ค่าเริ่มต้น, 22 มิ.ย. 2558, 17:58:13) [GCC 4.8.2] บน linux2

>>> import timeit
>>> timeit.timeit("[x for x in xrange(1000000) if x%4]",number=100)

5.656799077987671

>>> timeit.timeit("[x for x in xrange(1000000) if x%4]",number=100)

5.579368829727173

>>> timeit.timeit("[x for x in range(1000000) if x%4]",number=100)

21.54827117919922

>>> timeit.timeit("[x for x in range(1000000) if x%4]",number=100)

22.014557123184204

ด้วย timeit number = 1 param:

>>> timeit.timeit("[x for x in range(1000000) if x%4]",number=1)

0.2245171070098877

>>> timeit.timeit("[x for x in xrange(1000000) if x%4]",number=1)

.10750913619995117

คอมพ์: ~ $ python3 Python 3.4.3 (ค่าเริ่มต้น, 14 ต.ค. 2558, 20:28:29) [GCC 4.8.4] บน linux

>>> timeit.timeit("[x for x in range(1000000) if x%4]",number=100)

9.113872020003328

>>> timeit.timeit("[x for x in range(1000000) if x%4]",number=100)

9.07014398300089

ด้วย timeit number = 1,2,3,4 param ทำงานได้อย่างรวดเร็วและเป็นแบบเชิงเส้น:

>>> timeit.timeit("[x for x in range(1000000) if x%4]",number=1)

.09329321900440846

>>> timeit.timeit("[x for x in range(1000000) if x%4]",number=2)

.18501482300052885

>>> timeit.timeit("[x for x in range(1000000) if x%4]",number=3)

0.2703447980020428

>>> timeit.timeit("[x for x in range(1000000) if x%4]",number=4)

.36209142999723554

ดูเหมือนว่าถ้าเราวัด 1 วนรอบลูปเช่น timeit.timeit ("[x สำหรับ x ในช่วง (1000000) ถ้า x% 4]", จำนวน = 1) (ตามที่เราใช้จริงในรหัสจริง) python3 ทำงานได้เร็วพอ แต่ในลูปซ้ำแล้วซ้ำอีกไพ ธ อน 2 xrange () ชนะด้วยความเร็วเทียบกับระยะ () จากไพ ธ อน 3


แต่นี่เป็นภาษาของตัวเอง ... ไม่มีส่วนเกี่ยวข้องกับ xrange / range
mimi.vx
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.