2013-03-14 5 views
1

Je suis familier avec la différence entre range() et xrange(). J'ai remarqué quelque chose de bizarre avec xrange():étrange comportement xrange() en Python 2

>>> xrange(1,10,2) 
xrange(1, 11, 2) 

>>> xrange(1,10,4) 
xrange(1, 13, 4) 

Fonctionnellement, il est correct:

>>> for item in xrange(1,10,4): 
...  print item 
... 
1 
5 
9 
>>> 

Cependant, comme vous pouvez le voir, la valeur d'arrêt dans l'objet xrange retourné est la valeur supérieure suivante après la dernière valeur légale. Une raison pour laquelle?

range() qui fournit désormais les mêmes fonctionnalités en Python 3 comme xrange en Python 2 se comporte comme prévu:

>>> range(1,10,4) 
range(1, 10, 4) 
>>> range(1,10,2) 
range(1, 10, 2) 
>>> 
+2

Non, 'xrange()' est pas la même que 'range()' en python 3. Ce dernier est une nouvelle type. La valeur de fin est * never * incluse dans 'range()' ou 'xrange()'. En raison de votre valeur 'step', ni' 11' ni '10' ne sont inclus dans la sortie de la plage. –

+0

@MartijnPieters Je suis d'accord avec votre première moitié du commentaire. Edited ma question. –

+0

@MartijnPieters Maintenant, en ce qui concerne la valeur d'arrêt jamais inclus, oui je le sais. Pourquoi l'objet xrange est-il renvoyé avec une valeur d'arrêt comme dernière valeur légale + étape? –

Répondre

2

xrange(1, 10, 4) est équivalent à xrange(1, 13, 4). Pour utiliser votre exemple:

>>> for item in xrange(1,13,4): 
...  print item 
... 
1 
5 
9 
>>> 

xrange en Python 2 canonise les start, stop, step arguments. En interne, l'implémentation xrange stocke le triple start, step et length (nombre d'éléments dans l'objet xrange) au lieu de start, step et stop. Voici comment xrange.__repr__() est mis en œuvre [1]:

rtn = PyString_FromFormat("xrange(%ld, %ld, %ld)", 
          r->start, 
          r->start + r->len * r->step, 
          r->step); 

[1] https://github.com/replit/empythoned/blob/master/cpython/Objects/rangeobject.c

+0

Super. Merci d'avoir partagé le lien vers la source de CPython. –

+0

J'accepte votre réponse, puisque c'est précisément ma question. –

+0

Voici le lien vers la source officielle de CPython 3.3: http://hg.python.org/cpython/file/e45db319e590/Objects/rangeobject.c#l808 –

3

La valeur d'arrêt d'un range ou xrange est toujours exclusive.

Citation du docs (Python 2):

Si step est positif, le dernier élément est le plus grand start + i * stepmoins destop; si step est négatif, le dernier élément est le plus petit start + i * stepsupérieur àstop.

Et pour Python 3:

Pour un step positif, le contenu d'une plage r sont déterminées par la formule r[i] = start + step*ii >= 0 et r[i] < stop.

Pour un step négatif, le contenu de la plage sont toujours déterminés par la formule r[i] = start + step*i, mais les contraintes sont i >= 0 et r[i] > stop.


A propos de la deuxième partie de votre question concernant la repr() du xrange:

xrange(1, 10, 4) et xrange(1, 13, 4) sont identiques et repr() pour les objets python natif retourne habituellement du code python valide pour recréer l'objet. Cela n'a pas besoin d'être exactement le même code python qui a initialement créé l'objet.

+1

Oui, j'accepterai votre réponse puisque la seconde moitié de la réponse est ce que je cherchais. Merci. –

+0

Désolé, j'ai accepté la réponse @ zodiac car il m'a même indiqué le code source qui me donne la réponse exacte que je cherchais. –

2

Est-ce vraiment important? L'effet est identique. Ni 10 ni 11 ne sont inclus dans la sortie de xrange(), et xrange(1, 11, 2) est équivalent à xrange(1, 10, 2).Le type de plage Python 2 (le résultat de xrange()) stocke la longueur de plage, pas la valeur de fin, de sorte que pour créer la sortie repr, il calcule cette valeur finale pour vous. Et parce que vous avez utilisé une valeur d'étape, le calcul montre le résultat de la formule start + length * step. Pour la mise en œuvre, la longueur est la valeur la plus importante, la valeur end peut être mise au rebut et recalculée en toute sécurité si nécessaire.

Ainsi, lorsque vous créez xrange(1, 10, 2), il calcule la longueur de la plage et les magasins qui au lieu de la valeur finale:

if (step > 0 && lo < hi) 
return 1UL + (hi - 1UL - lo)/step; 
else if (step < 0 && lo > hi) 
return 1UL + (lo - 1UL - hi)/(0UL - step); 
else 
return 0UL; 

Python 3 objets Range stocke la valeur finale en plus à la longueur , vous pouvez donc interroger l'objet et l'afficher dans la sortie repr.

+0

Je ne sais pas pourquoi vous demandez "Est-ce vraiment important?". Je sais que l'effet est le même. Ce n'est pas ce que j'ai demandé. En tous cas. –

Questions connexes