Maison > Article > base de données > Qu'est-ce que range() ? Pourquoi ne pas produire des itérateurs ?
Le contenu de cet article concerne ce qu'est range() ? Pourquoi ne pas produire des itérateurs ? Il a une certaine valeur de référence. Les amis dans le besoin peuvent s'y référer. J'espère qu'il vous sera utile.
Iterator est l'un des (un des) 23 modèles de conception les plus couramment utilisés. On le voit partout en Python. Nous l'utilisons souvent, mais nous n'avons pas forcément connaissance de son existence. Dans ma série sur les itérateurs (lien en fin d'article), j'ai mentionné au moins 23 façons de générer des itérateurs. Certaines méthodes sont spécifiquement utilisées pour générer des itérateurs, tandis que d'autres utilisent les itérateurs « secrètement » pour résoudre d'autres problèmes.
Avant que le système n'apprenne les itérateurs, j'ai toujours pensé que la méthode range() était également utilisée pour générer des itérateurs, mais maintenant j'ai soudainement découvert qu'elle ne génère que des objets itérables, pas des itérateurs ! (PS : range() en Python2 génère une liste. Cet article est basé sur Python3 et génère un objet itérable)
Donc, j'ai cette question : Pourquoi range() ne génère-t-il pas d'itérateur ? En cherchant des réponses, j'ai découvert que j'avais des malentendus sur le type de plage. Par conséquent, cet article vous donnera une compréhension globale de la portée, et j'ai hâte d'apprendre et de progresser avec vous.
1. Qu'est-ce que range() ?
Sa syntaxe : range(start, stop [,step]); stop ; step est la taille du pas, la valeur par défaut est 1 et ne peut pas être 0. La méthode range() génère une plage d’entiers fermée à gauche et ouverte à droite.
>>> 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
Il y a plusieurs points à noter concernant la fonction range() : (1) elle représente un intervalle fermé à gauche et ouvert à droite (2) le paramètre qu'elle reçoit doit être un entier et peut être un nombre négatif, mais il ne peut pas s'agir d'autres types tels que des nombres à virgule flottante ; (3) Il s'agit d'un type de séquence immuable, qui peut être utilisé pour juger des éléments, rechercher des éléments, effectuer des tranches et d'autres opérations, mais ne peut pas modifier des éléments (4 ; ) C'est un objet itérable, mais pas un itérateur.
# (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. Pourquoi range() ne produit-il pas d'itérateur ?
Il existe de nombreuses méthodes intégrées pour obtenir des itérateurs, telles que zip(), enumerate(), map(), filter() et reverse(), etc., mais comme range() uniquement La méthode d'un objet itérable est unique (s'il existe des contre-exemples, veuillez nous le faire savoir). C'est là que j'ai une mauvaise compréhension de mes connaissances.
Pendant le parcours de la boucle for, les performances des objets itérables et des itérateurs sont les mêmes, c'est-à-dire qu'ils sont tous deux évalués paresseusement, et il n'y a aucune différence de complexité spatiale et de complexité temporelle. J'ai un jour résumé la différence entre les deux comme "la même chose mais deux choses différentes": la même chose est que les deux peuvent être itérés paresseusement, mais la différence est que l'objet itérable ne prend pas en charge l'auto-traverse (c'est-à-dire la méthode next() ), et l'itérateur lui-même ne prend pas en charge le découpage (c'est-à-dire la méthode __getitem__()
).
Malgré ces différences, il est difficile de conclure lequel est le meilleur. Maintenant, la subtilité est la suivante : pourquoi les itérateurs sont-ils conçus pour les cinq méthodes intégrées, alors que la méthode range() est conçue pour être un objet itérable ? Ne vaudrait-il pas mieux les unifier tous ?
En fait, Python a fait beaucoup de ce genre de choses dans un souci de standardisation. Par exemple, il existe deux méthodes range() et xrange() dans Python2, mais Python3 en a éliminé une et. a également utilisé la méthode "Li Dai Tao Jian". Pourquoi ne pas être plus formel et faire en sorte que range() génère un itérateur ?
Je n'ai pas trouvé d'explication officielle sur cette question. Ce qui suit est purement opinion personnelle.
zip() et d'autres méthodes doivent recevoir les paramètres de certains objets itérables, ce qui est un processus de retraitement. Par conséquent, ils espèrent également produire certains résultats immédiatement, donc les développeurs Python conçoivent le résultat est un itérateur. . Cela présente également l'avantage que lorsque l'objet itérable utilisé comme paramètre change, l'itérateur utilisé comme résultat ne sera pas utilisé de manière incorrecte car il est consommable.
La méthode range() est différente. Le paramètre qu'elle reçoit n'est pas un objet itérable. Il s'agit d'un processus de traitement initial, il est donc conçu comme un objet itérable, qui peut être utilisé directement ou pour d'autres utilisations de retraitement. . Par exemple, des méthodes telles que zip() peuvent accepter des paramètres de type plage.
>>> for i in zip(range(1,6,2), range(2,7,2)): >>> print(i, end="") (1, 2)(3, 4)(5, 6)
En d'autres termes, la méthode range() est un producteur primaire, et les matières premières qu'elle produit ont de grandes utilisations. Si vous la transformez tôt en itérateur, ce sera sans aucun doute superflu.
Pensez-vous que cette interprétation ait du sens ? N'hésitez pas à discuter de ce sujet avec moi.
3. Quel est le type de plage ?
Ce qui précède est ma réponse à "Pourquoi range() ne génère pas d'itérateur". Suivant cette idée, j'ai étudié l'objet de portée qu'il générait, et après recherche, j'ai découvert que cet objet de portée n'était pas simple.
La première chose étrange, c'est que c'est une séquence immuable ! Je n'ai jamais remarqué cela. Même si je n'ai jamais pensé à modifier la valeur de range(), cette fonctionnalité non modifiable m'a quand même surpris.
En parcourant la documentation, la division officielle est la suivante - Il existe trois types de séquences de base : liste, tuple et objet plage. (Il existe trois types de séquences de base : les listes, les tuples et les objets de plage.)
这我倒一直没注意,原来 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 对象这细微而有意思的特性,我觉得这篇文章写得值了。本文是作为迭代器系列文章的一篇来写的,所以对于迭代器的基础知识介绍不多,欢迎查看之前的文章。另外,还有一种特殊的迭代器也值得单独成文,那就是生成器了,敬请期待后续推文哦~
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!