IndexError on list.pop() in list comprehension - python

To learn python I am implementing Bulls and Cows. So I need to generate a 'random' number. I tried this:
possibilities = range(10)
number = "".join([str(possibilities.pop(random.choice(possibilities))) for i in range(4)])
Which I thought was a nice solution to the problem, but once in every couple of runs I get an IndexError on that line: IndexError: pop index out of range
So it looks like random.choice() gives me a non existent index. How come?
Or am I making a wrong assumption and is the problem something else entirely?
Come to think of it, I haven't got a clue how to debug a statement like this, any pointers on that?
Edit
My first solution was of the procedural kind:
for i in range(4):
idx = random.choice(possibilities)
number = number + str(possibilities[idx])
del possibilities[idx]
This code seems to do the exact same thing as the one-liner above, but has no problems whatsoever.

You aren't randomly choosing indices, you're choosing values from the list. What if you pick 9 after a lower number or two has been popped from the list?
Instead, use random.sample:
number = "".join(map(str, random.sample(range(10), 4)))

Related

IndexError: list index out of range - when it's clearly not

Pretty straight forward random password generating program.
I am generating 8 unique random numbers assigning those numbers to a variable and using that variable to print out a unique character from the alpha list.
The problem is that I am getting a "IndexError: list index out of range" error some of the times I'm running the program. I double checked and the the numbers that it's saying are out of range are totally in range since they are either 3, or 55, or etc. And I think the IndexError only pops up for the alpha list (e.g. alpha[c5]) not the actual nums list.
What am I missing? Please be gentle, I am new to programming. Sorry if this is a stupid question.
from random import random, sample
alpha = ['A','B','C','D','E','F','G','H','I','J',\
'K','L','M','N','O','P','Q','R','S','T','U','V','W',\
'X','Y','Z','a','b','c','d','e','f','g','h','i','j',\
'k','l','m','n','o','p','q','r','s','t','u','v','w',\
'x','y','z','0','1','2','3','4','5','6','7','8','9']
nums = sample(range(0,63),8)
print(nums)
c1 = nums[0]
c2 = nums[1]
c3 = nums[2]
c4 = nums[3]
c5 = nums[4]
c6 = nums[5]
c7 = nums[6]
c8 = nums[7]
print(alpha[c1],alpha[c2],alpha[c3],alpha[c4],alpha[c5],alpha[c6],\
alpha[c7],alpha[c8],end='')
That 63 should be a 62, standard one-off error. Easy fix: nums = sample(range(0, len(alpha)), 8) (or more concisely, nums = sample(range(len(alpha)), 8)).
You’ll achieve your result with an even more concise one-liner:
print(sample(alpha, 8)) that samples your elements directly, forgoing the need for a range.
Checkout the docs of random.sample for more usage tips.
https://docs.python.org/3/library/random.html#functions-for-integers
nums = sample(range(0,63),8)
This means: choose 8 values that are within the range between 0 and 63, not inclusive - which you will then use to index into alpha.
This is needlessly complex. Just sample alpha directly:
letters = sample(alpha, 8)
That's how the method is supposed to work. There is nothing magic about range. It is not part of the language's syntax, and it doesn't cause any function or language construct to behave in a special way. It's just another object. The point of random.sample is that you start with a sequence (something that, conceptually, contains multiple values in a defined order) and you get some of its elements. A range is a sequence. But so is a list, or a string.
By trying to specify the length, you only add complexity and a source of bugs. There is no reason to do any indexing like this.
But to answer the actual questions/concerns raised:
some of the times I'm running the program
Well, yes, of course. You're picking values randomly, and almost all of them are valid.
I double checked and the the numbers that it's saying are out of range are totally in range
No; the numbers that don't cause the problem are in range. If they were out of range, then you wouldn't get to see the value after you tried to index with it, because the problem would happen first.
Unless you checked all the values before using them to index (which your print statement clearly doesn't do), and waited until you had a run of the program where the error occurred, and saw that on that run, all the values were valid, you don't have any evidence that "the values are in range".
You don't know anything about "the numbers that it's saying are out of range" because you don't know which number it's saying is out of range. The reason you don't know this is because the error message doesn't say the value that caused the problem. (This is, in my view, a shortcoming of the Python built-ins; it does show you the analogous information when you try to use a dict and get a KeyError.)
You could potentially find out by using a try block to wrap each attempt at indexing:
try:
print(f'the first letter is {alpha[c1]}')
except IndexError:
print(f'c1 is equal to {c1}, which is not a valid index for alpha')
But as you can imagine, this becomes really tedious really quickly. It's better not to have to hunt down bugs like this, by writing your code in a way that cannot have such bugs in the first place.
Do not use indexes if you have a simple way to avoid them.
And I think the IndexError only pops up for the alpha list (e.g. alpha[c5]) not the actual nums list.
Correct. nums has 8 elements, because of how it was created via random.sample. You get elements 0 through 7 inclusive, which are all valid indices. Those numbers are hard-coded and cannot fail.
The resulting values in c1 etc. are decided randomly. They can randomly end up being invalid.
You can use the len of alpha instead for your range
nums = sample(range(0, len(alpha)), 8)

While-loop - IndexError: list assignment index out of range

I keep getting Index Errors when I try to use while loops in combination with lists.
I wrote the code some time ago, came back to it, but just couldn't wrap my head around it enough to find my mistake. Apparently, the error has something to do with my lists indexes being too small or too large.
indexes = []
#Or indexes[0], but this threw another error
indexes.append(decoded.find(special_string))
x=1
while indexes[x-1] > 0:
total = sum(indexes)
indexes[x] = decoded.find(special_string, total)
x+=1
print(indexes)
My goal is to find all substrings (special_string) in a string and get their indexes (please inform me if you know an easier way to do this). I want to write all indexes out into a list for further use.
I think the only change you need to make is from:
indexes[x] = decoded.find(special_string, total)
to:
indexes.append(decoded.find(special_string, total))
You can't assign indexes[x] since it doesn't exist.

I want to exclude all numbers that aren't divisible by 7 and that are not multiples of 5 in a range of numbers from (0,300)

basically I have to get a list of numbers that are divisible by 7 but not multiples of 5. but for some reason when i put the conditions it tells me i have error.
for i in [x for x in xrange(0,100) if x%7 ==0 and if x%5 !=0 ]:
print i
I know you posted something along the lines of a list comprehension but it's a bit hard to read. So a few things...
I would try writing this as a multi-line for loop before condensing it down to a list comprehension.
I'm not sure why you have an 'x' in here, and 'xrange' doesn't make sense.
Edit: Just realized why I don't recognize xrange and it's because I never worked with Python 2.x
So thinking through this, you are basically looking for any number from 0-300 that is divisible by 7 but is not a multiple of 5.
Means we have a few things...
range(0,301): Since range is not inclusive of our last value we want n+1
Our number, let's say "i" is both... "i%7==0" and "i%5!=0"
So let's look line-by-line
for i in range(0,301):
Okay cool, now you don't need a nested for loop list comprehension like you did in your example. Now, you need to know "if" i is ____... So we need an if statement.
if i%7==0 and i%5!=0:
See the logic? And of course that if statement is inside of our for loop to loop over all the values in our range.
Finally, if our "i" meets our criteria, then we can print all the values.
print(i)
So, our final code looks like...
for i in range(0,301):
if (i % 7 == 0) and (i % 5 != 0):
print(i)
Of course, there are ways you can make this more elegant, but this is the general idea.
List Comprehension:
party = [i for i in range(0,301) if i%7==0 and i%5!=0]
print(party)
That stores them all in a list so you can access them whenever. Or you can print it without assigning it of course.
Edit: The title and then what you say in the body are kind of conflicting. After reading over my own answer, I'm not completely sure if that's what you're looking for, but that is how it came across to me. Hope it helps!
Your list comprehension is incorrect. It should be something similar to:
[x for x in xrange(100) if x%5 and not x%7]
Even better (more efficient) will be something similar to
[x for x in xrange (7, 100, 7) if x%5]
Even better will be ... Nah, we'll just stop here for now.

Python: weird list index out of range error [duplicate]

This question already has answers here:
Strange result when removing item from a list while iterating over it
(8 answers)
Closed 7 years ago.
l = range(100)
for i in l:
print i,
print l.pop(0),
print l.pop(0)
The above python code gives the output quite different from expected. I want to loop over items so that I can skip an item while looping.
Please explain.
Never alter the container you're looping on, because iterators on that container are not going to be informed of your alterations and, as you've noticed, that's quite likely to produce a very different loop and/or an incorrect one. In normal cases, looping on a copy of the container helps, but in your case it's clear that you don't want that, as the container will be empty after 50 legs of the loop and if you then try popping again you'll get an exception.
What's anything BUT clear is, what behavior are you trying to achieve, if any?! Maybe you can express your desires with a while...?
i = 0
while i < len(some_list):
print i,
print some_list.pop(0),
print some_list.pop(0)
I've been bitten before by (someone else's) "clever" code that tries to modify a list while iterating over it. I resolved that I would never do it under any circumstance.
You can use the slice operator mylist[::3] to skip across to every third item in your list.
mylist = [i for i in range(100)]
for i in mylist[::3]:
print(i)
Other points about my example relate to new syntax in python 3.0.
I use a list comprehension to define mylist because it works in Python 3.0 (see below)
print is a function in python 3.0
Python 3.0 range() now behaves like xrange() used to behave, except it works with values of arbitrary size. The latter no longer exists.
The general rule of thumb is that you don't modify a collection/array/list while iterating over it.
Use a secondary list to store the items you want to act upon and execute that logic in a loop after your initial loop.
Use a while loop that checks for the truthfulness of the array:
while array:
value = array.pop(0)
# do some calculation here
And it should do it without any errors or funny behaviour.
Try this. It avoids mutating a thing you're iterating across, which is generally a code smell.
for i in xrange(0, 100, 3):
print i
See xrange.
I guess this is what you want:
l = range(100)
index = 0
for i in l:
print i,
try:
print l.pop(index+1),
print l.pop(index+1)
except IndexError:
pass
index += 1
It is quite handy to code when the number of item to be popped is a run time decision.
But it runs with very a bad efficiency and the code is hard to maintain.
This slice syntax makes a copy of the list and does what you want:
l = range(100)
for i in l[:]:
print i,
print l.pop(0),
print l.pop(0)

how to loop through and get the last value

Hi i have the following code:
m= list()
for i in range (1,6):
set = base.Getentity(constants.ABAQUS,"SET",i)
m.append(set)
print(set)
and my result is
<Entity:0*17a:id:1>
<Entity:0*14g:id:2>
<Entity:0*14f:id:3>
<Entity:0*14a:id:4>
None
None
Here i have four elemnts in my set named set. Even though my code is written in ansa python, my question is very General
I would like to write a code which goes through the set and prints the last elemnt in my case
'<Entity:0*17a:id:4>'.
and aslo i dont want to use the range function so pls help me with writing the code.
I suggest you look at Iterators, that will help you loop through the list
If you don't want to use the range function, you can use xrange. It returns an xrange object, which is kind of like an iterator and generates the numbers on demand.
You are getting None as the last two values because there are no 'set' with the id 5 and 6 in your model
Use a filter before appending to the list m
m= list()
for i in range (1,6)
set = base.Getentity(constants.ABAQUS,"SET",i)
if set!=None:
m.append(set)
Now you can just call m[-1] for the last entity
hope this helps

Categories