Coding Path Questions?

Hello @mtf ,

Thank you so much for this information. I really appreciate it.

So basically the range function creates or generates a range of numbers and len() allows us to access an index in that range?

Sorry if I am struggling to understand. I think I am on the right track but will wait for your response.

Thank you again for your time.

P.S. With the code below specifically the line that is written as such: total_revenue += prices[i] * last_week[i]

Why couldn’t i write the cuts_under_30 = [hairstyles[i] for i in range(len(new_prices)) if new_prices[i] < 30] like the following: cuts_30 = [hairsytles[i] for i in new_prices[i] if new_prices[i] < 30]
Since new_prices is already a list and there is no need to create a range using the range()? Sorry if I am misunderstanding this.

hairstyles = [“bouffant”, “pixie”, “dreadlocks”, “crew”, “bowl”, “bob”, “mohawk”, “flattop”]

prices = [30, 25, 40, 20, 20, 35, 50, 35]

last_week = [2, 3, 5, 8, 4, 4, 6, 2]

total_price = 0

#created a loop to sum up the total of all our prices for our cuts

for price in prices:

total_price += price

#created a variable to get the average price of our cuts

average_price = total_price/len(prices)

#printing out the average price of our cuts

print(“The average price of our cuts are:”, average_price, “\n”)

#prices were too expensive. Created a for loop to cut the price of each haircut but $5 using a compreshive list

new_prices = [price - 5 for price in prices]

print(“The new price for each of our cuts are:\n”, new_prices, “\n”)

#finding out the total revenue of our income from last week

total_revenue = 0

for i in range(8):

total_revenue += prices[i] * last_week[i]

print(“Total Revenue is:”, total_revenue, “\n”)

#Finding out our daily average of our total_revenu

average_daily_revenue = total_revenue / 7

print(“Your average daily revenue is:”, average_daily_revenue, “\n”)

#list comprehension to advertise haircuts that are under 30 dollars

cuts_under_30 = [hairstyles[i] for i in range(len(new_prices)) if new_prices[i] < 30]

print(“Here are the cuts that are under $30 dollars:”, cuts_under_30)

1 Like

len() is a function that returns the length of an iterable object. That’s where its purpose ends. When we take into account that iterable indices are zero-based, the last element in a list will be at index N minus 1, hence range() defaults to zero, and excludes N. The number of items is not affected. Remember z, above. Its length is five, meaning there are five items. The range object had a count from zero to four, which includes every index.

It is the index by which we locate the position of an element in the iterable. Knowing the length of something is how we know we can count from one to N (the length) and reach the end of the list, but not surpass it.

The problem with that is that i in new_prices[i] is accessed before it is defined in the loop. It’s like saying i = i when the program doesn’t know what i is, yet.

Furthermore, we would only iterate the parent iterable, not any that it contains, or may contain. The biggest elephant in the room is the lack of a range to generate an index by which we can access the element values by position.

As mentioned earlier, since we are accessing two lists simultaneously, we absolutely must have an index (a range object). We cannot access corresponding elements by any other means, and the .index() method is unreliable.

Down the road we will learn how to enumerate a list, thereby exposing a persistent record of the index associated with each value. That’s a whole new rabbit hole that requires knowledge of the above mentioned objects and functions, so keep to the present topic, however long it takes to sink in. It won’t pay to move forward without ample confidence. In due course this will become second nature. Be diligent and patient as you go.

Hello @mtf !

Thank you once again for explaining the information you are helping me with. It is much appreciated.

I think I am starting to see where my thinking is stinking lol. Please correct me if I am wrong.

In the code posted above, where I inserted all the code from the lesson I went through, it looks like as you stated, that the last line of code new_prices doesn’t have a way to iterate through the list and thus why the range() is being used. Would this be correct?

I guess the way I understood the code that I wrote above was that new_prices was defined before the comprehensive for loop and therefore it should have had the ability to access the idicies at new_prices using new_prices[i]. So would this be where I was thinking wrong about this topic?

One last thing, that I am trying to wrap my head around is range(len(new_prices)).
Here the way I am interpreting the code is that we create a range to iterate through which takes the argument new_prices. So if this is true, then why do I need to add the length of new_prices in order to iterate through this range?

Thank you again for your kind patience. It is always appreciated.

Best Regards,

The only argument(s) permitted by the range() function is(are) numbers, namely integers. Length, when returned from the len() function is always an integer (a counting number) or zero if the object is empty.

len('')    #  0  =>  empty string
len([])    #  0  =>  empty list

len(new_prices)    #  8

So, range(len(new_prices)) is the exact same thing as range(8). When we cast the range object to a list, we will have,

[ 0, 1, 2, 3, 4, 5, 6, 7 ]  => 8 indices

We need to use the len() function to count the elements in the new_prices list since we cannot physically count them when the program is running. We need the program to do that, dynamically.

Hello @mtf

Thank you again for your explanation.

So basically len() in this instance is providing the size of our list and the range() function is creating a list to iterate through?

Also in the code I posted above that shows the whole exercise, wasn’t new_price, which is a list, already declared?

So just curious why couldn’t it be written as range(new_prices) instead of range(len(new_prices))?

Thank you again for all your time and help.

1 Like

Exactly, and only that. The length, or number of countable items in the list. The return value is an integer.

Not a list, proper, but an iterator that unlike other iterators is not consumed. It is persistent. To view the range object we can either iterate over it and print the values, or cast it to a list object.

 list()   =>  list constructor
 len()    =>  iteraable item count
 range()  =>  generate an ordered sequence

list(range(len(new_prices)))
# [ 0, 1, 2, 3, 4, 5, 6, 7 ]    =>  list of indices

new_prices is list, not an integer. len(new_prices) is an integer. Remember, range() can only take integers as arguments, and len() can only take an iterable as the argument.

Strings, lists, tuples and sets are all iterable and can be passed into len() to obtain the length (element count).

Hello @mtf ,

Thank you once again for your time and help.

I think I am good to go on the len() function which returns an integer. As far as range goes, it looks like it creates an object that is iteratable for a defined set of length. For example if I call the range(10) the range function will create an object between the numbers of 0 and 9. Would this be accurate?

1 Like

Yes. Now you’re getting the picture. For the time being get comfortable with using the range() function, and be sure to read the documentation and learn all the various ways it can be implemented.

To further understand how iterators work, pour everything you have into that unit when it comes around. No point peaking ahead, just work with the range and len functions enough for it to grow on you.

During your off time (when not immersed in lesson material and practice) see if you can accomplish these following configurations:

  • an ordered sequence that starts on a number other than zero.
    ‘range(n, N)’ where n is greater than zero and where ‘N’ is greater than n.
  • an ordered sequence that counts by two’s starting on any n and ending on any N. For this you will need to learn about the step or stride.
  • an ordered sequence where n is negative and N is positive.
  • an ordered sequence where all the numbers are negative.

Once you’re comfortable with your outcomes, then and only then repeat all the above, point by point, except this time with the sequence being in reverse. It’s important you leave this until you have done all the above in left to right order.

Hello @mtf,

Thank you so much for letting me know that I am on the right track. I really appreciate your help.

I have been trying to practice what you had mentioned in your bullet points and I am still working on getting more acquainted with the practice of using the range () and len() functions.

I have a quick question. I am working on an advanced python challenge within codecademy. Below is the code for the function I need help with to determine which index has the same matching number location of the two lists provides as arguments into the function I created. After that I have to then print the new list index showing the exact position of the number locations.

Blockquote
#Write your function here
def same_values(lst1, lst2):
new_list =
for index in range(len(lst1)):
if lst1[index] == lst2[index]:
new_list.append(index)
return new_list

print(same_values([5, 1, -10, 3, 3], [5, 10, -10, 3, 5]))

The code above works but the part I am still having trouble with is the following:

Blockquote
for index in range(len(lst1)):

In the above code, why couldn’t I just use for index in lst1?

The reason I ask this is because lst1 is already a list that is passed into the argument of the function. So this list should therefore have the size of the list argument that is passed in. Or am I am just on another planet looking at this wrong still? lol

Thanks again for your help

Since there are two lists we need to create an index. Recall that,

for x in iterable:

is a ‘read-only’ loop such that x is each value in turn from left to right. index would not be a variable name one would use since it is a misnomer. A list item (value) is independent of the structure, and not an index. It is, at index i, not i.

We need to be able to access corresponding elements in two lists, hence the range to act as a stand-in index.

 for i in range(len(lst1)):

i is an in-sequence value between 0 and len(lst1) minus 1. Remember the stop value of a range is not in the returned sequence. Given use of a range object as an index, we know it defaults to zero-based indexing.

range(10)

has ten values in its returned sequence, as in, when employed it counts from 0 to 9.

For this challenge we must assume that both lists have the same length so it doesn’t matter which list we query for the length. They both will return the same counting sequence from range(). We do need to test both lists on each corresponding index.

Keep in mind that we are not querying whether the list contains the same values (in any order). The extra constraint is that values are the same at corresponding positions in their respective lists. We can only do this with an index we can iterate over; ergo, a range object.


Moving off topic, let’s explore finding matches anywhere in either list. For that we don’t need an index. We can use our handy read-only loop.

>>> a = range(3, 91, 3)
>>> b = range(5, 91, 5)
>>> [x for x in a if x in b]  # list comprehension
    [15, 30, 45, 60, 75, 90]
>>> 

Ignore the logic and focus on the for loop, keeping note that we have no certain knowledge about either list, only that one will contain values divisible by three, and the other values divisible by five. Their lengths are for sure not the same given that 3 goes into 90 more times than 5 does.

We are only seeking matches that occur anywhere in either list. If we don’t count the space taken up by the actual code, the above only consumes 96 bytes of memory split between the two range objects.

Hello @mtf,

Thank you again for your time and help with understanding ranges and for loops.

I think I am starting to understand a little more. It looks like I need to create a loop that is able to iterate over a list. In order to accomplish this I need to use the for i in range(len(lst)) line of code in order to accomplish this. Am I on the right track with this?

When the function is called that passes in the lists as arguments in the example below, why wouldn’t the python know that the argument is a list since it uses brackets ?

print(same_values([5, 1, -10, 3, 3], [5, 10, -10, 3, 5]))

Also one other quick question…what is the ‘x’ doing in the first part of the for loop below:

a = range(3, 91, 3)
b = range(5, 91, 5)
[x for x in a if x in b] # list comprehension
[15, 30, 45, 60, 75, 90]

Thank you again for all your help.

1 Like

We use a stand-in range as an index when we need read/write access to the list elements, or as above when comparing two lists by position. Remember, we cannot compare elements at the same position without an index.

  • len() always returns a positive integer OR zero
  • range only takes integer arguments for start, stop and step
  • range only returns a sequence of numbers,

With practice these will become like right and left hands, and all will become second nature in time. Just don’t read anything else into these two functions and their purpose. There are no hidden secrets.

  • The x at the very front is the expression to add to the final list.
  • The x in the for loop is the iteration variable except in this case it is the actual value, not an index
  • a is the iterable
  • x in b is the test for membership in the other iterable, b

If you are not familiar with list comprehensions, then let’s reconstruct it as a normal loop:

c = []
for x in a:
    if x in b:
        c.append(x)
print (c)
[15, 30, 45, 60, 75, 90]
1 Like

Missed that question earlier. Python does ‘know’ they are lists. We only have to look at the attributes, or check the type.

>>> type([*'string'])
    <class 'list'>
>>> print ([*'string'])
    ['s', 't', 'r', 'i', 'n', 'g']
>>> 

Now let’s check the attributes…

>>> dir([*'string'])
    [ ... ]    # a lengthy list
>>> hasattr([*'string'], '__delitem__')
    True
>>>

Notice above I’ve been using list literals, not variables with a list assigned. This is only to demonstrate the ‘on the fly’ nature of Python. It does not do anything until it has determined which type of data it is parsing, and instantiating that class. That gives the object its attributes.

Above we query if the object has a '__delitem__' attribute, which it does. (This is a ‘dunder method’, but you’ll learn more later, so ignore for now.) As far as I know, list objects are the only class with that attribute since no other iterable permits deletion of items.

Correction: dict objects also have that attribute. My bad.

>>> s = [*'string']
>>> del(s[3])    # this is a global function, not a method
>>> s
    ['s', 't', 'r', 'n', 'g']
>>> s.remove('n') 
>>> s.remove('g')
>>> s
    ['s', 't', 'r']
>>> s.pop(1)
    't'
>>> s
    ['s', 'r']
>>>

Bottom line, we cannot trick Python into misclassifying the objects in our function call arguments. They are the first thing to be parsed to determine their type, their position, and whether or not they are valid arguments.

Hello @mtf ,

Thank you again for your time. It looks like I will be learning more about the things I don’t understand over time. I am excited about the things I have learned so far. I have been using this course here at codecademy in addition to YouTube videos from Harvard in my spare time to learn things hands on using VSCode. Still taking the time to get use to VSCode but I am moving along slowly but surely.

I appreciate the explanations you are providing. I don’t understand them all but I am coming around to knowing what you mean by some of them. I guess this is just a learning curve on my end and I am doing the best I possibly can at the moment. I am motivated and excited. Currently I finished a bunch of Python challenges within Codecademy. I did really well on the functions. I did average on the loops and didn’t do great on the lists. I have gone back to re-read some of the material and I am guessing with practice I will get better over time.

Quick question…With our previous conversation below:

When the function is called that passes in the lists as arguments in the example below, why wouldn’t the python know that the argument is a list since it uses brackets ?

print(same_values([5, 1, -10, 3, 3], [5, 10, -10, 3, 5]))
[/quote]

Since python understands that the argument is a list based on the class, why doesn’t our for loop know that it is a list to iteratre through? Or am I misunderstanding this?

One last question. When I am done with the first course in python should i go to the intermediate and advanced course? I want to work with sites and developing apps in the future. I was thinking about going to JavaScript or the full stack dev course. What is your thoughts and opinions, I started with python because it was supposed to be an easier method of entry when learning code. Don’t get me wrong I do see that I already have challenges with that. I ultimately want to make video games more full time in the end. I wanted to start thinking about and writing code and therefore I tried python first. The games I want to make are not huge AAA games. I want to make games on the 2D side of things.

Thank you again for your time and help.

1 Like

for loops don’t care that it is a list, nor would it know. It only works on classes that are iterable and that have an '__iter__' attribute. We tell for what to do, not the other way around.

As to your other question, now that you are learning Python, I would stick with it. Don’t move forward until you have no more questions or difficulty with the basics. It will only bog you down. One needs to be absolutely comfortable with the core language, as well as how to work with the built-in modules. If you get ahead of yourself there will be no reward waiting at the end of the tunnel.

Essentially, the basics of Python are enough to immerse one in the core programming concepts. Most languages mirror these concepts so once you are cool with data structures (lists, dictionaries, sets, tuples) and primitives (strings, numbers, booleans) and proficient at designing loops and control flow structures these will translate over to JavaScript quite readily.

Give yourself a few more weeks of working with basic Python and jump over to HTML/CSS/JavaScript (they all go together) and begin exploring web and app development in those APIs. It will be new, and with a steep learning curve, at first. You only have to apply yourself and not get too ambitious or impatient. Ground yourself in the basics so well you can do it in your sleep.

Hello @mtf,

I hope you are doing well today.

Thank you again for your help and advice. It is always appreciated.

I will take your advice and work on finishing up the course. Currently I am working with strings, string slicing and accessing particular parts of a string. Good stuff. Still working on some of the concepts.

Here is where I am having some trouble using the len() function for the word blueberry which has 9 characters when favorite_fruit = “blueberry”.

length = len(favorite_fruit)
last_chars = favorite_fruit[length-4:]
print(last_chars)

Output: erry

Here is another example that I had to work on that was causing me the same issue:

first_name = “Reiko”
last_name = “Matsuki”

def password_generator(first_name, last_name):
length_first = len(first_name)
length_last = len(last_name)
first_last_three = first_name[length_first - 2:]
last_last_three = last_name[:4]
temp_password = first_last_three + last_last_three
return temp_password

temp_password = password_generator(first_name, last_name)

print(f"Your new employee username is: {temp_password} Please keep it safe!")

Should I be reading the length_first -2: below as 5 -2 which is 3 from the backside of the list of characters that start at zero, one, two, three and don’t count the third character item in the list or should I just look at that code as take the last 2 characters and store it into the variable first_last_three?

first_last_three = first_name[length_first - 2:]
last_last_three = last_name[:4]

Thank you again for your time, help and advice. I really appreciate your kindness.

P.S. I also understand now that a for loop has no idea about a list that is taken as an argument from a function. Good to know. Based on my question below.

why doesn’t our for loop know that it is a list to iterate through?

for loops don’t care that it is a list, nor would it know. It only works on classes that are iterable and that have an '__iter__' attribute. We tell for what to do, not the other way around.

1 Like

When working with iterables from right to left then it is simpler to work with negative indices that also show that intention.

>>> 'string'[-3:]
    'ing'
>>>

See how easy that is? How easy is it for the reader to ascertain the expected outcome? Very, since we tell them our intentions without any complicated arithmetic. The above says, 'go three indices over from the right, and return everything from that position to the end on the right.

Yes, it is the same as,

    s = 'string'
    n = len(s)
    print (s[n-3:])    #  'ing'

In our earlier example we don’t even need to know the length of the string (or iterable). If the string is not 3 or more characters in length, the slice will not raise an exception since it is virtual, and does what it can, ignoring the actual length as an issue.

>>> 'as'[-3:]
    'as'

It did what it could with what it was given. That little colon in there is the savior, and what makes this virtual, and not imperative. We might not get the outcome we want, but it won’t raise any eyebrows for Python, either. No errors are ever reported with slices.

Very few cultures have short names like, Li or Ng so it is 95% certain that most names will be three or more letters. We can adjust our code when this is not the case.

Now when capturing a slice from the left side, we should probably be thinking positive indices.

>>> 'string'[:3]
    'str'
>>> 

That also tells the reader what our intentions are, and that we are slicing from left to right. We also know to expect three characters in the outcome.

Slicing is a fantastic tool, and one we find in many languages, though perhaps not applied in so simple a fashion as Python. In any case, slices are always virtual.

def gen_pwd(first, last):
    return first[:3] + last[-3:]
>>> gen_pwd('Wee', 'Gillis')
    'Weelis'
>>>

Our function did not need to know the length of the inputs. If we have a need for minimum six characters, then our function will only need to examine the return value before sending it back to the caller. That is another story that falls under, ‘mitigation’ and will require additional logic to deal with the imposed constraints and try to return the minimum number of characters, perhaps by stealing some of the unused characters of the other name. It takes ideation to work this one out. For another discussion.


Following is an example where we take a name, remove the space(s) and form a string from the remaining letters. We then wish to extract the middle six letters from that string.

>>> n = 'Robert Browning'
>>> p = "".join(n.split())
>>> p
    'RobertBrowning'
>>> m = len(p) // 2
>>> s = p[m-3:m+3]
>>> s
    'rtBrow'
>>>

We don’t know what the starting letter will be, only the midpoint, and from that we subtract half of six (3) and add the same on the stop. The outcome has the requisite number of characters, and we can be happy with that.

This sort of thinking doesn’t just happen or fall out of thin air. It is a sort of ‘thinking outside the box’ that we acquire intuitively with time and lots of practice and practical work.

Hello @mtf,

Thank you again for your time and help explaining this information.

I understood the very first part of what your coding examples were which I am really excited about. Pretty cool I didn’t need to use the len() but thank God it worked either way.

The last part of the coding block you added I will need more time to think about it more. I don’t understand it at the moment.

As a side note, I am feeling frustrated with lists and accessing items within them. I feel kinda lost. I am doing the exercises and I am thinking that I know the answer but ultimately the code I write says I don’t. Not really sure what else to do to better understand these concepts better. One second I am like ok my for loop needs a range(len(name)) in order to have a list to iterate through and the next second I am trying to access a particular part of the range and I write the code incorrectly. Any thoughts? Just really really frustrated. I am not giving up just want to really get a grasp on what I am learning.

As a side note I am not in the string method section and using methods like .upper(), .lower(), .split() and .title(). The .split() is really confusing to me. From what I understand so far it breaks up a string using a delimiter and what ever that delimiter the .split() puts it as a new item in a list. I think that is what is happening.

Here is an excersise below that just made me feel like I know nothing about what is going on.

authors = “Audre Lorde,Gabriela Mistral,Jean Toomer,An Qi,Walt Whitman,Shel Silverstein,Carmen Boullosa,Kamala Suraiyya,Langston Hughes,Adrienne Rich,Nikki Giovanni”

author_names = authors.split(‘,’)

print(author_names, “\n”)

author_last_names =

for name in author_names:
author_last_names.append(name.split(" ")[-1])

print(author_last_names, “\n”)

print(authors[-1], “\n”)

In the following code to get the last name of the code above:

for name in author_names:
author_last_names.append(name.split(" ")[-1])

I think to myself how in the world would I ever know that name.split(" ")[-1]
is even a way I could write this code, referring to the [-1] at the end. How would I ever know this? LOL! This is the kind of thing that frustrates me to no end. I am having to click on the solutions to see how the final code is written to even get a small glimpse of what is going on.

From what I read earlier in the forums of others having the same problem with that line of code to get the last name I am not the only one having this problem. Once a name is split using the white space how would the -1 know that the last name is another element in the list?

Sorry to vent…I am just confused and I want to get better at what I am learning. I am just frustrated and wish I had a better understanding of for loops, lists (strings or other objects).

Thank you again for your help and time.

print(author_names[-1], “\n”)

Hello @mtf ,

One last frustration I am having difficulty with is documentation. I have tried reading documentation on python.org and my brain immediately turns to mush trying to read it. Any thoughts on how to get better with reading documentation more easily?

Keep reading until they make sense. The documentation is not going to change. The promises the language makes are fixed. Documentation is how we learn of those promises. What does a built-in function promise to do? It all takes time, and interpretation of what we read. Give up at this stage of learning, and what’s left but to go home? You’re at a difficult stage, alright. Stay the course and it gets easier with each eureka moment. It all comes down to understanding the promises that the language makes.

a = 7
b = 6
c = a + b

The language promises me the sum of a and b. We take that on faith. That is why we are discussing promises, not certainty.

1 Like