Search code examples
pythonotree

Index error in code to show image random without repetion


I am trying to display a series of images randomly but I want to avoid repeating the images.

The code below works on startup but there comes a time when the following error simply appears:

pop index out of range

sufijos_png = list(range(1, 10+1))

def vars_for_template(self):
    n_img = random.choice(sufijos_png)
    self.player.n_loteria = n_img
    sufijos_png.pop(n_img-1)
    return dict(
        image_path='dict/G{}.png'.format(n_img)
    )´

Anyone have any idea how to fix this error?

Solution

  • .pop() actually returns and removes the last element of the list sufijos_png, so with the logic above, it's possible that at some point it will try to remove the element at index n_img-1 from the list sufijos_png, which doesn't exist anymore. To demonstrate, let's take the example you gave:

    sufijos_png = list(range(1, 10+1))
    sufijos_png
    
    [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
    

    Notice that len(sufijos_png) = 10. Now, the first time we randomly choose a value from sufijos_png. Let's say that value is 9, so

    n_img = random.choice(sufijos_png)
    n_img
    9
    

    Then we pop (+ return) the value at position n_img-1 = 9-1 = 8 of sufijos_png.

    sufijos_png.pop(n_img-1) # pops value at index position 8
    sufijos_png
    [1, 2, 3, 4, 5, 6, 7, 8, 10]
    

    Now, imagine the next random draw from sufijos_png is equal to 10 (it's one of the values still remaining there). However, the length of sufijos_png is now 9, so the index range is 0-8. Therefore the following will raise an IndexError:

    n_img = random.choice(sufijos_png)
    n_img
    10 # one of the remaining possible values in sufijos_png
    
    sufijos_png.pop(n_img-1) # pops value at index position 10, which doesn't exist
    
    IndexError: pop index out of range
    

    One way to overcome this problem, assuming that you just need a random number that doesn't repeat itself to assign to self.player.n_loteria = n_img, is to generate a list of digits/values, then shuffle them, and then keep popping from this randomly ordered list. For example:

    import random
    sufijos_png = list(range(1, 10+1))
    random.shuffle(sufijos_png) # just need to shuffle once
    
    def vars_for_template(self):
        n_img = sufijos_png.pop() # every time you accessing, you're depleting the list
        self.player.n_loteria = n_img
        return dict(
            image_path='dict/G{}.png'.format(n_img)
        )