Related
I am new to python / coding and looking to understanding the range function more and how it is used in conjunction with the "for" loop.
So I know that the range function takes in 3 parameters: range(start, stop, step). For the below function, I passed in my array called test_numbers, then created a "for" loop where "i" starts counting from len(list_of_numbers)-1 (to give me the index values)
I was expecting the result for "i" to print 0,1,2,3,4,5 but it only printed 5 for "i". I am wondering why is that? If I put 6 as the "stop" argument, would it not just print from range start of the length of the array as in [0,1,2,3,4,5] all the way then stop before 6? that is my confusion. Any help /explanation would be great!
test_numbers = [1,2,4,5,6]
def testRange(list_of_numbers):
for i in range(len(list_of_numbers), 6):
print(i)
testRange(test_numbers)
The result: 5
Was expecting: 0,1,2,3,4,5
When you call range() with two arguments, the first argument is the starting number, and the second argument is the end (non-inclusive). So you're starting from len(list_of_numbers), which is 5, and you're ending at 6. So it just prints 5.
To get the results you want, the starting number should be 0, and the end should be len(list_of_numbers)+1. If you call it with one argument, that's the end, and 0 is the default start. So use
for i in range(len(list_of_numbers)+1):
or if you want to pass the start explicitly:
for i in range(0, len(list_of_numbers)+1):
range gives you and iterator between (start, end) end not included.
So in your case the iterator is (start=len(list_of_numbers), end=6).
Since len(list_of_numbers) = 5, this translates to range(5,6) which is 1 element, 5, since 6 is excluded.
https://docs.python.org/3/library/functions.html#func-range
range stop parameter is exclusive and start is inclusive - if you provided len result (of 5) and 6 as the stop then the range result will contain only one element [5]
If you'd like to have 0..6 you should use
range(0, 6)
or, what you probably want to do to iterate over all array indices
range(0, len (list_of_numbers))
The simplest way is just iterate throught list, beacause it is iterable:
test_numbers = [1,2,4,5,6]
def testRange(list_of_numbers):
for i in list_of_numbers:
print(i)
testRange(test_numbers)
This question already has answers here:
Why are slice and range upper-bound exclusive?
(6 answers)
Closed last month.
>>> range(1,11)
gives you
[1,2,3,4,5,6,7,8,9,10]
Why not 1-11?
Did they just decide to do it like that at random or does it have some value I am not seeing?
Because it's more common to call range(0, 10) which returns [0,1,2,3,4,5,6,7,8,9] which contains 10 elements which equals len(range(0, 10)). Remember that programmers prefer 0-based indexing.
Also, consider the following common code snippet:
for i in range(len(li)):
pass
Could you see that if range() went up to exactly len(li) that this would be problematic? The programmer would need to explicitly subtract 1. This also follows the common trend of programmers preferring for(int i = 0; i < 10; i++) over for(int i = 0; i <= 9; i++).
If you are calling range with a start of 1 frequently, you might want to define your own function:
>>> def range1(start, end):
... return range(start, end+1)
...
>>> range1(1, 10)
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
Although there are some useful algorithmic explanations here, I think it may help to add some simple 'real life' reasoning as to why it works this way, which I have found useful when introducing the subject to young newcomers:
With something like 'range(1,10)' confusion can arise from thinking that pair of parameters represents the "start and end".
It is actually start and "stop".
Now, if it were the "end" value then, yes, you might expect that number would be included as the final entry in the sequence. But it is not the "end".
Others mistakenly call that parameter "count" because if you only ever use 'range(n)' then it does, of course, iterate 'n' times. This logic breaks down when you add the start parameter.
So the key point is to remember its name: "stop".
That means it is the point at which, when reached, iteration will stop immediately. Not after that point.
So, while "start" does indeed represent the first value to be included, on reaching the "stop" value it 'breaks' rather than continuing to process 'that one as well' before stopping.
One analogy that I have used in explaining this to kids is that, ironically, it is better behaved than kids! It doesn't stop after it supposed to - it stops immediately without finishing what it was doing. (They get this ;) )
Another analogy - when you drive a car you don't pass a stop/yield/'give way' sign and end up with it sitting somewhere next to, or behind, your car. Technically you still haven't reached it when you do stop. It is not included in the 'things you passed on your journey'.
I hope some of that helps in explaining to Pythonitos/Pythonitas!
Exclusive ranges do have some benefits:
For one thing each item in range(0,n) is a valid index for lists of length n.
Also range(0,n) has a length of n, not n+1 which an inclusive range would.
It works well in combination with zero-based indexing and len(). For example, if you have 10 items in a list x, they are numbered 0-9. range(len(x)) gives you 0-9.
Of course, people will tell you it's more Pythonic to do for item in x or for index, item in enumerate(x) rather than for i in range(len(x)).
Slicing works that way too: foo[1:4] is items 1-3 of foo (keeping in mind that item 1 is actually the second item due to the zero-based indexing). For consistency, they should both work the same way.
I think of it as: "the first number you want, followed by the first number you don't want." If you want 1-10, the first number you don't want is 11, so it's range(1, 11).
If it becomes cumbersome in a particular application, it's easy enough to write a little helper function that adds 1 to the ending index and calls range().
It's also useful for splitting ranges; range(a,b) can be split into range(a, x) and range(x, b), whereas with inclusive range you would write either x-1 or x+1. While you rarely need to split ranges, you do tend to split lists quite often, which is one of the reasons slicing a list l[a:b] includes the a-th element but not the b-th. Then range having the same property makes it nicely consistent.
The length of the range is the top value minus the bottom value.
It's very similar to something like:
for (var i = 1; i < 11; i++) {
//i goes from 1 to 10 in here
}
in a C-style language.
Also like Ruby's range:
1...11 #this is a range from 1 to 10
However, Ruby recognises that many times you'll want to include the terminal value and offers the alternative syntax:
1..10 #this is also a range from 1 to 10
Consider the code
for i in range(10):
print "You'll see this 10 times", i
The idea is that you get a list of length y-x, which you can (as you see above) iterate over.
Read up on the python docs for range - they consider for-loop iteration the primary usecase.
Basically in python range(n) iterates n times, which is of exclusive nature that is why it does not give last value when it is being printed, we can create a function which gives
inclusive value it means it will also print last value mentioned in range.
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()
The range(n) in python returns from 0 to n-1. Respectively, the range(1,n) from 1 to n-1.
So, if you want to omit the first value and get also the last value (n) you can do it very simply using the following code.
for i in range(1, n + 1):
print(i) #prints from 1 to n
It's just more convenient to reason about in many cases.
Basically, we could think of a range as an interval between start and end. If start <= end, the length of the interval between them is end - start. If len was actually defined as the length, you'd have:
len(range(start, end)) == start - end
However, we count the integers included in the range instead of measuring the length of the interval. To keep the above property true, we should include one of the endpoints and exclude the other.
Adding the step parameter is like introducing a unit of length. In that case, you'd expect
len(range(start, end, step)) == (start - end) / step
for length. To get the count, you just use integer division.
Two major uses of ranges in python. All things tend to fall in one or the other
integer. Use built-in: range(start, stop, step). To have stop included would mean that the end step would be assymetric for the general case. Consider range(0,5,3). If default behaviour would output 5 at the end, it would be broken.
floating pont. This is for numerical uses (where sometimes it happens to be integers too). Then use numpy.linspace.
Question
I want to remove items from a list such that I keep the first n items, and remove the next 2n items.
For example
for n=8, I want to keep the first 8, remove the next 16 and repeat this as necessary:
a = range(48)
Which I want to become
[0,1,2,3,4,5,6,7,24,25,26,27,28,29,30,31]
This is to pick out the first 8 hours of a day, and run a function on each hour.
I've found it hard to phrase this in search queries so the answer is probably simple but I've had no luck!
You could just use a comprehension list:
[ a[i] for i in range(len(a)) if (i % 24 < 8) ]
The above only create a new list. If you want to edit the list in place, you must explicitely delete unwanted elements, starting from the end to avoid changing indexes:
for i in range(len(a) - 1, 0, -1):
if i % 24 >= 8:
del a[i]
def hours(n):
items = [x for x in range(49)]
del items[n:n*3]
print(items)
hours(8)
Depending on how new you are you might have a hard time understanding this code, so I will try to explain a little:
We start by creating a function which takes a parameter n which, for test purposes, we will be using 8 then we use a list comprehension to generate all our numbers (0, 48) and then delete the unneeded elements using the del statement, we are deleting from the nth to the n*3 element in the list. For example, if n were to be passed as 9 our use of the del statement could be translated as: del [9:27].
Hope this makes sense.
This should be quite easy to understand
a = range(48)
n=8
result=[]
while a:
result+= a[:n]
a=a[n*3:]
print result
How to use more than one condition in Python for loop?
for example in java:
int[] n={1,2,3,4,6,7};
for(int i=0;i<n.length && i<5 ;i++){
//do sth
}
How dose the python for loop do this?
The Python for loop does not, itself, have any support for this. You can get the same effect using a break statement:
n = [1, 2, 3, 4, 6, 7]
for i in n:
if i >= 5:
break
# do something with i
In Python, a for is really a foreach that iterates over some "iterator" or some "iterable object". This is even true when you just want to repeat a specific number of times:
for i in range(1, 8):
# do something with i
In Python 2.x, the above for loop builds a list with the numbers 1 through 7 inclusive, then iterates over the list; in Python 3.x, the above loop gets an "iterator object" that yields up the values 1 through 7 inclusive, one at a time. (The difference is in the range() function and what it returns. In Python 2.x you can use xrange() to get an iterator object instead of allocating a list.)
If you already have a list to iterate over, it is good Python to iterate over it directly rather than using a variable i to index the list. If you still need an index variable you can get it with enumerate() like so:
n = [3, 5, 10, "cat", "dog", 3.0, 4.0] # list can contain different types
for i, value in enumerate(n):
# we only want to process the first 5 values in this list
if i >= 5:
break
# do something with value
EDIT: An alternate way to solve the above problem would be to use list slicing.
for value in n[:5]:
# do something with value
This works if n is a list. The for loop will set value to successive items from the list, stopping when the list runs out or 5 items have been handled, whichever comes first. It's not an error to request a slice of longer length than the actual list.
If you want to use the above technique but still allow your code to work with iterators, you can use itertools.islice():
from itertools import islice
for value in islice(n, 5):
# do something with value
This will work with a list, an iterator, a generator, any sort of iterable.
And, as with list slicing, the for loop will get up to 5 values and it's not an error to request an islice() longer than the number of values the iterable actually has.
The direct equivalent of your Java code is a while loop:
n = [1, 2, 3, 4, 6, 7]
i = 0
while i < len(n) and i < 5:
# do sth
i += 1
You could also do:
n = [1, 2, 3, 4, 6, 7]
for x in n[:5]:
# do sth
Here is one way to have two or more conditions with the for loop, which is what the question actually asks. The point I am trying to make is that it can be done, but isn't very pythonic and it's generally not a good idea to try to rewrite code from another language line by line.
from itertools import takewhile, count
n=[1,2,3,4,6,7]
for i in takewhile(lambda i:i<len(n) and i<5, count()):
print(i)
You can write a while loop with the same sort of logic (but in Python, && is spelled and, and || is spelled or); or you can use a for loop that iterates over a sequence and then add extra logic to break out of the loop.
In Python, we prefer not to use integers to index into a container. This would actually break a lot of our code, because we do not check the data types at compile-time, and some containers cannot be indexed into. Wanting to index into a container is already a design smell: the fact that everything is in a sequential container is supposed to mean that none of the elements are "special", or we would have kept them separate. Iteration is iteration, and we should not go out of our way to make it more complicated.
Assuming that you want the ith item of n somewhere in the loop, enumerate saves us from typing n[i] all over the place - the value will be stored in the variable item
n = [1,2,3,4,6,7]
for i, item in enumerate(n):
if i>=5:
break
# do something
print item # for example
Note that the loop will terminate automatically if the length of n is less than 5
Python's for is not like the for in languages based on C syntax. In Python, for iterates over a sequence, whereas in C it loops while a condition is true. This is a profound difference.
The C-like for can be replaced roughly with the following code:
i = 0;
while (i < n.length && i < 5) {
// do sth
i++;
}
(There are some complications from break and continue, but let's ignore those for now.)
This rewrite also indicates a way to do what you want in Python: use while:
i = 0
while i < len(n) and i < 5:
// do something
i += 1
In your particular case, however, it is easiest to use for with a suitable list of indexes:
for i in range(min(len(n), 5)):
// do something
range will return a list of integers (0, 1, 2, ...) and what you want is to have the list go up to 5, or the length of your array, whichever is smaller. The above code achieves that.
The for statement in Python iterates a "list" of objects (I put list in quotes because I mean it in the generic sense, it can iterate over anything that is iterable).
To code a conditional loop (rather than iterating until completion), use while:
n = [1, 2, 3, 4, 6, 7]
i = 0
while i < len(n) and i < 5:
# do stuff
i += 1
Now just to be complete, your example could also be written as:
n = [1, 2, 3, 4, 6, 7]
for i in range(0,min(len(n),5)):
# do stuff
or:
n = [1, 2, 3, 4, 6, 7]
for i in range(0,len(n)):
if i >= 5:
break
# do stuff
print max(3 for i in range(4))
#output is 3
Using Python 2.6
The 3 is throwing me off, heres my attempt at explaining whats going on.
for i in range(4) makes a loop that loops 4 times, incrementing i from 0 to 3 at the start of each loop. [no idea what the 3 means in this context...] max() returns the biggest iterable passed to it and the result is printed to screen.
3 for i in range(4) is a generator that yields 3 four times in a row and max takes an iterable and returns the element with the highest value, which is, obviously, three here.
This evaluates to:
print max([3,3,3,3])
... which is an elaborate way to say print 3.
expr for x in xs is a generator expression. Typically, you would use x in expr. For example:
[2*i for i in range(4)] #=> [0, 2, 4, 6]
It can be rewritten as:
nums = []
for i in range(4):
nums.append(3)
print max(nums) # 3! Hurrah!
I hope that makes its pointlessness more obvious.
The expression:
print max(3 for i in range(4))
is printing the result of the max() function, applied to what is inside the parentheses. Inside the parentheses however, you have a generator expression creating something similar to an array, with all elements equal to 3, but in a more efficient way than the expression:
print max([3 for i in range(4)])
which will create an array of 3s and destroy it after it is no longer needed.
Basically: because inside the parentheses you will create only values that are equal, and the max() function returns the biggest one, you do not need to create more than one element. Because with the number of elements always equal to one, the max() function becomes not needed and your code can be effectively replaced (at least in the case you have given) by the following code:
print 3
That is simply all ;)
To read more about differences between comprehension and generator expression, you can visit this documentation page.