web-dev-qa-db-fra.com

Pourquoi la plage (début, fin) n'inclut-elle pas la fin?

>>> range(1,11)

vous donne

[1,2,3,4,5,6,7,8,9,10]

Pourquoi pas 1-11?

Ont-ils simplement décidé de le faire comme ça au hasard ou a-t-il une valeur que je ne vois pas?

271
MetaGuru

Parce qu'il est plus courant d'appeler range(0, 10) qui renvoie [0,1,2,3,4,5,6,7,8,9] qui contient 10 éléments, ce qui correspond à len(range(0, 10)). N'oubliez pas que les programmeurs préfèrent l'indexation basée sur 0.

En outre, considérez l'extrait de code commun suivant:

for i in range(len(li)):
    pass

Pouvez-vous voir que si range() montait exactement à len(li), cela serait problématique? Le programmeur devrait soustraire explicitement 1. Cela suit également la tendance commune des programmeurs qui préfèrent for(int i = 0; i < 10; i++) à for(int i = 0; i <= 9; i++).

Si vous appelez fréquemment avec une valeur de début 1, vous pouvez définir votre propre fonction:

>>> def range1(start, end):
...     return range(start, end+1)
...
>>> range1(1, 10)
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
219
marcog

Les gammes exclusives présentent certains avantages:

Tout d'abord, chaque élément de range(0,n) est un index valide pour les listes de longueur n.

De plus, range(0,n) a une longueur de n et non pas de n+1 comme le ferait une plage inclusive.

23
sepp2k

Bien qu'il y ait quelques explications algorithmiques utiles ici, je pense qu'il peut être utile d'ajouter un raisonnement simple de la vie réelle expliquant pourquoi cela fonctionne de la sorte, ce que j'ai trouvé utile pour présenter le sujet aux jeunes nouveaux arrivants:

Avec quelque chose comme "range (1,10)", il peut y avoir confusion en pensant que deux paramètres représentent le "début et la fin".

C'est en fait commencer et "arrêter".

Maintenant, si étaient la "fin", alors, oui, vous pourriez vous attendre à ce que ce nombre soit inclus dans la valeur finale. entrée dans la séquence. Mais ce n'est pas la "fin".

D'autres appellent à tort ce paramètre "count", car si vous n'utilisez que 'range (n)', alors il le fait bien sûr, il itère 'n' fois. Cette logique échoue lorsque vous ajoutez le paramètre de démarrage.

Le point clé est donc de rappeler son nom: "stop". Cela signifie que c’est le point auquel, une fois atteint, l’itération s’arrête immédiatement. Pas après ce point.

Ainsi, bien que "start" représente bien la première valeur à inclure, lorsque vous atteignez la valeur "stop", il "rompt" plutôt que de continuer à traiter "celui-ci aussi" avant de vous arrêter.

Une analogie que j’ai utilisée pour expliquer cela aux enfants est que, paradoxalement, il est plus sage que les enfants! Il ne s’arrête pas après il est censé le faire - il s’arrête immédiatement sans terminer ce qu’il faisait. (Ils l'obtiennent;))

Une autre analogie - lorsque vous conduisez une voiture, vous ne ne passez pas un panneau stop/renonciation/'céder le passage' avec elle assis quelque part à côté ou derrière votre voiture. Techniquement, vous ne l'avez pas encore atteinte lorsque vous vous arrêtez. Cela ne fait pas partie des "choses que vous avez passées pendant votre voyage".

J'espère que cela aidera à expliquer Pythonitos/Pythonitas!

19
dingles

Cela fonctionne bien en combinaison avec l'indexation de base zéro et len(). Par exemple, si vous avez 10 éléments dans une liste x, ils sont numérotés de 0 à 9. range(len(x)) vous donne 0-9.

Bien sûr, les gens vont vous dire que c'est plus Pythonic que de faire for item in x ou for index, item in enumerate(x) plutôt que for i in range(len(x)).

Le découpage en tranches fonctionne également de cette façon: foo[1:4] correspond aux éléments 1 à 3 de foo (en gardant à l'esprit que l'élément 1 est en réalité le deuxième élément en raison de l'indexation à base zéro). Par souci de cohérence, ils devraient tous deux fonctionner de la même manière.

J'y pense comme: "le premier numéro que vous voulez, suivi du premier que vous ne pas voulez". Si vous voulez 1-10, le premier numéro que vous ne voulez pas est 11, donc c'est range(1, 11).

Si cela devient lourd dans une application particulière, il est assez facile d'écrire une petite fonction d'assistance qui ajoute 1 à l'index final et appelle range().

18
kindall

C'est également utile pour diviser des plages; range(a,b) peut être divisé en range(a, x) et range(x, b), tandis qu'avec une plage inclusive, vous écrivez soit x-1 ou x+1. Bien que vous ayez rarement besoin de scinder des plages, vous avez tendance à scinder les listes assez souvent. C’est l’une des raisons pour lesquelles découper une liste l[a:b] inclut l’énième élément, mais pas le bième. Alors range avoir la même propriété le rend bien cohérent.

12
xuanji

La longueur de la plage correspond à la valeur supérieure moins la valeur inférieure.

C'est très similaire à quelque chose comme:

for (var i = 1; i < 11; i++) {
    //i goes from 1 to 10 in here
}

dans un langage de style C.

Aussi comme la gamme de Ruby:

1...11 #this is a range from 1 to 10

Cependant, Ruby reconnaît que vous voudrez souvent inclure la valeur du terminal et propose la syntaxe alternative:

1..10 #this is also a range from 1 to 10
11
Skilldrick

Fondamentalement dans python range(n) itère n fois, ce qui est de nature exclusive c'est pourquoi il ne donne pas la dernière valeur lors de son impression, nous pouvons créer une fonction qui donne valeur, cela signifie que la dernière valeur mentionnée dans la plage sera également imprimée.

def main():
    for i in inclusive_range(25):
        print(i, sep=" ")


def inclusive_range(*args):
    numargs = len(args)
    if numargs == 0:
        raise TypeError("you need to write at least a value")
    Elif numargs == 1:
        stop = args[0]
        start = 0
        step = 1
    Elif numargs == 2:
        (start, stop) = args
        step = 1
    Elif numargs == 3:
        (start, stop, step) = args
    else:
        raise TypeError("Inclusive range was expected at most 3 arguments,got {}".format(numargs))
    i = start
    while i <= stop:
        yield i
        i += step


if __== "__main__":
    main()
4
Ashish Dixit

Considérons le code

for i in range(10):
    print "You'll see this 10 times", i

L'idée est que vous obteniez une liste de longueur y-x, sur laquelle vous pouvez (comme vous le voyez ci-dessus) parcourir.

Lisez sur le python docs pour la plage - ils considèrent l'itération pour boucle comme la cas d'utilisation principale.

4
Robert

Il est juste plus commode de raisonner dans de nombreux cas.

Fondamentalement, nous pourrions considérer une plage comme un intervalle entre start et end. Si start <= end, l'intervalle entre eux est de end - start. Si len était en réalité défini comme la longueur, vous auriez:

len(range(start, end)) == start - end

Cependant, nous comptons les entiers inclus dans la plage au lieu de mesurer la longueur de l'intervalle. Pour que la propriété ci-dessus reste vraie, nous devons inclure l'un des points d'extrémité et exclure l'autre.

Ajouter le paramètre step revient à introduire une unité de longueur. Dans ce cas, vous vous attendez à

len(range(start, end, step)) == (start - end) / step

pour la longueur. Pour obtenir le nombre, vous utilisez simplement la division entière.

1
Arseny