Tại sao không có hàm xrange trong Python3?


273

Gần đây tôi bắt đầu sử dụng Python3 và nó thiếu các tổn thương xrange.

Ví dụ đơn giản:

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

Kết quả lần lượt là:

1) 1.53888392448 2) 3.215819835662842

Tại sao vậy? Ý tôi là, tại sao xrange lại bị xóa? Đó là một công cụ tuyệt vời để học hỏi. Đối với người mới bắt đầu, giống như tôi, như tất cả chúng ta đã ở một thời điểm nào đó. Tại sao phải loại bỏ nó? Ai đó có thể chỉ cho tôi PEP thích hợp không, tôi không thể tìm thấy nó.

Chúc mừng.


231
rangetrong Python 3.x là xrangetừ Python 2.x. Trên thực tế, Python 2.x rangeđã bị xóa.
Anorov

27
PS, bạn không bao giờ nên có thời gian với time. Bên cạnh việc dễ sử dụng hơn và khó mắc lỗi hơn và lặp lại các bài kiểm tra cho bạn, hãy timeitquan tâm đến tất cả những điều bạn sẽ không nhớ, hoặc thậm chí biết cách chăm sóc (như vô hiệu hóa GC) và có thể sử dụng đồng hồ với độ phân giải tốt hơn hàng ngàn lần.
abarnert

7
Ngoài ra, tại sao bạn kiểm tra thời gian để lọc rangetrên x%4 == 0? Tại sao không chỉ kiểm tra list(xrange())so với list(range()), vì vậy có càng ít công việc ngoại lai càng tốt? (Ví dụ: làm thế nào để bạn biết 3.x không hoạt động x%4chậm hơn?) Đối với vấn đề đó, tại sao bạn xây dựng một khối lượng lớn list, bao gồm toàn bộ phân bổ bộ nhớ (ngoài việc chậm, cũng rất khác nhau) ?
abarnert

5
Xem docs.python.org/3.0/whatsnew/3.0.html , phần "Lượt xem và trình lặp thay vì danh sách": "phạm vi () hiện hành xử như xrange () được sử dụng để hành xử, ngoại trừ nó hoạt động với các giá trị có kích thước tùy ý. không còn tồn tại." Vì vậy, phạm vi bây giờ trả về một iterator. iter(range)là dư thừa.
ToolmakerSteve 14/12/13

9
Xin lỗi, nhận ra trích dẫn tài liệu thay đổi không làm cho nó rõ ràng rõ ràng. Đối với bất kỳ ai khác đang bối rối và không muốn đọc qua câu trả lời được chấp nhận từ lâu và tất cả các nhận xét của nó: Bất cứ nơi nào bạn đang sử dụng xrange trong python 2, hãy sử dụng phạm vi trong python 3. Nó thực hiện những gì xrange sử dụng, đó là trả về một vòng lặp. Nếu bạn cần kết quả trong một danh sách, hãy làm list(range(..)). Điều đó tương đương với phạm vi của python 2. Hay nói cách khác: xrange đã được đổi tên thành phạm vi, vì đó là mặc định tốt hơn; không cần thiết phải có cả hai, làm list(range)nếu bạn thực sự cần một danh sách. .
ToolmakerSteve 14/12/13

Câu trả lời:


175

Một số phép đo hiệu suất, sử dụng timeitthay vì cố gắng thực hiện bằng tay time.

Đầu tiên, Apple 2.7.2 64 bit:

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

Bây giờ, python.org 3.3.0 64-bit:

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

Rõ ràng, 3.x rangethực sự là chậm hơn so với 2.x một chút xrange. Và xrangechức năng của OP không có gì để làm với nó. (Không có gì đáng ngạc nhiên, vì một cuộc gọi một lần đến __iter__vị trí không có khả năng hiển thị trong số 10000000 cuộc gọi đến bất kỳ điều gì xảy ra trong vòng lặp, nhưng ai đó đã đưa ra như một khả năng.)

Nhưng nó chỉ chậm hơn 30%. Làm thế nào mà OP có được gấp 2 lần chậm? Chà, nếu tôi lặp lại các bài kiểm tra tương tự với Python 32 bit, tôi nhận được 1,58 so với 3,12. Vì vậy, tôi đoán rằng đây là một trường hợp khác trong đó 3.x đã được tối ưu hóa cho hiệu suất 64 bit theo cách làm tổn thương 32 bit.

Nhưng nó thực sự quan trọng? Hãy xem điều này, với 3.3.0 64 bit một lần nữa:

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

Vì vậy, việc xây dựng listmất nhiều hơn gấp đôi so với toàn bộ lần lặp.

Và đối với "tiêu thụ nhiều tài nguyên hơn Python 2.6+", từ các thử nghiệm của tôi, có vẻ như 3.x rangecó kích thước chính xác như 2.x xrange, và ngay cả khi nó lớn gấp 10 lần, xây dựng danh sách không cần thiết vẫn còn khoảng 10000000x vấn đề hơn bất cứ điều gì mà phép lặp phạm vi có thể làm.

Và những gì về một forvòng lặp rõ ràng thay vì vòng lặp C bên trong 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

Vì vậy, gần như lãng phí thời gian trong fortuyên bố như trong công việc thực tế của việc lặp lại range.

Nếu bạn lo lắng về việc tối ưu hóa việc lặp lại của một đối tượng phạm vi, có lẽ bạn đang tìm sai chỗ.


Trong khi đó, bạn cứ hỏi tại sao lại xrangebị xóa, bất kể mọi người nói với bạn điều tương tự bao nhiêu lần, nhưng tôi sẽ nhắc lại lần nữa: Nó không bị xóa: nó được đổi tên thành rangevà 2.x rangelà những gì đã bị xóa.

Dưới đây là một số bằng chứng cho thấy rangeđối tượng 3.3 là hậu duệ trực tiếp của xrangeđối tượng 2.x (chứ không phải của rangechức năng 2.x ): nguồn tới 3.3range2.7xrange . Bạn thậm chí có thể thấy lịch sử thay đổi (tôi tin rằng, sự thay đổi đã thay thế phiên bản cuối cùng của chuỗi "xrange" ở bất kỳ đâu trong tệp).

Vì vậy, tại sao nó chậm hơn?

Chà, trước tiên, họ đã thêm rất nhiều tính năng mới. Mặt khác, họ đã thực hiện tất cả các loại thay đổi ở mọi nơi (đặc biệt là trong vòng lặp) có tác dụng phụ nhỏ. Và đã có rất nhiều công việc để tối ưu hóa đáng kể các trường hợp quan trọng khác nhau, ngay cả khi đôi khi nó hơi bi quan những trường hợp ít quan trọng hơn. Thêm tất cả điều này lên, và tôi không ngạc nhiên rằng việc lặp lại rangecàng nhanh càng tốt bây giờ chậm hơn một chút. Đó là một trong những trường hợp ít quan trọng mà không ai quan tâm đủ để tập trung vào. Không ai có thể có trường hợp sử dụng thực tế trong đó sự khác biệt hiệu suất này là điểm nóng trong mã của họ.


Nhưng nó chỉ chậm hơn 30%. Vẫn chậm hơn, nhưng một người bạn đời phản ứng tuyệt vời, một cái gì đó để suy nghĩ. Nó không trả lời câu hỏi của tôi mặc dù: tại sao xrange bị xóa ?? Hãy suy nghĩ về nó theo cách này - nếu bạn có một ứng dụng phụ thuộc vào hiệu suất dựa trên đa xử lý biết bạn cần bao nhiêu hàng đợi một lần, 30% có tạo ra sự khác biệt hay không? Bạn thấy đấy, bạn nói nó không thành vấn đề, nhưng mỗi khi tôi sử dụng phạm vi, tôi nghe thấy tiếng quạt đau khổ lớn có nghĩa là cpu là điều tồi tệ nhất, trong khi xrange không làm điều đó. Hãy suy nghĩ về điều đó;)
catalesia

9
@catalesia: Một lần nữa, nó không bị xóa, nó chỉ được đổi tên range. Đối rangetượng trong 3.3 là hậu duệ trực tiếp của xrangeđối tượng trong 2.7, không phải là rangechức năng trong 2.7. Nó giống như hỏi trong khi itertools.imapđược gỡ bỏ có lợi cho map. Không có câu trả lời, bởi vì không có điều đó xảy ra.
abarnert

1
@catalesia: Các thay đổi hiệu suất nhỏ có lẽ không phải là kết quả của quyết định thiết kế trực tiếp làm cho phạm vi chậm hơn, nhưng tác dụng phụ của 4 năm thay đổi trên Python đã khiến nhiều thứ nhanh hơn, một số thứ chậm hơn một chút (và một số thứ nhanh hơn trên x86_64 nhưng chậm hơn trên x86 hoặc nhanh hơn trong một số trường hợp sử dụng nhưng chậm hơn ở những trường hợp khác, v.v.). Không ai có thể lo lắng về sự khác biệt 30% dù là mất bao lâu để lặp đi lặp lại rangetrong khi không làm gì khác.
abarnert

1
"Không ai có thể lo lắng về sự khác biệt 30% dù là mất bao lâu để lặp lại một phạm vi trong khi không làm gì khác. " Chính xác.
catalesia

18
@catalesia: Vâng, chính xác. Nhưng bạn dường như nghĩ rằng điều đó có nghĩa ngược lại với những gì nó nói. Đây không phải là trường hợp sử dụng mà bất cứ ai cũng sẽ quan tâm, vì vậy không ai nhận thấy rằng nó chậm hơn 30%. Vậy thì sao? Nếu bạn có thể tìm thấy một chương trình thực tế chạy chậm hơn trong Python 3.3 so với 2.7 (hoặc 2.6) vì điều này, mọi người sẽ quan tâm. Nếu bạn không thể, họ sẽ không, và bạn cũng không nên.
abarnert

141

Phạm vi Python3 của xrange python2 của. Không cần phải quấn một vòng lặp xung quanh nó. Để có được một danh sách thực tế trong Python3, bạn cần sử dụnglist(range(...))

Nếu bạn muốn một cái gì đó hoạt động với Python2 và Python3, hãy thử điều này

try:
    xrange
except NameError:
    xrange = range

1
Đôi khi bạn cần mã hoạt động trong cả Python 2 và 3. Đây là một giải pháp tốt.
Greg Glockner

3
Vấn đề là với điều này, mã sử dụng cả hai rangexrangesẽ hành xử khác nhau. Nó không đủ để làm điều này, người ta cũng phải đảm bảo không bao giờ cho rằng nó rangeđang trả về một danh sách (như trong python 2).
LangeHaare

Bạn có thể sử dụng xrange từ dự án này. Có futurizecông cụ để tự động chuyển đổi mã nguồn của bạn: python-future.org/ từ
guettli 22/03/18

17

rangeKiểu của Python 3 hoạt động giống như của Python 2 xrange. Tôi không chắc tại sao bạn thấy chậm lại, vì trình lặp được trả về bởi xrangehàm của bạn chính xác là những gì bạn nhận được nếu bạn lặp lại rangetrực tiếp.

Tôi không thể tái tạo sự chậm lại trên hệ thống của mình. Đây là cách tôi đã thử nghiệm:

Python 2, với 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, với rangetốc độ nhanh hơn một chút:

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

Gần đây tôi đã biết rằng loại Python 3 rangecó một số tính năng gọn gàng khác, chẳng hạn như hỗ trợ cho việc cắt lát: range(10,100,2)[5:25:5]range(20, 60, 10)!


Có lẽ sự chậm lại đến từ việc tra cứu cái mới xrangerất nhiều lần, hoặc điều đó chỉ được thực hiện một lần?
askewchan

Liệu một iterator thực sự tăng tốc độ nào? Tôi nghĩ rằng nó chỉ lưu bộ nhớ.
askewchan

3
@catalesia Tôi nghĩ rằng điểm ở đây là xrangeđã không bị loại bỏ, chỉ cần đổi tên .
askewchan

1
@Blckknght: Chúc mừng, nhưng nó vẫn không có lời giải thích như: "Đặt nghĩa đen và hiểu [19] [20] [xong] {x} có nghĩa là đặt ([x]); {x, y} có nghĩa là đặt ([ x, y]). {F (x) cho x trong S nếu P (x)} có nghĩa là đặt (F (x) cho x trong S nếu P (x)). NB. {phạm vi (x)} có nghĩa là đặt ( [phạm vi (x)]), kHÔNG set (phạm vi (x)) không có nghĩa đen cho một tập rỗng;.. sử dụng set () (hoặc {1} & {2} :-) không có frozenset đen; họ quá hiếm khi cần thiết. "
catalesia

3
Chiến thắng lớn nhất trong 3.x range, theo như tôi nghĩ, là thời gian không đổi __contains__. Người mới sử dụng để viết 300000 in xrange(1000000)và điều đó khiến nó lặp đi lặp lại toàn bộ xrange(hoặc ít nhất là 30% đầu tiên của nó), vì vậy chúng tôi phải giải thích tại sao đó là một ý tưởng tồi, mặc dù nó trông rất trớ trêu. Bây giờ, nó pythonic.
abarnert

1

Một cách để sửa mã python2 của bạn là:

import sys

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

1
Điểm nằm trong python3 xrange không được xác định, do đó, mã kế thừa đã sử dụng ngắt xrange.
pate

không, chỉ cần định nghĩa range = xrangenhư trong nhận xét của @John La Roy
mimi.vx

@ mimi.vx Không chắc chắn phạm vi = xrange sẽ hoạt động trong Python3 vì xrange không được xác định. Nhận xét của tôi đề cập đến trường hợp bạn có mã kế thừa cũ có chứa các cuộc gọi xrange VÀ bạn đang cố gắng để nó chạy theo python3.
pate

1
Ah, xấu của tôi .. xrange = range... tôi đã chuyển báo cáo
mimi.vx

phạm vi một iiterator, và dù sao đây cũng sẽ là một ý tưởng tồi tệ ngay cả khi nó không phải bởi vì nó phải giải nén toàn bộ phạm vi trước và mất đi những lợi thế của việc sử dụng một trình vòng lặp cho loại điều này. Vì vậy, câu trả lời đúng không phải là "phạm vi = xrange" của nó "xrange = phạm vi"
Shayne

0

xrange từ Python 2 là một trình tạo và thực hiện iterator trong khi phạm vi chỉ là một hàm. Trong Python3 tôi không biết tại sao lại bỏ xrange.


Không, phạm vi không phải là một interator. Bạn không thể làm tiếp () với cấu trúc này. Để biết thêm thông tin, bạn có thể kiểm tra tại đây treyhunner.com/2018/02/python-range-is-not-an-iterator
Michel Fernandes

Cảm ơn bạn rất nhiều vì đã làm rõ. Nhưng tôi sẽ nói lại ý định của nhận xét ban đầu, và đó là PY3 range()tương đương với PY2 xrange(). Và do đó trong PY3 xrange()là dư thừa.
Stephen Rauch

-2

comp: ~ $ python Python 2.7.6 (mặc định, ngày 22 tháng 6 năm 2015, 17:58:13) [GCC 4.8.2] trên 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

Với số lượng thời gian = 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)

0.10750913619995117

comp: ~ $ python3 Python 3.4.3 (mặc định, ngày 14 tháng 10 năm 2015, 20:28:29) [GCC 4.8.4] trên 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

Với số lượng thời gian = 1,2,3,4 param hoạt động nhanh chóng và theo cách tuyến tính:

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

0,09329321900440846

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

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

0,36209142999723554

Vì vậy, có vẻ như nếu chúng ta đo 1 chu kỳ vòng lặp đang chạy như timeit.timeit ("[x cho x trong phạm vi (1000000) nếu x% 4]", number = 1) (như chúng ta thực sự sử dụng trong mã thực) python3 hoạt động đủ nhanh, nhưng trong các vòng lặp lặp lại, python 2 xrange () thắng tốc độ so với phạm vi () từ python 3.


nhưng đây là do ngôn ngữ tự nó ... không có gì để làm với xrange / phạm vi.
mimi.vx
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.