This article brings you what is range()? Why not produce iterators? It has certain reference value. Friends in need can refer to it. I hope it will be helpful to you.
The iterator is one of the most commonly used (one of) the 23 design patterns. It can be seen everywhere in Python. We often use it, but we are not necessarily aware of its existence. In my series on iterators (link at end of article), I mentioned at least 23 ways to generate iterators. Some methods are specifically used to generate iterators, and some methods "secretly" use iterators to solve other problems.
Before the system learned about iterators, I always thought that the range() method was also used to generate iterators, but now I suddenly discovered that it only generates iterable objects, not iterators! (PS: range() in Python2 generates a list, this article is based on Python3, and generates an iterable object)
So, I have this question: Why does range() not generate an iterator? In the process of looking for answers, I found that I had some misunderstandings about the range type. Therefore, this article will give you a comprehensive understanding of range, and I look forward to learning and making progress together with you.
1. What is range()?
Its syntax: range(start, stop [,step]); start refers to the starting value of counting, the default is 0; stop refers to the end value of counting, but does not include stop; step is the step size, the default is 1, and cannot be 0. The range() method generates a range of integers that is closed on the left and open on the right.
>>> a = range(5) # 即 range(0,5) >>> a range(0, 5) >>> len(a) 5 >>> for x in a: >>> print(x,end=" ") 0 1 2 3 4
There are several points to note about the range() function: (1) It represents a left-closed and right-open interval; (2) The parameter it receives must be an integer, which can be a negative number, but cannot be Floating point numbers and other types; (3) It is an immutable sequence type that can perform operations such as judging elements, finding elements, slicing, etc., but cannot modify elements; (4) It is an iterable object, but not an iterator.
# (1)左闭右开 >>> for i in range(3, 6): >>> print(i,end=" ") 3 4 5 # (2)参数类型 >>> for i in range(-8, -2, 2): >>> print(i,end=" ") -8 -6 -4 >>> range(2.2) ---------------------------- TypeError Traceback (most recent call last) ... TypeError: 'float' object cannot be interpreted as an integer # (3)序列操作 >>> b = range(1,10) >>> b[0] 1 >>> b[:-3] range(1, 7) >>> b[0] = 2 TypeError Traceback (most recent call last) ... TypeError: 'range' object does not support item assignment # (4)不是迭代器 >>> hasattr(range(3),'__iter__') True >>> hasattr(range(3),'__next__') False >>> hasattr(iter(range(3)),'__next__') True
2. Why does range() not produce an iterator?
There are many built-in methods to get iterators, such as zip(), enumerate(), map(), filter(), reversed(), etc., but range() only gets The method of an iterable object is unique (if there are any counterexamples, please let us know). This is where I have a misunderstanding of my knowledge.
During for-loop traversal, the performance of iterable objects and iterators is the same, that is, they are both lazily evaluated, and there is no difference in space complexity and time complexity. I once summarized the difference between the two as "the same thing but two different things": the same thing is that both can be iterated lazily, but the difference is that the iterable object does not support self-traversal (i.e. next() method), and the iterator itself does not support slicing (i.e. __getitem__()
method).
Despite these differences, it is difficult to conclude which one is better. Now the subtlety is, why are iterators designed for all five built-in methods, but the range() method is designed to be an iterable object? Wouldn't it be better to unify them all?
In fact, Python has done a lot of this kind of thing for the sake of standardization. For example, there are two methods range() and xrange() in Python2, but Python3 has eliminated one of them and used "Li Dai Tao Jian" method. Why not be more formal and make range() generate an iterator?
Regarding this issue, I have not found an official explanation. The following is purely personal opinion.
zip() and other methods need to receive the parameters of certain iterable objects, which is a process of reprocessing them. Therefore, they also hope to produce certain results immediately, so Python developers design The result is an iterator. This also has the advantage that when the iterable object used as a parameter changes, the iterator used as the result will not be used incorrectly because it is consumable.
The range() method is different. The parameter it receives is not an iterable object. It is an initial processing process, so it is designed as an iterable object, which can be used directly or for Other reprocessing uses. For example, methods such as zip() can accept range type parameters.
>>> for i in zip(range(1,6,2), range(2,7,2)): >>> print(i, end="") (1, 2)(3, 4)(5, 6)
In other words, the range() method is a primary producer, and the raw materials it produces have great uses. Turning it into an iterator early would undoubtedly be superfluous.
Do you think this interpretation makes sense? Feel free to discuss this topic with me.
3. What is the range type?
The above is my answer to "Why range() does not generate an iterator". Following this idea, I studied the range object it generated, and upon research, I found that this range object is not simple.
The first strange thing is that it is an immutable sequence! I never noticed this. Although I have never thought about modifying the value of range(), this unmodifiable feature still surprised me.
Looking through the documentation, the official division is as follows -There are three basic sequence types: list, tuple and range object. (There are three basic sequence types: lists, tuples, and range objects.)
这我倒一直没注意,原来 range 类型居然跟列表和元组是一样地位的基础序列!我一直记挂着字符串是不可变的序列类型,不曾想,这里还有一位不可变的序列类型呢。
那 range 序列跟其它序列类型有什么差异呢?
普通序列都支持的操作有 12 种,在《你真的知道Python的字符串是什么吗?》这篇文章里提到过。range 序列只支持其中的 10 种,不支持进行加法拼接与乘法重复。
>>> range(2) + range(3) ----------------------------------------- TypeError Traceback (most recent call last) ... TypeError: unsupported operand type(s) for +: 'range' and 'range' >>> range(2)*2 ----------------------------------------- TypeError Traceback (most recent call last) ... TypeError: unsupported operand type(s) for *: 'range' and 'int'
那么问题来了:同样是不可变序列,为什么字符串和元组就支持上述两种操作,而偏偏 range 序列不支持呢?虽然不能直接修改不可变序列,但我们可以将它们拷贝到新的序列上进行操作啊,为何 range 对象连这都不支持呢?
且看官方文档的解释:
...due to the fact that range objects can only represent sequences that follow a strict pattern and repetition and concatenation will usually violate that pattern.原因是 range 对象仅仅表示一个遵循着严格模式的序列,而重复与拼接通常会破坏这种模式...
问题的关键就在于 range 序列的 pattern,仔细想想,其实它表示的就是一个等差数列啊(喵,高中数学知识没忘...),拼接两个等差数列,或者重复拼接一个等差数列,想想确实不妥,这就是为啥 range 类型不支持这两个操作的原因了。由此推论,其它修改动作也会破坏等差数列结构,所以统统不给修改就是了。
4、小结
回顾全文,我得到了两个偏冷门的结论:range 是可迭代对象而不是迭代器;range 对象是不可变的等差序列。
若单纯看结论的话,你也许没有感触,或许还会说这没啥了不得啊。但如果我追问,为什么 range 不是迭代器呢,为什么 range 是不可变序列呢?对这俩问题,你是否还能答出个自圆其说的设计思想呢?(PS:我决定了,若有机会面试别人,我必要问这两个问题的嘿~)
由于 range 对象这细微而有意思的特性,我觉得这篇文章写得值了。本文是作为迭代器系列文章的一篇来写的,所以对于迭代器的基础知识介绍不多,欢迎查看之前的文章。另外,还有一种特殊的迭代器也值得单独成文,那就是生成器了,敬请期待后续推文哦~
The above is the detailed content of What is range()? Why not produce iterators?. For more information, please follow other related articles on the PHP Chinese website!