Reputation: 47
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
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
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
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