0

I'm currently doing the logic testing of my assignment so my code still needs some refactoring and removing of none needed globals and imports but I'm getting a randomly occurring error.

The error will occur randomly, sometimes it might not happen for 40 iterations, other times it will happen on the first one. It's completely random.

the error occurs on the line where i calculate the answer to the sum beforehand and store it.

answer = operator(times, toTimesBy)

I get an error saying the variable operator is referenced before assignment although this error only pops up randomly.

UnboundLocalError: local variable 'operator' referenced before assignment

#import required additional libraries
import random
import operator
import sys


global times
global toTimesBy
global answer
global ops
global stringOperator
global questionCounter


#dictionary to hold operators which allow assignment to variables
ops = {"+": operator.add,
       "-": operator.sub,
       "*": operator.mul}

questionCounter = 1
print(questionCounter)

def generate(questionCounter):
    #set question counter
    questionCounter = questionCounter + 1
    #generate questions
    times = random.randint(1, 12)
    toTimesBy = random.randint(1, 12)
    #randomly select the operator
    operatorSelector = random.randint(1, 100)

    if operatorSelector in range(0, 32):
        operator = ops["+"]
        stringOperator = "+"
    elif operatorSelector in range (33, 65):
        operator = ops["-"]
        stringOperator = "-"
    elif operatorSelector in range(66, 100):
        operator = ops["*"]
        stringOperator = "*"

    #to work out answer
    answer = operator(times, toTimesBy)
    print(answer)
    #print which question the user is answering
    print('question ' + str(questionCounter) + ' is:')
    #print the question
    print(str(times) + stringOperator + str(toTimesBy))
    #collect input
    userInput = input('What is your answer? ')
    #check if right
    if userInput == str(answer):
        print('correct')
        generate(questionCounter)
        #check if wrong
    else:
        print('wrong')
        generate(questionCounter)


generate(questionCounter)

2 Answers2

1

The upper limit of most Python functions is non-inclusive (but in random.randint the upper limit is inclusive). range(0, 32) is an iterable from 0 to 31, range(33, 65) is an iterable from 33 to 64 and so on. When operatorSelector is 32, 65 or 100 no branch of if is executed.

Also a check like in range(1, 31) in Python 2 may have to perform up to 30 comparisons of operatorSelector with numbers.

You should just use operators comparison operators for that:

if 0 <= operatorSelector <= 32:
    operator = ops["+"]
    stringOperator = "+"
elif 33 <= operatorSelector <= 65:
    operator = ops["-"]
    stringOperator = "-"
elif 66 <= operatorSelector <= 100:
    operator = ops["*"]
    stringOperator = "*"
Kolmar
  • 14,086
  • 1
  • 22
  • 25
  • This is the right answer. I'm commenting only to point out that in Python 3, the `in` operator on a `range` object is more efficient than it was in Python 2, so it won't actually need to check lots of values (`x in range(start, stop, step)` is tested with something similar to `start <= x < stop and (start-x) % step == 0`, though with some extra logic to support negative `step` values). – Blckknght Jan 30 '15 at 19:47
0

Your error is that your if/elif functions don't cover the entire range of possible values. range is not inclusive of the endpoint.

if operatorSelector in range(0, 32):

This doesn't cover 32.

elif operatorSelector in range (33, 65):

This doesn't cover 65.

elif operatorSelector in range(66, 100):

This doesn't cover 100.

When 32, 65 or 100 are selected, the lookup for which operator to utilize isn't performed.


There are a few options for solving this:

  • Option 1 is to set a default before your if/elif and realize that you will have slight bias toward this particular operator (in the case below, "+")

Example:

operator = ops["+"]
stringOperator = "+"
...
if operatorSelector in range(33, 65):
    operator = ops["-"]
    stringOperator = "-"
  • Option 2 is to expand your ranges to include the missing numerals:

Example:

if operatorSelector in range(0, 33):
    ...
elif operatorSelector in range (33, 66):
    ...
elif operatorSelector in range(66, 100):
    ...

Again, since range isn't inclusive of the end point, there is no overlap here.

Community
  • 1
  • 1
Andy
  • 49,085
  • 60
  • 166
  • 233