Python Forum
"Up to but not including" (My personal guide on slicing & indexing)
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
"Up to but not including" (My personal guide on slicing & indexing)
#1
I’m writing a mini guide for myself for future reference on indexing strings and lists and how they compare to parameters that are passed into randint() and range().

I need you people to do two things:
  1. Verify that my current notes are accurate (especially the summary at the bottom)
  2. Share your insight that I could add to my mini guide.
Here is my guide in a Jupyter Notebook hosted on my GitHub repo. It’s kind of long. Here is the important part that I need you call to evaluate and comment on:

What follows is a list comprehension operation which is a demonstration of both range() and randint() which take in identical numerical parameters but produces different behaviour. The randint() starts at '0' and goes up to and includes the final number - - so can generate from a spectrum of 6 potential random integers: 0,1,2,3,4,5. However range() produces a total of strictly 5 items, not 6. See here:

import random

new_list = [ random.randint(0,5) for i in range(0,5) ]
print(new_list)
Here is some output:

Quote:[1, 4, 0, 5, 3]

As shown above, passing in the same '0,5' (for randint() and range()) creates a list of a total of 5 items but 6 potential integers - - 0,1,2,3,4,5.

Now check out this demo of len() method on list/strings:

colours = ["indigo", "orange", "crimson", "emerald",]
print(f"The length of the colours string is {len(colours)}. \
 But the 4th item in the lists is pulled using [3]: {colours[3]} \n")
 
quick_string = "Bob"
print(f'Here is a new string I just created: "{quick_string}". \
 The length of this string is {len(quick_string)} \
  but to pull the 3rd character, you use [:2] which results in this: "{quick_string[:2]}"')

Here is the output:

Quote:The length of the colours string is 4.
But the 4th item in the lists is pulled using [3]: emerald
Here is a new string I just created: "Bob".
The length of this string is 3 but to pull the
3rd character, you use [:2] which results in this: "b"

Therefore, the len() method is inclusive. len() starts at the 0 index position but the counting starts at '1'. This is true for both strings and lists.

To summarize for my future reference, here is my "Up to but not including" legend:
  • For strings and lists, len() begins counting at the 0 index position but begins counting as 1 and is always inclusive.
  • For string and list slicing it begins at the index position specified (including 0) yet is always exclusive.
  • For randint() parameters, it is similar to len() with counting begining where specified, inclusive of 0 (if there is a 0).
  • For range() parameters it is like slicing.

Is this all accurate? What have I got wrong? What have I got right? Is there anything that you'd like to correct? How might you people clarify what I am trying to say here?

In terms of research so far, I’ve used more than one Udemy course on Python like Jose Portilla’s “Complete Python 3 Masterclass Journey” as well as Colt Steele’s "The Modern Python 3 Bootcamp". I've used a Pynative guide called “Python range() Function Explained with Examples” as well as a Stackoverflow question/answer thread titled “Difference between random randint vs randrange” helped really confuse me as well.
Reply
#2
(Nov-19-2019, 10:44 PM)Drone4four Wrote: 3rd character, you use [:2] which results in this: "b"

This is incorrect, I expect the colon is a typo. 'Bob'[:2] gives you up to index 2, or 'Bo'. 'Bob'[2] (without the colon) gets you 'b'.

I think some of your bullet points are more confusing than helpful. For example, len() gives you the number of items in a sequence. Trying to equate that to indexes just seems to confuse the issue. Likewise, randint gives a random number between the first and second parameters, inclusive of both. You don't need to equate it to indexes. Now range parameters are like slices, but note that the random module also has randrange, and it's parameters work like ranges and slices.

Now, I like to think of the indexes as being between the items, rather than on the items:

Output:
lunch = ['Spam', 'spam', 'spam', 'eggs', 'spam'] ^ ^ ^ ^ ^ ^ | | | | | | 0 1 2 3 4 5 -5 -4 -3 -2 -1
If you just give one index, you get the item after it. That's why -1 gets you the last item. But if you give two indexes, you get everything between them. That's why 1:-1 gets you everything but the first and last item.
Craig "Ichabod" O'Brien - xenomind.com
I wish you happiness.
Recommended Tutorials: BBCode, functions, classes, text adventures
Reply
#3
(Nov-19-2019, 10:44 PM)Drone4four Wrote: Therefore, the len() method is inclusive.
I wouldn't say it this way. What would it mean for len to be exclusive, rather than inclusive? That it would actually be the length - 1? Why would you ever have a function for getting the length of something that returns that number minus 1? Don't think of len as inclusive - it doesn't do what the range or randint do, and we shouldn't try to reuse terminology that doesn't fit.

(Nov-19-2019, 10:44 PM)Drone4four Wrote: For string and list slicing it begins at the index position specified (including 0) yet is always exclusive.
The way I tend to remember this is that the following function always returns true:
def f(n):
    return len(list(range(n))) == n
When Python was designed, this was intentional - range(n) generates n elements. I believe the rationale was that this would be the most common use case, and so should be the easiest to learn.

(Nov-19-2019, 10:44 PM)Drone4four Wrote: For randint() parameters, it is similar to len() with counting begining where specified, inclusive of 0 (if there is a 0).
Just to repeat: I wouldn't think of it as "similar to len" (as what they do is very different). While there's an argument that range() would be inclusive, I don't believe that a similar argument exists for randint. Why would you tell randint a number bigger than what you actually want, just for it to subtract from that? There's no reason for such a thing.

(Nov-19-2019, 10:44 PM)Drone4four Wrote: For range() parameters it is like slicing.
This is a fair comparison. I believe the rationale for slicing was similar, in that [icopde]slicable[:n][/icode] is going to be length n. Just as with range, I believe there's a reasonable argument why it could be inclusive, but it's probably good that the two are consistent with each other. (As a side note: try evaluating [icode]list(range(10))[:20][icode] if you're not sure of what the result will be; I don't have any additional commentary on this other than finding it interesting.)

I think I've communicated my intuitions here, which are based on ~14 years of Python. Hopefully they're of use, and if they're not, hopefully someone has a correction for me :)
Feel like you're not getting the answers you want? Checkout the help/rules for things like what to include/not include in a post, how to use code tags, how to ask smart questions, and more.

Pro-tip - there's an inverse correlation between the number of lines of code posted and my enthusiasm for helping with a question :)
Reply
#4
I think that Dijkstra made good observations in his Why numbering should start at zero.
I'm not 'in'-sane. Indeed, I am so far 'out' of sane that you appear a tiny blip on the distant coast of sanity. Bucky Katt, Get Fuzzy

Da Bishop: There's a dead bishop on the landing. I don't know who keeps bringing them in here. ....but society is to blame.
Reply
#5
@ichabod801: Yes, that was a typo. Thank you for catching that. And thanks for the clarification using your lunch list example. This is enormously helpful.

@micseydel: Your sample function which returns the length of a list in a given range (n) proves that it matches the function's initial n parameter. I really like this. Thank you for sharing.

I played around some more with slicing and such. I’ve pushed my changes back up to my GitHub repo here.

Here is my take way based on what I have learned here:

One way to distinguish between len() and the integers passed into a list slice is to reflect on the following code snippet:

colours5 = ["indigo", "orange", "crimson","emerald",]

# To get the last index of len of list, this will not work:
print(f'First attempt: {colours5[len(colours5):]}') 

# To get the last index of len of list...
# ...you need to subtract 1 integer from the total length of the list:
print(f'Second attempt:{colours5[len(colours5)-1:]}') 
Here is the output:

Quote:First attempt: []
Second attempt:['emerald']

The final item in the list above is the 4th. But submitting '4' as a slice returns nothing. To grab the last item in a list, you need to take the total length minus 1. This distinction between len() and slicing parameters is a helpful way of recalling the difference between the two. This is clear and easy to remember.

Referring back to the code sample in my original post (here it is again):

import random
new_list = [ random.randint(0,5) for i in range(0,5) ]
print(new_list)
Here is the output:

Quote:[3, 4, 1, 4, 0]

From this code snippet, as you can see, if there is a single integer passed into the range() function, it starts at 0 and goes up to but does not include the final number in the generated list. In this way it's similar to list slicing. But when passing in parameters into randint(), the result includes any integer from the beginning parameter as well as up to (and including) the final integer at the end. This isn't quite as easy to remember. Next time I get disoriented when writing my script in the future, I'll refer back to this written distinction.
Reply
#6
(Nov-20-2019, 09:17 PM)Drone4four Wrote: From this code snippet, as you can see, if there is a single integer passed into the range() function, it starts at 0 and goes up to but does not include the final number in the generated list. In this way it's similar to list slicing. But when passing in parameters into randint(), the result includes any integer from the beginning parameter as well as up to (and including) the final integer at the end. This isn't quite as easy to remember.

Hi!

You could use mnemonics:
If you are a RANGER (for range()), you are a good guy, and don't reach the last limit ...
If you are RANDY (for randint()), you reach the limits ...

Angel Shifty Cool Tongue Big Grin

All the best,
Reply


Possibly Related Threads…
Thread Author Replies Views Last Post
  slicing and indexing a list example leodavinci1990 4 215 Oct-12-2020, 06:39 AM
Last Post: bowlofred
  Need to access Windows Machine or Personal Certificate Store and send to web app seswho 0 167 Sep-14-2020, 04:57 PM
Last Post: seswho
  New User online Guide NAP1947 1 216 Sep-06-2020, 04:36 PM
Last Post: Larz60+
  Including modules in Python using sys.path.append JoeDainton123 1 357 Aug-24-2020, 04:51 AM
Last Post: millpond
  Including a Variable In the HTML Tags When Sending An Email JoeDainton123 0 235 Aug-08-2020, 03:11 AM
Last Post: JoeDainton123
  Remove from end of string up to and including some character lbtdne 2 489 May-17-2020, 09:24 AM
Last Post: menator01
  Not able to crack a simple visualization – missing something basic – plz guide darpInd 4 438 Mar-27-2020, 09:26 AM
Last Post: darpInd
  how to import a module which is installed to personal folder using pip install --pre? geekgeek 2 496 Mar-09-2020, 02:38 PM
Last Post: geekgeek
  How to change 0 based indexing to 1 based indexing in python..?? Ruthra 2 480 Jan-22-2020, 05:13 PM
Last Post: Ruthra
  Applying row height to all rows including and after row 7 curranjohn46 2 946 Oct-14-2019, 03:10 PM
Last Post: curranjohn46

Forum Jump:

User Panel Messages

Announcements
Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020