Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
160 views
in Technique[技术] by (71.8m points)

python - 为什么在Python 3中“范围(1000000000000000(1000000000000001))”这么快?(Why is “1000000000000000 in range(1000000000000001)” so fast in Python 3?)

It is my understanding that the range() function, which is actually an object type in Python 3 , generates its contents on the fly, similar to a generator.

(据我了解, range()函数实际上是Python 3中的一种对象类型 ,它像生成器一样动态生成其内容。)

This being the case, I would have expected the following line to take an inordinate amount of time, because in order to determine whether 1 quadrillion is in the range, a quadrillion values would have to be generated:

(在这种情况下,我本以为下一行会花费过多的时间,因为要确定1个四舍五入是否在该范围内,必须生成一个四舍五入值:)

1000000000000000 in range(1000000000000001)

Furthermore: it seems that no matter how many zeroes I add on, the calculation more or less takes the same amount of time (basically instantaneous).

(此外:似乎无论我添加多少个零,计算多少都花费相同的时间(基本上是瞬时的)。)

I have also tried things like this, but the calculation is still almost instant:

(我也尝试过这样的事情,但是计算仍然是即时的:)

1000000000000000000000 in range(0,1000000000000000000001,10) # count by tens

If I try to implement my own range function, the result is not so nice!!

(如果我尝试实现自己的范围函数,结果将不是很好!)

def my_crappy_range(N):
    i = 0
    while i < N:
        yield i
        i += 1
    return

What is the range() object doing under the hood that makes it so fast?

(使range()如此之快的对象是做什么的?)


Martijn Pieters' answer was chosen for its completeness, but also see abarnert's first answer for a good discussion of what it means for range to be a full-fledged sequence in Python 3, and some information/warning regarding potential inconsistency for __contains__ function optimization across Python implementations.

(选择Martijn Pieters的答案是因为它的完整性,但也可以看到abarnert的第一个答案 ,它很好地讨论了range在Python 3中成为完整序列的含义,以及有关__contains__函数优化可能存在不一致的一些信息/警告。 Python实现。)

abarnert's other answer goes into some more detail and provides links for those interested in the history behind the optimization in Python 3 (and lack of optimization of xrange in Python 2).

(abarnert的其他答案更加详细,并为那些对Python 3优化背后的历史(以及Python 2中缺少xrange优化)感兴趣的人提供了链接。)

Answers by poke and by wim provide the relevant C source code and explanations for those who are interested.

(pokewim的答案 感兴趣的人提供了相关的C源代码和说明。)

  ask by Rick supports Monica translate from so

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

The Python 3 range() object doesn't produce numbers immediately;

(Python 3 range()对象不会立即产生数字。)

it is a smart sequence object that produces numbers on demand .

(它是一个智能序列对象,可按需生成数字。)

All it contains is your start, stop and step values, then as you iterate over the object the next integer is calculated each iteration.

(它所包含的只是您的开始,结束和步长值,然后在对对象进行迭代时,每次迭代都会计算出下一个整数。)

The object also implements the object.__contains__ hook , and calculates if your number is part of its range.

(该对象还实现object.__contains__钩子 ,并计算您的数字是否属于其范围。)

Calculating is a O(1) constant time operation.

(计算是O(1)恒定时间操作。)

There is never a need to scan through all possible integers in the range.

(永远不需要扫描范围内的所有可能整数。)

From the range() object documentation :

(从range()对象文档中 :)

The advantage of the range type over a regular list or tuple is that a range object will always take the same (small) amount of memory, no matter the size of the range it represents (as it only stores the start , stop and step values, calculating individual items and subranges as needed).

(与常规listtuple相比, range类型的优势在于,范围对象将始终占用相同(少量)的内存,无论其表示的范围大小如何(因为它仅存储startstopstep值) ,根据需要计算单个项目和子范围)。)

So at a minimum, your range() object would do:

(因此,至少,您的range()对象可以:)

class my_range(object):
    def __init__(self, start, stop=None, step=1):
        if stop is None:
            start, stop = 0, start
        self.start, self.stop, self.step = start, stop, step
        if step < 0:
            lo, hi, step = stop, start, -step
        else:
            lo, hi = start, stop
        self.length = 0 if lo > hi else ((hi - lo - 1) // step) + 1

    def __iter__(self):
        current = self.start
        if self.step < 0:
            while current > self.stop:
                yield current
                current += self.step
        else:
            while current < self.stop:
                yield current
                current += self.step

    def __len__(self):
        return self.length

    def __getitem__(self, i):
        if i < 0:
            i += self.length
        if 0 <= i < self.length:
            return self.start + i * self.step
        raise IndexError('Index out of range: {}'.format(i))

    def __contains__(self, num):
        if self.step < 0:
            if not (self.stop < num <= self.start):
                return False
        else:
            if not (self.start <= num < self.stop):
                return False
        return (num - self.start) % self.step == 0

This is still missing several things that a real range() supports (such as the .index() or .count() methods, hashing, equality testing, or slicing), but should give you an idea.

(这仍然缺少真正的range()支持的几件事(例如.index().count()方法,哈希,相等性测试或切片),但应该给您一个提示。)

I also simplified the __contains__ implementation to only focus on integer tests;

(我还简化了__contains__实现,只专注于整数测试。)

if you give a real range() object a non-integer value (including subclasses of int ), a slow scan is initiated to see if there is a match, just as if you use a containment test against a list of all the contained values.

(如果为实的range()对象提供非整数值(包括int子类),则会启动慢速扫描以查看是否存在匹配项,就像您对包含的所有值的列表使用了包含测试。)

This was done to continue to support other numeric types that just happen to support equality testing with integers but are not expected to support integer arithmetic as well.

(这样做是为了继续支持其他数字类型,这些数字类型恰好支持整数的相等性测试,但预计也不支持整数算术。)

See the original Python issue that implemented the containment test.

(请参阅实现收容测试的原始Python问题 。)


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...