Почему диапазон (начало, конец) не включает конец? - PullRequest
262 голосов
/ 22 декабря 2010
>>> range(1,11)

дает вам

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

Почему не 1-11?

Они просто решили сделать это случайным образом или это имеет какое-то значение, которого я не вижу?

Ответы [ 9 ]

215 голосов
/ 22 декабря 2010

Потому что чаще вызывать range(0, 10), который возвращает [0,1,2,3,4,5,6,7,8,9], который содержит 10 элементов, что равно len(range(0, 10)). Помните, что программисты предпочитают индексирование на основе 0.

Также рассмотрим следующий фрагмент кода:

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

Можете ли вы увидеть, что, если range() достигнет точно len(li), это будет проблематично? Программист должен был бы явно вычесть 1. Это также следует общей тенденции программистов, предпочитающих for(int i = 0; i < 10; i++) над for(int i = 0; i <= 9; i++).

Если вы часто вызываете диапазон, начинающийся с 1, вы можете определить свою собственную функцию:

>>> def range1(start, end):
...     return range(start, end+1)
...
>>> range1(1, 10)
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
23 голосов
/ 22 декабря 2010

Эксклюзивные диапазоны имеют некоторые преимущества:

С одной стороны, каждый элемент в range(0,n) является допустимым индексом для списков длины n.

Кроме того, range(0,n) имеет длину n, а не n+1, как было бы в инклюзивном диапазоне.

19 голосов
/ 27 января 2016

Хотя здесь есть несколько полезных алгоритмических объяснений, я думаю, что это может помочь добавить несколько простых «реальных» рассуждений о том, почему это работает таким образом, что я нашел полезным при ознакомлении с предметом для молодых новичков:

С чем-то вроде 'range (1,10)' может возникнуть путаница из-за того, что пара параметров представляет "начало и конец".

Это на самом деле начало и "остановка".

Теперь, если бы это было значением "конца", тогда да, вы могли бы ожидать, что число будет включено в качестве последней записи в последовательности.Но это не «конец».

Другие ошибочно называют этот параметр «счетчиком», потому что если вы когда-либо используете только «range (n)», тогда, конечно, он повторяет «n» раз.Эта логика ломается, когда вы добавляете параметр запуска.

Поэтому ключевым моментом является запоминание его имени: " stop ".Это означает, что это точка, в которой при достижении итерация будет немедленно остановлена.Не после этой точки.

Таким образом, хотя «start» действительно представляет первое включаемое значение, при достижении значения «stop» оно «ломается»вместо того, чтобы продолжать обрабатывать «это тоже» перед тем, как остановиться.

Одна аналогия, которую я использовал, объясняя это детям, состоит в том, что, по иронии судьбы, он ведет себя лучше, чем дети!Он не останавливается после , он должен был - он останавливается немедленно, не завершая то, что делал.(Они получают это;))

Еще одна аналогия - когда вы ведете машину, вы не проходите знак остановки / выхода / уступить дорогу и конецсидя рядом с вашей машиной.Технически вы все еще не достигли этого, когда остановитесь.Он не включен в «вещи, которые вы передали в своем путешествии».

Я надеюсь, что кое-что из этого поможет в объяснении Pythonitos / Pythonitas!

18 голосов
/ 22 декабря 2010

Хорошо работает в сочетании с индексацией на основе нуля и len().Например, если у вас есть 10 элементов в списке x, они нумеруются от 0 до 9.range(len(x)) дает вам 0-9.

Конечно, люди скажут вам, что больше Pythonic должен делать for item in x или for index, item in enumerate(x), чем for i in range(len(x)).

Нарезка работает таким образомтакже: foo[1:4] - это пункты 1-3 из foo (учитывая, что элемент 1 фактически является вторым элементом из-за индексации на основе нуля).Для согласованности они оба должны работать одинаково.

Я думаю о нем как: «первое число, которое вы хотите, после которого следует первое число, которое вы не хотите».Если вы хотите 1-10, первое число, которое вам не нужно, это 11, так что это range(1, 11).

Если оно становится громоздким в конкретном приложении, достаточно легко написать небольшую вспомогательную функцию, которая добавляет1 до конечного индекса и вызывает range().

12 голосов
/ 17 февраля 2013

Это также полезно для разделения диапазонов;range(a,b) можно разделить на range(a, x) и range(x, b), тогда как с инклюзивным диапазоном вы бы написали либо x-1, либо x+1.Хотя вам редко требуется разделять диапазоны, вы, как правило, довольно часто разделяете списки, что является одной из причин, по которой вырезание списка l[a:b] включает в себя элемент a, а не элемент bТогда range с тем же свойством делает его хорошо согласованным.

11 голосов
/ 22 декабря 2010

Длина диапазона - это верхнее значение минус нижнее значение.

Это очень похоже на что-то вроде:

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

на языке стиля C.

Также как и диапазон Ruby:

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

Тем не менее, Ruby признает, что вы часто хотите включить значение терминала, и предлагает альтернативный синтаксис:

1..10 #this is also a range from 1 to 10
4 голосов
/ 03 августа 2018

В основном на python range(n) повторяется n раз, что имеет исключительный характер, поэтому оно не дает последнего значения при печати, мы можем создать функцию, которая дает включающее значение означает, что оно также напечатает последнее значение, указанное в диапазоне.

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 __name__ == "__main__":
    main()
4 голосов
/ 22 декабря 2010

Рассмотрим код

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

Идея состоит в том, что вы получите список длиной y-x, который вы можете (как вы видели выше) повторить.

Читайте документы по питону для диапазона - они рассматривают итерацию цикла для основного варианта использования.

1 голос
/ 29 мая 2019

Во многих случаях просто удобнее рассуждать.

По сути, мы можем рассматривать диапазон как интервал между start и end.Если start <= end, длина интервала между ними составляет end - start.Если бы len было фактически определено как длина, вы бы получили:

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

Однако мы подсчитываем целые числа, включенные в диапазон, вместо измерения длины интервала.Чтобы сохранить указанное выше свойство true, мы должны включить одну из конечных точек и исключить другую.

Добавление параметра step похоже на введение единицы длины.В этом случае вы ожидаете

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

для длины.Чтобы получить количество, вы просто используете целочисленное деление.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...