В основном вы выбираете один набор отдельных ошибок для другого. Один набор с большей вероятностью приведет к преждевременному завершению ваших циклов, а другой может вызвать исключение (или переполнение буфера в других языках). После того, как вы написали кучу кода, вы увидите, что выбор поведения range()имеет смысл гораздо чаще
@unutbu Эта статья Djikstra часто цитируется в этой теме, но здесь нет ничего ценного, люди используют ее просто как призыв к авторитету. Единственная уместная псевдо-причина, которую он приводит для вопроса ОП, заключается в том, что он чувствует, что включение верхней границы становится «неестественным» и «уродливым» в конкретном случае, когда последовательность в пустом - это совершенно субъективная позиция, и ее легко оспаривать, так что это не приносит много на стол. «Эксперимент» с Mesa не имеет особой ценности либо без знания их конкретных ограничений или методов оценки.
sundar - Восстановить Монику
6
@andreasdr Но даже если косметический аргумент верен, разве подход Python не создает новую проблему читабельности? В английском языке общего пользования термин «диапазон» подразумевает, что что-то колеблется от чего-то до чего-то - например, интервал. Этот len (list (range (1,2))) возвращает 1, а len (list (range (2))) возвращает 2 - это то, что вы действительно должны научиться переваривать.
Армин
Ответы:
246
Потому что более распространенным является вызов с 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, вы можете определить свою собственную функцию:
Если бы это было обоснование, параметры не были бы range(start, count)?
Марк Рэнсом
3
@shogun Начальное значение по умолчанию равно 0, то range(10)есть эквивалентно range(0, 10).
Мойнудин
4
Вы range1не будете работать с диапазонами, размер шага которых отличается от размера 1.
dimo414
6
Вы объясняете, что диапазон (x) должен начинаться с 0, а x будет «длиной диапазона». ХОРОШО. Но вы не объяснили, почему диапазон (x, y) должен начинаться с x и заканчиваться y-1. Если программист хочет цикл for с i в диапазоне от 1 до 3, он должен явно добавить 1. Это действительно удобство?
Армин
7
for i in range(len(li)):скорее антипаттерн. Надо использовать enumerate.
Ганс
27
Хотя здесь есть несколько полезных алгоритмических объяснений, я думаю, что это может помочь добавить несколько простых «реальных» рассуждений о том, почему это работает таким образом, что я нашел полезным при представлении предмета молодым новичкам:
С чем-то вроде «range (1,10)» может возникнуть путаница, если подумать, что пара параметров представляет «начало и конец».
Это фактически начало и «остановка».
Теперь, если бы это было «конечное» значение, то да, вы могли бы ожидать, что это число будет включено в качестве последней записи в последовательности. Но это не «конец».
Другие ошибочно называют этот параметр «count», потому что если вы когда-либо используете «range (n)», то он, конечно, повторяет «n» раз. Эта логика ломается, когда вы добавляете параметр запуска.
Таким образом, ключевой момент заключается в том, чтобы запомнить его название: « стоп ». Это означает, что это точка, в которой при достижении итерация будет немедленно остановлена. Не после этого момента.
Таким образом, хотя «start» действительно представляет первое значение, которое должно быть включено, при достижении значения «stop» оно «ломается», а не продолжает обрабатывать «то же самое» перед остановкой.
Одна аналогия, которую я использовал, объясняя это детям, заключается в том, что, по иронии судьбы, он ведет себя лучше, чем дети! Он не останавливается после того, как должен был - он останавливается немедленно, не заканчивая то, что делал. (Они получают это;))
Другая аналогия - когда вы ведете машину, вы не проходите мимо знака «стоп / выход / уступить дорогу» и в конечном итоге сидите рядом с вашей машиной или позади нее. Технически вы все еще не достигли этого, когда остановитесь. Это не входит в «вещи, которые вы прошли в своем путешествии».
Я надеюсь, что это поможет объяснить Pythonitos / Pythonitas!
Вы пытаетесь нанести помаду на свинью. Различие между «стопом» и «концом» абсурдно. Если я перейду с 1 на 7, я не сдал 7. Это просто недостаток Python - иметь разные соглашения для стартовой и конечной позиций. На других языках, включая человеческие, «от Х до Y» означает «от Х до Y». В Python «X: Y» означает «X: Y-1». Если у вас встреча с 9 до 11, вы говорите людям, что это с 9 до 12 или с 8 до 11?
bzip2
24
Эксклюзивные диапазоны имеют ряд преимуществ:
С одной стороны, каждый элемент range(0,n)является допустимым индексом для списков длины n.
Также range(0,n)имеет длину n, не n+1включающую диапазон.
Хорошо работает в сочетании с индексацией на основе нуля и 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().
Согласитесь на нарезку. w = 'abc'; w[:] == w[0:len(w)]; w[:-1] == w[0:len(w)-1];
kevpie
def full_range(start,stop): return range(start,stop+1) ## helper function
Нобар
может быть, пример перечисления следует прочитать, for index, item in enumerate(x)чтобы избежать путаницы
Seans
@seans Спасибо, исправлено.
любезно
12
Это также полезно для разделения диапазонов; range(a,b)можно разделить на range(a, x)и range(x, b), тогда как с включенным диапазоном вы бы написали либо x-1или x+1. Хотя вам редко требуется разделять диапазоны, вы, как правило, довольно часто разделяете списки, что является одной из причин, по которой разделение списка l[a:b]включает в себя a-й элемент, но не b-й. Тогда rangeналичие того же свойства делает его хорошо согласованным.
Г! Я не использую Ruby, но я могу себе представить , что 1..10против 1...10того трудно различить при чтении коды!
Мойнудин
6
@marcog - когда вы знаете, что две формы существуют, ваши глаза настраиваются на разницу :)
Skilldrick
11
Оператор диапазона Руби совершенно интуитивно понятен. Чем длиннее форма, тем короче последовательность. кашель
Рассел Борогове
4
@Russell, возможно 1 ............ 20 должен давать тот же диапазон, что и 1..10. Теперь это будет какой-то синтаксический сахар, на который стоит перейти. ;)
kevpie
4
@Russell Дополнительная точка сжимает последний элемент из диапазона :)
Skilldrick
5
В основном в 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:raiseTypeError("you need to write at least a value")elif numargs ==1:
stop = args[0]
start =0
step =1elif numargs ==2:(start, stop)= args
step =1elif numargs ==3:(start, stop, step)= args
else:raiseTypeError("Inclusive range was expected at most 3 arguments,got {}".format(numargs))
i = start
while i <= stop:yield i
i += step
if __name__ =="__main__":
main()
По сути, мы можем думать о диапазоне как об интервале между startи end. Если start <= endдлина интервала между ними равна end - start. Если бы на lenсамом деле была определена длина, вы бы имели:
len(range(start, end))== start - end
Однако мы подсчитываем целые числа, включенные в диапазон, а не измеряем длину интервала. Чтобы оставить указанное выше свойство истинным, мы должны включить одну из конечных точек и исключить другую.
Добавление stepпараметра похоже на введение единицы длины. В этом случае вы ожидаете
len(range(start, end, step))==(start - end)/ step
для длины. Чтобы получить количество, вы просто используете целочисленное деление.
Эти защиты непоследовательности Python веселые. Если мне нужен интервал между двумя числами, зачем мне использовать вычитание, чтобы получить разницу вместо интервала? Неправильно использовать разные соглашения об индексации для начальной и конечной позиций. Зачем вам нужно писать «5:22», чтобы получить позиции с 5 по 21?
bzip2
Это не Python, это довольно распространено по всем направлениям. В C, Java, Ruby вы называете это
Арсений
Я хотел сказать, что это обычное дело для индексации, а не то, что другие языки обязательно имеют точно такой же тип объекта
range()
имеет смысл гораздо чащеОтветы:
Потому что более распространенным является вызов с
range(0, 10)
возвратом,[0,1,2,3,4,5,6,7,8,9]
который содержит 10 равных элементовlen(range(0, 10))
. Помните, что программисты предпочитают индексирование на основе 0.Также рассмотрим следующий общий фрагмент кода:
Можете ли вы увидеть, что если бы
range()
дошло до того,len(li)
что это было бы проблематично? Программист должен был бы явно вычесть 1. Это также следует общей тенденции программистов, предпочитающихfor(int i = 0; i < 10; i++)
болееfor(int i = 0; i <= 9; i++)
.Если вы часто вызываете диапазон с начала 1, вы можете определить свою собственную функцию:
источник
range(start, count)
?range(10)
есть эквивалентноrange(0, 10)
.range1
не будете работать с диапазонами, размер шага которых отличается от размера1
.for i in range(len(li)):
скорее антипаттерн. Надо использоватьenumerate
.Хотя здесь есть несколько полезных алгоритмических объяснений, я думаю, что это может помочь добавить несколько простых «реальных» рассуждений о том, почему это работает таким образом, что я нашел полезным при представлении предмета молодым новичкам:
С чем-то вроде «range (1,10)» может возникнуть путаница, если подумать, что пара параметров представляет «начало и конец».
Это фактически начало и «остановка».
Теперь, если бы это было «конечное» значение, то да, вы могли бы ожидать, что это число будет включено в качестве последней записи в последовательности. Но это не «конец».
Другие ошибочно называют этот параметр «count», потому что если вы когда-либо используете «range (n)», то он, конечно, повторяет «n» раз. Эта логика ломается, когда вы добавляете параметр запуска.
Таким образом, ключевой момент заключается в том, чтобы запомнить его название: « стоп ». Это означает, что это точка, в которой при достижении итерация будет немедленно остановлена. Не после этого момента.
Таким образом, хотя «start» действительно представляет первое значение, которое должно быть включено, при достижении значения «stop» оно «ломается», а не продолжает обрабатывать «то же самое» перед остановкой.
Одна аналогия, которую я использовал, объясняя это детям, заключается в том, что, по иронии судьбы, он ведет себя лучше, чем дети! Он не останавливается после того, как должен был - он останавливается немедленно, не заканчивая то, что делал. (Они получают это;))
Другая аналогия - когда вы ведете машину, вы не проходите мимо знака «стоп / выход / уступить дорогу» и в конечном итоге сидите рядом с вашей машиной или позади нее. Технически вы все еще не достигли этого, когда остановитесь. Это не входит в «вещи, которые вы прошли в своем путешествии».
Я надеюсь, что это поможет объяснить Pythonitos / Pythonitas!
источник
Эксклюзивные диапазоны имеют ряд преимуществ:
С одной стороны, каждый элемент
range(0,n)
является допустимым индексом для списков длиныn
.Также
range(0,n)
имеет длинуn
, неn+1
включающую диапазон.источник
Хорошо работает в сочетании с индексацией на основе нуля и
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()
.источник
w = 'abc'; w[:] == w[0:len(w)]; w[:-1] == w[0:len(w)-1];
def full_range(start,stop): return range(start,stop+1) ## helper function
for index, item in enumerate(x)
чтобы избежать путаницыЭто также полезно для разделения диапазонов;
range(a,b)
можно разделить наrange(a, x)
иrange(x, b)
, тогда как с включенным диапазоном вы бы написали либоx-1
илиx+1
. Хотя вам редко требуется разделять диапазоны, вы, как правило, довольно часто разделяете списки, что является одной из причин, по которой разделение спискаl[a:b]
включает в себя a-й элемент, но не b-й. Тогдаrange
наличие того же свойства делает его хорошо согласованным.источник
Длина диапазона - это верхнее значение минус нижнее значение.
Это очень похоже на что-то вроде:
на языке C-стиля.
Также как и ассортимент Руби:
Тем не менее, Ruby признает, что много раз вы захотите включить значение терминала, и предлагает альтернативный синтаксис:
источник
1..10
против1...10
того трудно различить при чтении коды!В основном в python
range(n)
итерируетn
время, которое имеет исключительную природу, поэтому оно не дает последнего значения при печати, мы можем создать функцию, которая дает инклюзивное значение, это означает, что она также будет печатать последнее значение, упомянутое в диапазоне.источник
Рассмотрим код
Идея в том, что вы получите список длины
y-x
, который вы можете (как вы видели выше) повторить.Читайте в документации по Python для диапазона - они рассматривают цикл итерации в качестве основного варианта использования.
источник
Это просто удобнее рассуждать во многих случаях.
По сути, мы можем думать о диапазоне как об интервале между
start
иend
. Еслиstart <= end
длина интервала между ними равнаend - start
. Если бы наlen
самом деле была определена длина, вы бы имели:Однако мы подсчитываем целые числа, включенные в диапазон, а не измеряем длину интервала. Чтобы оставить указанное выше свойство истинным, мы должны включить одну из конечных точек и исключить другую.
Добавление
step
параметра похоже на введение единицы длины. В этом случае вы ожидаетедля длины. Чтобы получить количество, вы просто используете целочисленное деление.
источник