Защо диапазонът (начало, край) не включва край?

>>> range(1,11)

дава ти

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

Защо не 1-11?

Дали просто са решили да го направят така на случаен принцип или има някаква стойност, която не виждам?


person Ryan    schedule 21.12.2010    source източник
comment
прочетете Dijkstra, ewd831   -  person SilentGhost    schedule 22.12.2010
comment
По принцип вие избирате един набор от различни бъгове за друг. Едната група е по-вероятно да доведе до преждевременно прекъсване на вашите цикли, другата е вероятно да причини изключение (или препълване на буфера на други езици). След като сте написали куп код, ще видите, че изборът на поведение range() има смисъл много по-често   -  person John La Rooy    schedule 22.12.2010
comment
Връзка към Dijkstra, ewd831: cs.utexas.edu/users/EWD/ ewd08xx/EWD831.PDF   -  person unutbu    schedule 22.12.2010
comment
@unutbu Тази статия на Djikstra е често цитирана в тази тема, но не предоставя нищо ценно тук, хората я използват просто като апел към авторитета. Единствената уместна псевдо-причина, която той дава за въпроса на OP е, че случайно смята, че включването на горната граница става неестествено и грозно в конкретен случай, когато последователността е празна - това е напълно субективна позиция и лесно аргументирано против, така че не носи много на масата. Експериментът с Mesa също няма голяма стойност, без да се познават техните конкретни ограничения или методи за оценка.   -  person sundar - Remember Monica    schedule 27.10.2013
comment
Аргументът на @sundar Dijkstras е, че конвенцията a < i <= b е единствената, за която a и b никога не трябва да напускат набора от естествени числа, стига диапазонът от i да е или празен, или в рамките на набора от естествени числа. Макар и предимно козметичен аргумент, мисля, че е напълно основателен.   -  person andreasdr    schedule 21.04.2015
comment
@andreasdr Но дори ако козметичният аргумент е валиден, подходът на Python не въвежда ли нов проблем с четливостта? В общоприетия английски терминът диапазон предполага, че нещо варира от нещо до нещо -- като интервал. Че len(list(range(1,2))) връща 1 и len(list(range(2))) връща 2 е нещо, което наистина трябва да се научите да усвоявате.   -  person armin    schedule 24.07.2016
comment
Друга странна конвенция на Python е заимствана. :P Предпочитам да симулирам естествена интуиция, а не да правя нещо като януари[0], февруари[1] и т.н.   -  person Peter.k    schedule 21.11.2017
comment
Би било хубаво, ако range() предложи опция за включваща горна граница. Добре съм с това, че range() е изключителен по подразбиране, но има ситуации, в които включването прави синтаксиса по-четлив.   -  person Shuklaswag    schedule 02.09.2018
comment
@Shuklaswag Мисля, че диапазон (начало, край+1) би бил много по-четлив от диапазон (начало, край, край_изключително=False) или всяка негова вариация.   -  person kkawabat    schedule 11.11.2019
comment
@sundar-ReinstateMonica Лесно се оспорва. как?   -  person user76284    schedule 01.05.2020
comment
Ако човек каже, че иска гама от цветове от зелено до червено, тогава много малко хора биха казали, че не искат червено. Така че диапазонът от английски думи не е подходяща дума. Това няма да се промени, но мисля, че това е пролука в бронята, че Python е разумен език.   -  person Peter Moore    schedule 06.08.2020
comment
Функцията Range в R връща минималната и максималната стойност от входния вектор   -  person SL5net    schedule 14.01.2021
comment
Реализация на Ruby за диапазон: (1..5) =› 1, 2, 3, 4, 5 и (1...5) =› 1, 2, 3, 4   -  person toquart    schedule 22.01.2021
comment
Написах куп код и този избор на поведение на range няма никакъв смисъл.   -  person EugZol    schedule 02.02.2021


Отговори (10)


Тъй като е по-често да се извиква 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]
person moinudin    schedule 21.12.2010
comment
Ако това беше разсъждението, нямаше ли параметрите да бъдат range(start, count)? - person Mark Ransom; 22.12.2010
comment
ах, наистина е така, защото х може да бъде 0 - person Ryan; 22.12.2010
comment

Използвам обект Employee и вътре в него е дефиниран списък с UserMaster

public class EmployeeMaster{
    private String employee_id;
    private String first_name;
    private String last_name;
    private List<UserMaster> userMaster = new ArrayList<UserMaster>();

    @OneToMany(fetch = FetchType.LAZY, mappedBy = "employeeMaster")
    @Cascade(value = {CascadeType.SAVE_UPDATE,CascadeType.DELETE_ORPHAN})   
    public  List<UserMaster> getUserMaster() {
        return userMaster;
    }
    public void setUserMaster(List<UserMaster> userMaster) {
        this.userMaster = userMaster;
    }
}

в моя метод се обаждам

xsession.saveOrUpdate(employeeMaster);

Тук изчиствам изрично предишната дъщерна колекция и добавям нови обекти Child

Но изтриването на сирак не работи тук .. Само заявката за вмъкване се изпълнява Pls Help

Колекция с cascade="all-delete-orphan" вече не се споменава от обекта собственик

съобщение се показва в конзолата

- person moinudin; 22.12.2010
comment
Вашият range1 няма да работи с диапазони, които имат различен размер на стъпката от 1. - person dimo414; 21.05.2015
comment
Вие обяснявате, че range(x) трябва да започва с 0 и x ще бъде дължината на диапазона. ДОБРЕ. Но не обяснихте защо диапазон(x,y) трябва да започва с x и да завършва с y-1. Ако програмистът иска for-цикъл с i вариращо от 1 до 3, той трябва изрично да добави 1. Това наистина ли е за удобство? - person armin; 24.07.2016
comment
Дължината на аргумента за диапазона не се прилага, когато стъпката не е равна на 1, т.е. range(0, 10, 2) е [0, 2, 4, 6, 8], което очевидно няма 10 елемента. Предполагам, че ще има end / step елемента, закръглявайки надолу, разбира се. - person dub stylee; 26.10.2016
comment
Въпреки че това е старо, нещо толкова просто като добавяне на +1 в обхват, реши проблема ми. - person andyADD; 29.01.2017
comment
@dimo414 Доста лесно е да имате допълнителен параметър като стъпка, за да работи функцията за диапазон. range1 = lambda start,end,step: range(start,end+1,step) for i in range(1,26): print(i) for i in range1(1,25,3): print(i) - person Doogle; 12.03.2017
comment
for i in range(len(li)): е по-скоро антимодел. Трябва да се използва enumerate. - person hans; 06.03.2018
comment
предпочитам е силна дума - person Chris Calo; 24.12.2018
comment
Програмистите наистина предпочитат базирано на 0 индексиране, поради което е странно, че Python използва индексиране на база 1 за стоп позиции. Не знам много компютърни езици, но всички те са имали само една конвенция. Python има две. Примерите за цикъл тук са измамни. Когато пиша 5:12 или диапазон (5, 12, 3), имам предвид преминаване от 5 към 12. Ако исках 11, това щях да напиша. В Python използвате число, за да представите числото преди него... но само понякога. - person bzip2; 18.01.2020
comment
това е мястото, където Ruby блести: (1..10) срещу (1...10) - person toquart; 10.04.2020
comment
@bzip2 Програмистите не предпочитат, те са учени, че в компютърните науки, поради някои конвенции, повечето неща се индексират с 0. Човек може също да създаде еквивалентен набор на еднакво валидни конвенции, където обектите се индексират, започвайки от 3512 (или всяко друго число), както и има еднакво валидна аритметика и аксиоматична математика. - person gented; 26.01.2021

Въпреки че тук има някои полезни алгоритмични обяснения, мисля, че може да помогне да добавите някои прости разсъждения от „реалния живот“ защо работи по този начин, което намерих за полезно, когато представям темата на млади новодошли:

С нещо като "диапазон (1,10)" може да възникне объркване от мисленето, че двойката параметри представлява "начало и край".

Всъщност е старт и "стоп".

Сега, ако беше "крайната" стойност, тогава, да, може да очаквате, че това число ще бъде включено като окончателен запис в поредицата. Но това не е "краят".

Други погрешно наричат ​​този параметър "брой", защото ако някога използвате само "диапазон (n)", тогава той, разбира се, повтаря "n" пъти. Тази логика се разпада, когато добавите началния параметър.

Така че ключовият момент е да запомните името му: „стоп“. Това означава, че това е точката, в която, когато се достигне, итерацията ще спре незабавно. Неследтози момент.

Така че, докато "старт" наистина представлява първата стойност, която трябва да бъде включена, при достигане на стойността "стоп" тя "се прекъсва", вместо да продължи да обработва "и тази" преди да спре.

Една аналогия, която използвах, обяснявайки това на децата, е, че по ирония на съдбата то се държи по-добре от децата! Не спира след както е трябвало - спира незабавно, без да завърши това, което е правил. (Те разбират това ;) )

Друга аналогия - когато карате кола, не подминавате знак за спиране/пропускане/„даване на път“ и в крайна сметка той стои някъде до или зад, твоята кола. Технически все още не сте го достигнали, когато спрете. Не е включено в „нещата, които сте преминали по време на вашето пътуване“.

Надявам се част от това да помогне при обяснението на Pythonitos/Pythonitas!

person dingles    schedule 27.01.2016
comment
Това обяснение е по-интуитивно. Благодаря - person Fred; 10.10.2019
comment
Опитваш се да сложиш червило на прасе. Разликата между стоп и край е абсурдна. Ако премина от 1 до 7, не съм преминал 7. Просто е недостатък на Python да има различни конвенции за начални и стоп позиции. В други езици, включително човешки, от X към Y означава от X към Y. В Python X:Y означава X:Y-1. Ако имате среща от 9 до 11, казвате ли на хората, че е от 9 до 12 или от 8 до 11? - person bzip2; 18.01.2020
comment
@bzip2, Python не е прасе, а змия. Това, което наричате несъответствия и недостатъци, не са: те са дизайнерски избори, направени от разработчиците, пренасяни последователно в целия език и позволяващи на милиони програмисти да решават своите задачи. Ако не ви харесва, използвайте разширение, което предоставя включващи диапазони, или превключете на друг език. Вашите коментари не допринасят за разбирането на Python, вместо това обиждат общността. Те също така показват, че не разбирате естеството на ексклузивните интервали, защото ако имате среща от 9 до 11, в 11 ще бъдете свободни. - person Arthur Khazbs; 27.01.2021

Изключителните гами имат някои предимства:

От една страна всеки елемент в range(0,n) е валиден индекс за списъци с дължина n.

Също така range(0,n) има дължина n, а не n+1, което би включвал диапазон.

person sepp2k    schedule 21.12.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().

person kindall    schedule 21.12.2010
comment
Съгласете се с нарязването. w = 'abc'; w[:] == w[0:len(w)]; w[:-1] == w[0:len(w)-1]; - person kevpie; 22.12.2010

Също така е полезно за разделяне на диапазони; range(a,b) може да се раздели на range(a, x) и range(x, b), докато с включващ диапазон бихте написали x-1 или x+1. Въпреки че рядко се налага да разделяте диапазони, вие сте склонни да разделяте списъци доста често, което е една от причините нарязването на списък l[a:b] да включва a-тия елемент, но не и b-тия. Тогава range със същото свойство го прави добре последователен.

person xuanji    schedule 17.02.2013

Дължината на диапазона е горната стойност минус долната стойност.

Много е подобно на нещо като:

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
person Skilldrick    schedule 21.12.2010

Помислете за кода

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

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

Прочетете документите на python за обхват - те смятат итерацията за цикъл за основна употреба.

person Robert    schedule 21.12.2010

По принцип в 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()
person Ashish Dixit    schedule 03.08.2018

Просто е по-удобно да се разсъждава в много случаи.

По принцип можем да мислим за диапазон като интервал между start и end. Ако start <= end, дължината на интервала между тях е end - start. Ако len действително беше дефинирано като дължина, ще имате:

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

Ние обаче броим целите числа, включени в диапазона, вместо да измерваме дължината на интервала. За да запазим горното свойство вярно, трябва да включим една от крайните точки и да изключим другата.

Добавянето на параметъра step е като въвеждане на единица за дължина. В такъв случай бихте очаквали

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

за дължина. За да получите броя, просто използвате целочислено деление.

person Arseny    schedule 29.05.2019
comment
Тези защити на непоследователността на Python са забавни. Ако исках интервала между две числа, защо бих използвал изваждане, за да получа разликата вместо интервала? Непоследователно е да се използват различни конвенции за индексиране за начална и крайна позиция. Защо трябва да напишете 5:22, за да получите позиции от 5 до 21? - person bzip2; 18.01.2020
comment
Не е на Python, доста често се среща навсякъде. В C, Java, Ruby, каквото и да е - person Arseny; 18.01.2020
comment
Исках да кажа, че е обичайно за индексиране, а не че другите езици непременно имат същия точен вид обект - person Arseny; 09.04.2020

range(n) в python се връща от 0 до n-1. Съответно range(1,n) от 1 до n-1. Така че, ако искате да пропуснете първата стойност и да получите и последната стойност (n), можете да го направите много просто, като използвате следния код.

for i in range(1, n + 1):
        print(i) #prints from 1 to n
person Nick Pantelidis    schedule 03.02.2021