RT Security
RT Security

Reputation: 47

StopIteration keeps showing randomly when running code

I'm very new to Python and I'm trying to put together a small program which does seem to work, however I get the below error every so often when running the code.

The end goal is slightly more complex than the current code, however the idea is the program reads from 2 separate CSV files, picks a random row from each, then concatenates specific values from those rows and prints the result.

Error Seen:

    Traceback (most recent call last):
  File ".\SimulationScript.py", line 57, in <module>
    counter(10)
  File ".\SimulationScript.py", line 45, in counter
    indicatorDefinition(indicator_file)
  File ".\SimulationScript.py", line 17, in indicatorDefinition
    chosen_row = next(row for row_number, row in enumerate(reader)
StopIteration

Code:

from sys import argv
import random
import csv

script, file1, file2 = argv
f1 = ''
f2 = ''

def iDefinition(i):
    with open(i) as file:
        lines = sum(1 for line in file)
        line_number = random.randrange(lines)

    with open(i) as file:
        reader = csv.DictReader(file)
        chosen_row = next(row for row_number, row in enumerate(reader)
            if row_number == line_number)

        global f1
        f1 = chosen_row['field_1']+'":"'+chosen_row['value_1']+'"'

def nDefinition(n):
    with open(n) as file:
        lines = sum(1 for line in file)
        line_number = random.randrange(lines)

    with open(n) as file:
        reader = csv.DictReader(file)
        chosen_row = next(row for row_number, row in enumerate(reader)
            if row_number == line_number)

        global f2
        f2 = '"code":"'+chosen_row['Node code']+'","'



def counter():
    count = 0

    while count < 6:
        nDefinition(file2)
        iDefinition(file1)
        print(f2+f1)

        count += 1

counter()

Upvotes: 2

Views: 1771

Answers (3)

Jon Clements
Jon Clements

Reputation: 142216

As mentioned - StopIteration gets raised when attempting to consume an element from an exhausted iterable.

it = iter([1, 2])
next(it)
# 1
next(it)
# 2
next(it)
# raises StopIteration as nothing to return
# however you can use the default argument to return a default value when exhausted
# next(it, 'cabbage!')
# cabbage!

However, since you only want to be taking a random line from a file, instead of counting the lines and picking a line number, then scanning the file and extracting that line, you can use a min with a random key and do it in one pass, eg:

import heapq, random

with open('your_file') as fin:
    random_line = min(fin, key=lambda L: random.random())

If you want to take more than 1, then you can use heapq as such:

with open('your_file') as fin:
    random_50 = heapq.nlargest(50, fin, key=lambda L: random.random())

If the files aren't going to blow up system memory and you're going to be wanting to take a random line a lot, then you can load them into a list and then use random.choice, eg:

with open('your_file') as fin:
    data = list(fin)

r1 = random.choice(data)
r2 = random.choice(data)
# ...

Upvotes: 2

TheKvist
TheKvist

Reputation: 763

As taken from https://docs.python.org/3/glossary.html#term-iterator:

When no more data are available a StopIteration exception is raised instead. At this point, the iterator object is exhausted and any further calls to its __next__() method just raise StopIteration again.

enumerate() returns an Iterator object, so the above mentioned is the case. Presumably your script has reached the last row in the CSV and your call to next() requires the Iterator to return another one (which is, of course, not possible).

As this exception is expected, you may want to surround the expression in a try-except block, catch the StopIteration exception and then continue with execution of your method.

Another option would be to use a for-loop, which simply stops after the last element is iterated over. next() calls are usually only used when you need to control when the next element is to be fetched but not if you want to iterate over everything in one go.

Upvotes: 0

Bernhard
Bernhard

Reputation: 1273

StopIterationError gets thrown by next when the iterable object is completely iterated over. This should not happen randomly but everytime you run the code after he the iteration ran through the whole iterable object.

Either you make a try/except block for it or you use a for loop instead of next. (The for loop in python automatically catches and "ignores" the stopiteration error after the loop is done).

    row for row_number, row in enumerate(reader):
        if row_number == line_number:
            chosen_row = row

Upvotes: 0

Related Questions