python variable NameError - python

I am having trouble with assigning values to vars and then accessing the values. For example:
# offer users choice for how large of a song list they want to create
# in order to determine (roughly) how many songs to copy
print "\nHow much space should the random song list occupy?\n"
print "1. 100Mb"
print "2. 250Mb\n"
tSizeAns = raw_input()
if tSizeAns == 1:
tSize = "100Mb"
elif tSizeAns == 2:
tSize = "250Mb"
else:
tSize = 100Mb # in case user fails to enter either a 1 or 2
print "\nYou want to create a random song list that is " + tSize + "."
Traceback returns:
Traceback (most recent call last):
File "./ranSongList.py", line 87, in <module>
print "\nYou want to create a random song list that is " + tSize + "."
NameError: name 'tSize' is not defined
I have read up on python variables and they do not need to be declared so I am thinking they can be created and used on the fly, no? If so I am not quite sure what the traceback is trying to tell me.
By the way, it appears as though python does not offer 'case' capabilities, so if anyone has any suggestions how to better offer users lists from which to choose options and assign var values I would appreciate reading them. Eventually when time allows I will learn Tkinter and port to GUI.

Your if statements are checking for int values. raw_input returns a string. Change the following line:
tSizeAns = raw_input()
to
tSizeAns = int(raw_input())

This should do it:
#!/usr/local/cpython-2.7/bin/python
# offer users choice for how large of a song list they want to create
# in order to determine (roughly) how many songs to copy
print "\nHow much space should the random song list occupy?\n"
print "1. 100Mb"
print "2. 250Mb\n"
tSizeAns = int(raw_input())
if tSizeAns == 1:
tSize = "100Mb"
elif tSizeAns == 2:
tSize = "250Mb"
else:
tSize = "100Mb" # in case user fails to enter either a 1 or 2
print "\nYou want to create a random song list that is {}.".format(tSize)
BTW, in case you're open to moving to Python 3.x, the differences are slight:
#!/usr/local/cpython-3.3/bin/python
# offer users choice for how large of a song list they want to create
# in order to determine (roughly) how many songs to copy
print("\nHow much space should the random song list occupy?\n")
print("1. 100Mb")
print("2. 250Mb\n")
tSizeAns = int(input())
if tSizeAns == 1:
tSize = "100Mb"
elif tSizeAns == 2:
tSize = "250Mb"
else:
tSize = "100Mb" # in case user fails to enter either a 1 or 2
print("\nYou want to create a random song list that is {}.".format(tSize))
HTH

In addition to the missing quotes around 100Mb in the last else, you also want to quote the constants in your if-statements if tSizeAns == "1":, because raw_input returns a string, which in comparison with an integer will always return false.
However the missing quotes are not the reason for the particular error message, because it would result in an syntax error before execution. Please check your posted code. I cannot reproduce the error message.
Also if ... elif ... else in the way you use it is basically equivalent to a case or switch in other languages and is neither less readable nor much longer. It is fine to use here. One other way that might be a good idea to use if you just want to assign a value based on another value is a dictionary lookup:
tSize = {"1": "100Mb", "2": "200Mb"}[tSizeAns]
This however does only work as long as tSizeAns is guaranteed to be in the range of tSize. Otherwise you would have to either catch the KeyError exception or use a defaultdict:
lookup = {"1": "100Mb", "2": "200Mb"}
try:
tSize = lookup[tSizeAns]
except KeyError:
tSize = "100Mb"
or
from collections import defaultdict
[...]
lookup = defaultdict(lambda: "100Mb", {"1": "100Mb", "2": "200Mb"})
tSize = lookup[tSizeAns]
In your case I think these methods are not justified for two values. However you could use the dictionary to construct the initial output at the same time.

Initialize tSize to
tSize = ""
before your if block to be safe. Also in your else case, put tSize in quotes so it is a string not an int. Also also you are comparing strings to ints.

I would approach it like this:
sizes = [100, 250]
print "How much space should the random song list occupy?"
print '\n'.join("{0}. {1}Mb".format(n, s)
for n, s in enumerate(sizes, 1)) # present choices
choice = int(raw_input("Enter choice:")) # throws error if not int
size = sizes[0] # safe starting choice
if choice in range(2, len(sizes) + 1):
size = sizes[choice - 1] # note index offset from choice
print "You want to create a random song list that is {0}Mb.".format(size)
You could also loop until you get an acceptable answer and cover yourself in case of error:
choice = 0
while choice not in range(1, len(sizes) + 1): # loop
try: # guard against error
choice = int(raw_input(...))
except ValueError: # couldn't make an int
print "Please enter a number"
choice = 0
size = sizes[choice - 1] # now definitely valid

You forgot a few quotations:
# offer users choice for how large of a song list they want to create
# in order to determine (roughly) how many songs to copy
print "\nHow much space should the random song list occupy?\n"
print "1. 100Mb"
print "2. 250Mb\n"
tSizeAns = raw_input()
if tSizeAns == "1":
tSize = "100Mb"
elif tSizeAns == "2":
tSize = "250Mb"
else:
tSize = "100Mb" # in case user fails to enter either a 1 or 2
print "\nYou want to create a random song list that is " + tSize + "."

Related

It wont print out the code at the bottom on the program

For some reason the print command at the bottom is not printing out it is just looping no matter how I put indentation as well.
result_list = []
print("Welcome to Speed Cameras")
while 1:
result_list.append(input("New Reading: "))
if result_list == "END":
break
try:
max_speed = max(result_list)
min_speed = min(result_list)
avg_speed = len(result_list) / len(result_list)
print("Max is:", max, " MPH:")
print("Min is:", min, " MPH")
print("Avg is", avg_speed, "MPH")
finally:
print("Thanks For Submitting")
You have 2 issues and both are here
if result_list == "END":
break
result_list is a list so it will never be equal to a string, instead you could check if the last item is END like this result_list[-1] == "END"
the second problem you have is indentation, your break is not in the if statement but in the while loop, but this doesn't seem to be the case with your error, so i think you copied your code into the question with an error
Here is the code would work:
result_list = []
print("Welcome to Speed Cameras")
while 1:
# you need test the input first before append it to the list and not test the list
inp = input("New Reading: ")
if inp == "END":
break
else:
result_list.append(float(inp)) # turn your input into a number before appending
try:
max_speed = max(result_list)
min_speed = min(result_list)
# len(result_list) / len(result_list) make no sense, use sum()
avg_speed = sum(result_list) / len(result_list)
print("Max is:", max_speed, " MPH:") # your variable name was wrong here
print("Min is:", min_speed, " MPH") # your variable name was wrong here
print("Avg is", avg_speed, "MPH")
# since you try, you should do something when you don't pass the try
# 1 there is no input at all
# 2 the input need to be numbers
except:
print('at least one speed input is needed, or please make sure your input to be a number')
finally:
print("Thanks For Submitting")
A few mistakes:
Test the input instead of the list, and then append it to the list
Need to turn input into number
Average speed formula need to be fixed
Don't forget except in try-except-finally
Wrong variable names inside print()
Please reading your own code a few time before post it for help. Some mistakes like variable names and avg-speed formula, are easy to identify.
Anyway hope this would help.

Trying to locate syntax error in python while loop

very very new beginner here - just started learning today! stumped on what the syntax error is here:
import random
x = random.randrange(7)
user_start = "yes"
user_start_input = str(input("type 'yes' to generate random number. "))
while user_start_input == user_input:
print("your random dice number is " + str(x))
user_start_input = input("roll again?")
if user_start_input != user_input:
break
print("done")
The error message is:
File "/Users/joel/Documents/Learning Python/Dice.py", line 12
while user_start_input == user_input:
^
SyntaxError: invalid syntax
what am I doing wrong?
First off we're (those that wish to answer) missing some information, while is on line 5 where as the error is being reported with while on line 12, there's plenty that could be causing an error to pop on a following line; eg. missing quote. Looks like G. Anderson already eluded to that last point, as far as errors usually being from a preceding line. My suggestion in this case would be to find an developer friendly text editor (IDE) that'll point out minor typos through syntax-highlighting; Atom is pretty groovy, especially with a few addons, but there's plenty of other text editors to play with.
Second, as commented by CoffeeTableEspresso the tabs are non-existent in your code snip! If your source code looks identical to what has been posted, then your bug-stomping has only just begun.
Third, because ya had stated that Python is not your first language it might be helpful, if not now then certainly in the future, to know of __doc__ strings, eg...
>>> print(random.randrange.__doc__)
Choose a random item from range(start, stop[, step]).
This fixes the problem with randint() which includes the
endpoint; in Python this is usually not what you want.
... many of the things within Python are documented and accessible via the __doc__ method, which can also be accessed with help(), eg. help(random.randrange), and it is possible to write your own with the following syntax...
def test_func(arg):
"""
This is a __doc__ string
"""
print("arg -> {0}".format(arg))
And finally, well for now, it's a good idea when writing in an unfamiliar language to use copious comments and split things up into smaller bits that express your intentions; for example...
#!/usr/bin/env python
import random
def dice(sides = 6):
"""
Returns random int between `1` and `sides`
"""
return random.randrange(start = 1, stop = int(sides) + 1, step = 1)
def prompt(message, expected):
"""
Returns `True` if user input matches `expected`
"""
return expected == str(input("{0} ".format(message)))
def main_loop():
"""
Returns list of `dice(...)` results, list length depends
upon number of times `prompt(...)` returns `True`
"""
roll_results = []
user_start = 'yes'
# Set message for first run of loop
message = "Type '{0}' to roll the dice".format(user_start)
while prompt(message = message, expected = user_start):
# Save dice output to variable for later use and
# append to list of rolls that will be returned
roll = dice(sides = 6)
roll_results.append(roll)
# For now just print each roll, but this is one
# aria to expand upon with your own edits
print("Rolled {0}".format(roll))
# Set new message line for following loop iterations
message = 'Roll again?'
return roll_results
# Do stuff if script is run directly instead of imported as a module
if __name__ == '__main__':
main_loop()
P.S. keep at it, eventually all the learnings'll start to click and the following RP related example classes will make more since...
#!/usr/bin/env python
from __future__ import range
import random
class DiceBag(dict):
"""
DiceBag is a collection of short-cuts to `random.randrange`.
- `selection`, list of `n` sided dice, eg `[4, 20]` would _stock_ bag with d4 and d20
"""
def __init__(self, selection = [2, 4, 20], **kwargs):
super(DiceBag, self).__init__(**kwargs)
self.update(selection = selection)
def dice(self, sides = 6):
"""
Returns random int between `1` and `sides`
"""
return random.randrange(start = 1, stop = int(sides) + 1, step = 1)
def handfull_of(self, dice = {}):
"""
Returns `dict` with lists of dice rolls
## Example
dice_bag = DiceBag()
toss_results = dice_bag.handfull_of({20: 1, 4: 2})
Should return results of one `d20` and two `d4` such as
{
20: [18],
4: [1, 3]
}
"""
output = {}
for sides, count in dice.items():
if sides not in self['selection']:
continue
rolls = []
for roll in range(count):
rolls.append(self.dice(sides))
output[sides] = rolls
if not output:
raise ValueError("No dice in bag matching sizes -> {0}".format(dice.keys()))
return output
"""
Short cuts for dice of a `n` sides, expand upon it if you wish
"""
#property
def coin(self):
return self.dice(sides = 1)
#property
def d4(self):
return self.dice(sides = 4)
#property
def d6(self):
return self.dice(sides = 6)
class Flail(DiceBag):
def __init__(self, damage_modifier = 0, damage_dice = {'sides': 6, 'count': 2}, **kwargs):
super(Flail, self).__init__(selection = [damage_dice['sides'], 20], **kwargs)
self.update(damage_modifier = damage_modifier)
self.update(damage_dice = damage_dice)
def attack(self, attack_modifier = 0):
"""
Returns `dict` with `hit` chance + `attack_modifier`
and `damage` rolls + `self['damage_modifier']`
"""
rolls = self.handfull_of(dice = {
20: 1,
self['damage_dice']['sides']: self['damage_dice']['count']
})
return {
'hit': rolls[20][0] + attack_modifier,
'damage': sum(rolls[self['damage_dice']['sides']]) + self['damage_modifier']
}
Updates
Here's what your code block may look like with proper indentation...
import random
x = random.randrange(7)
user_start = "yes"
user_start_input = input("type 'yes' to generate random number. ")
while user_start_input == user_input:
print("your random dice number is " + str(x))
user_start_input = input("roll again?")
print("done")
... and here's what a working version might look like...
import random
message = "type 'yes' to generate random number. "
expected = "yes"
while input(message) == expected:
x = random.randrange(7)
print("your random dice number is {num}".format(num = x))
message = "roll again? "
print("done")
... there's little reason to use an if something break when using while to do the same kinda thing, well given the current question's code sample.
Moving the assignment of x to be within the loop ensures that there's a chance of a new number on each iteration, while not stated I've a feeling that that was your intent.
Using input(message) and updating the message displayed instead hopefully makes sense. Though I'm not sure why you where wrapping things within str(), didn't seem to make a bit of difference when I tested.
First, it seems like you've mixed up the two variable names user_start and user_input, so those need to be changed to the same variable name.
Next, Python structures code with indentation: so the content in while loops and the like would need to be indented.
So here, you would indent all the code inside the while loop, and further indent the code inside the if statement inside the while loop.
It also seems like the purpose of your code is to simulate a dice roll each time the while loop runs again. In the while loop, you call on the variable x for the dice roll, but x is never changed. You never changed x to be a different random number, so it will just show the same random number every time the user rolls the dice again.
To fix this, simply re-define x each time the while loop is run. So just move the definition of the variable x to within the while loop.
With all these fixes, the code works:
import random
user_start = "yes"
user_start_input = str(input("type 'yes' to generate random number. "))
while user_start_input == user_start:
x = random.randrange(7)
print("your random dice number is " + str(x))
user_start_input = input("roll again?")
if user_start_input != user_start:
break
print("done")
Of course the variable names could be a bit more informative, and the code could be structured better to improve performance and user friendliness, but overall, great job for a beginner!

Python: Issue with Elif Break

I'm trying to make a simple program that will take all of your lottery numbers, and compare them (using set intersect) with the winning numbers that you input.
I've gotten the groundwork laid where you enter your numbers, it gets submitted to a sublist, which will then be converted into five separate sets, which will be used to compare. However, when you run the script, the while loop will not break when the length of the list is 5 (this is the goal).
Can someone explain what I'm doing wrong? Or maybe even a better way of working this whole program. I'm relatively new to the world of Python, I'm just diving in, and trying to make this program work.
# Start Program
def set_convert(list):
conversion = set(list)
return conversion
def comparison(winning_numbers, my_numbers):
pass
def main():
print('Welcome to the Lottery Checker v1.0!')
winning_numbers = [int(x) for x in input('Enter the winning numbers(Sep w/ Spaces): ').split()]
winning_set = set_convert(winning_numbers)
my_numbers = []
while True:
numbers = [int(x) for x in input('Enter your numbers(Sep w/ Spaces Max: 5): ').split()]
if len(numbers) == 6:
my_numbers.append(numbers)
print('Added! Want to add more?')
elif len(my_numbers) == 5:
break
else:
pass
else:
pass
print('Here are your numbers: {}. Good luck! :-)'.format(my_numbers))
main()
Replace
elif len(my_numbers) == 5:
with
elif len(numbers) == 5:
Also, it is advisable that you don't use the keyword list as an argument for the function set_convert. Rather, define it as:
def set_convert(mylist):
conversion = set(mylist)
return conversion
And finally, you don't need to pass in my_numbers and winning_numbers into the function comparison as arguments since they are available in the outer scope.

changing data from printing line by line to a list (in a text file)

I'm using python, idle version 3.4.2
My code when it prints, print 3 scores per name but they print like this:
Maya: 3
Maya:2
Maya: 4
I was wondering if there was any way to get it to print like this in a list/dictionary so i can call upon the scores later for sorting
Maya = [3,2,4]
Here is my code.
students_names = []
import random #import module
print("What is your name?") #prints writing in brackets
name = input().title() #Capitalizes the first letter of the word inputted
students_names.append(name)
print("What class are you in? (Enter 1, 2 or 3)") #asks the user to input a number
while True:
try:
class_number = int(input()) #asks for an integer input from user
except ValueError:
print("Sorry, I didn't understand that, please try again") #print statement
continue
if class_number > 3: #if input is more than 3
print("SORRY but that class isn't recognised, try again") #print statement
continue
else:
print ("Hello,", name, "from class", class_number, "welcome to my quiz") #prints writing in brackets and anything saved in the variable "name" and "class_number"
break #break out of loop
score = 0 #sets the variable "score" to zero
question = 0 # sets the variable "question" to zero
print(class_number)
while question < 3:#If questions (intitally set to 0) is smaller than 10, carry out this function
question +=1 # add one to the value of "question"
maths = random.randint(1,3) #randomly generate a number from 1-3 and store as "maths"
num1 = random.randint(1,10)#randomly generate an integer from 1-10 and store as "num1"
num2 = random.randint(1,10)#randomly generate a second integer from 1-10 and store as "num2"
if maths == 1: #if the number generated is 1
print(num1, "+", num2) #prints num1 + num2
ans = num1 + num2 #sets "ans" to equal the value of num1 added to num2
elif maths == 2: #if the number generated is 1
print(num1, "*", num2) #print num1 multiplied by num2
ans = num1 * num2 #sets "ans" to equal the value of num1 multiplied by num2
else: #else run this part of code
print(num1, "-", num2) #print num1 subtracted by num2
ans = num1 - num2 #sets "ans" to equal the value of num1 subtracted by num2
while True:
try:
user_ans = int(input()) #user inputs answer to question
except ValueError: #runs when the user input is no an integer
print ("SORRY but that answer isn't recognised, try again")
else:
break
if user_ans == ans:
score+=1
print("Well done, you are CORRECT")
else:
print("SORRY, you are INCORRECT") #print writing in brackets
print("The correct answer was", ans)
if score == 10: #run this part of code if "score" equals 10
print("fantastic", name, "you got full marks!")#print statement and name
elif score >= 6: #run this part of code if "score" is larger than or equal to 6
print("well done, there's some improvement to be done here though", name, "you got", score, "/10")# then print statement and score
elif score <=5: #run this part of code if "score" is smaller than or equal to 5
print("hmm, maybe some more practise would be beneficial", name, "you got", score, "/10") #then print statement and score
class_number = str(class_number) + ".txt" #this adds '.txt' to the end of the file (therefore creating a text file) so it can be used to create a file under the name of the class
file = open(class_number, 'a') #opens the file in 'append' mode so you don't delete all the information
file.write(str(students_names))
file.write(str(name + " : ")) #writes the name and ":" to file
file.write(str(score)) #writes the score to file
file.write('\n')#writes the score to the file
file.close()#safely closes the file to save the information
Sure simply add the score values foreach person to a new dictionary.
name_dict = {}
if name in name_dict:
name_dict[name].append(new_score)
else:
name_dict[name] = [new_score]
It is a little bit hard to follow up on your code. Therefore I was not completely able to integrate it.
Ok, I think I get what you're looking for. But to simplify my answer to that, we first need to simplify your example. A good way to do so, is to cut your code into functions:
How to store your results?
First we'll look at where you need help on, which is the last bit of your code, which fits in a function we'll call store_results():
def store_results(...):
class_number = str(class_number) + ".txt" # this adds '.txt' to the end of the file (therefore creating a text file) so it can be used to create a file under the name of the class
file = open(class_number, 'a') # opens the file in 'append' mode so you don't delete all the information
file.write(str(students_names))
file.write(str(name + " : ")) # writes the name and ":" to file
file.write(str(score)) # writes the score to file
file.write('\n') # writes the score to the file
file.close() # safely closes the file to save the information
I used here ... for the arguments of the function, which is not python, to have a thought about it. What arguments does that function needs to work as is?
class_number: the class the student answering the QA is in
name: the score the student had on the QA
student_names
What you're doing here, is that when you run that code, you're appending name
into student_names, which will always contain one, and only one name. So when
you write file.write(str(students_names)), for a user whose name is John, you'll
end up writing:
...
['John']
John : 5
...
Which is not something I believe you're expecting. You actually do not need that list at all.
So as a first update, you should do:
def store_results(class_number, name, score):
class_file = "{}.txt".format(class_number) # this adds '.txt' to the end of the file (therefore creating a text file) so it can be used to create a file under the name of the class
with open(class_file, 'a') as f: # opens the file in 'append' mode so you don't delete all the information
f.write("{}: {}\n".format(name, score) # outputs the user's score to the file
Other changes in that snippet are:
- you shall not use file as a variable as it already exists as a global type, and using it in
your code is considered bad practice called "shadowing" which can lead to hard to find bugs
(not in this case, but if you don't take the good habits now, it's bite you in a future code).
- you shall use with open() as f: construct instead of opening and closing the file manually,
because in your existing code, if there's an exception thrown between open and close your
file won't be properly closed, ending up in possible lost writes.
- you shall use format strings instead of direct string concatenation, which makes the code
easier to read and to modify.
Now, that being said, you'll still have your issue having the result of each test taken
by an user being shown as:
John: 2
John: 4
John: 5
The right way to solve this is to not use "blinded" appending to the file, but instead
use the file as some sort of very basic database. The most simple option would be to
write your scores within a JSON file, which is syntactically compatible with python
(pretty useful for manual testing):
import os
import json
def store_results(class_number, name, score):
# this adds '.json' to the end of the file (therefore creating a json file)
class_file = "{}.json".format(class_number)
# first step: load the existing data
# first test if the file exists, and if not, use an empty score dictionary
if not os.path.exists(class_file):
scores = {}
# otherwise populate the dictionary
else:
# open as read only, because we just want the data out of it
with open(class_file, 'r') as f:
# load contents of the json file "f" as object "scores"
scores = json.load(f)
# second step: update the data
scores.setdefault(name, []).append(score)
# third step: update the file
# use 'w' when opening the file to overwrite all data within it
with open(class_file, 'w') as f:
# dump object "scores" within file "f"
json.dump(scores, f)
Within this code, I used a trick to do insertion of the score in one line:
scores.setdefault(name, []).append(score)
which is equivalent to:
# if the user's "name" is not already in the dictionary
if name not in scores.keys():
# add it as a new key containing an empty list
scores[name] = []
# append the "score" to the list indexed by the user's "name"
scores[name].append(score)
Then the results within a file would look like:
{"John":[3,5,10],"Jane":[2],"Bob":[1,0,2]}
To make the output file more readable you can change the json.dump line into:
json.dump(scores, f, sort_keys=True, indent=4, separators=(',', ': '))
which will output:
{
"Bob": [
1,
0,
2
],
"Jane": [
2
],
"John": [
3,
5,
10
]
}
How to run the QA?
Finally you want to create another function that handles the QA process, within which
you call the function we just defined
# add the following import at the start of your script if you're running python2 and not python3
from __future__ import print_function
def run_qa():
print("What is your name?") # prints writing in brackets
name = input().title() # Capitalizes the first letter of the word inputted
print("What class are you in? (Enter 1, 2 or 3)") # asks the user to input a number
while True:
try:
class_number = int(input()) # asks for an integer input from user
except ValueError:
print("Sorry, I didn't understand that, please try again") # print statement
continue
if class_number > 3: # if input is more than 3
print("SORRY but that class isn't recognised, try again") # print statement
continue
else:
break # break out of loop
# prints writing in brackets and anything saved in the variables "name" and "class_number"
print ("Hello, {} from class {} welcome to my quiz".format(name, class_number))
score = 0 # sets the variable "score" to zero
question = 0 # sets the variable "question" to zero
while question < 3: # If questions (initially set to 0) is smaller than 10, carry out this function
question += 1 # add one to the value of "question"
maths = random.randint(1,3) # randomly generate a number from 1-3 and store as "maths"
num1 = random.randint(1,10) # randomly generate an integer from 1-10 and store as "num1"
num2 = random.randint(1,10) # randomly generate a second integer from 1-10 and store as "num2"
if maths == 1: # if the number generated is 1
print("{} + {} = ?".format(num1, num2)) # prints num1 + num2
ans = num1 + num2 # sets "ans" to equal the value of num1 added to num2
elif maths == 2: # if the number generated is 1
print("{} * {} = ?".format(num1, num2)) # print num1 multiplied by num2
ans = num1 * num2 # sets "ans" to equal the value of num1 multiplied by num2
else: # else run this part of code
print("{} - {} = ?".format(num1, num2)) # print num1 subtracted by num2
ans = num1 - num2 # sets "ans" to equal the value of num1 subtracted by num2
while True:
try:
# print a nice little prompt for the user to enter his answer
print("> ", end="")
user_ans = int(input()) # user inputs answer to question
# if an exception is raised by "int()" the break is not being called
# here you achieve the same as a single line, as you're doing in two
# lines with your try/except/else clause, making your code more readable.
break
except ValueError: # runs when the user input is no an integer
print ("SORRY but that answer isn't recognised, try again")
if user_ans == ans:
score += 1
print("Well done, you are CORRECT")
else:
print("SORRY, you are INCORRECT") # print writing in brackets
print("The correct answer was {}".format(ans))
if score == 10: # run this part of code if "score" equals 10
print("Fantastic {}, you got full marks!".format(name))# print statement and name
elif score >= 6: # run this part of code if "score" is larger than or equal to 6
print("Well done, there's some improvement to be done here, though {} you got {}/10".format(name, score))# then print statement and score
elif score <=5: # run this part of code if "score" is smaller than or equal to 5
print("hmm, maybe some more practice would be beneficial, {}, you got {}/10".format(name, score)) # then print statement and score
# return the results
return class_number, name, score
Finally, to call your code, you just need to add the following at the end of your script:
if __name__ == "__main__":
class_number, name, score = run_qa()
store_results(class_number, name, score):
the reason for the if statement is to make it possible to later include your code as
a module in another module without running it, which is considered good practice!
as another improvement I'd avoid repeating the same code several times to check input
from the user, but use a function instead:
def get_integer_input(maxval=None):
while True:
try:
# print a nice prompt
print("> ", eol="")
i = int(input()) # asks for an integer input from user
if maxval:
if i >= maxval:
continue
print("Sorry, input shall be inferior than {}, try again".format(maxval))
break
except ValueError:
print("Sorry, I didn't understand that, please try again") # print statement
return i
Then in your code you use it that way:
...
class_number = get_integer_input(maxval=3)
...
BTW, as an improvement, I'd actually split the run_qa in two functions: one that
generates the questions, and the other that does the interactive process.
so that you would do:
if __name__ == "__main__":
questions = generate_qa()
class_number, name, score = run_qa(questions)
store_results(class_number, name, score)
with generate_qa() being something like:
def generate_qa():
questions = []
while len(questions) < 3: # add a question until we have three generated
op = random.randint(0,2) # randomly generate a number from 1-3 and store as "maths"
num1 = random.randint(1,10) # randomly generate an integer from 1-10 and store as "num1"
num2 = random.randint(1,10) # randomly generate a second integer from 1-10 and store as "num2"
questions.append( (op, num1, num2) )
return questions
and then:
def run_qa(questions):
print("What is your name?") # prints writing in brackets
name = input().title() # Capitalizes the first letter of the word inputted
print("What class are you in? (Enter 1, 2 or 3)") # asks the user to input a number
class_number = get_integer_input(maxval=3)
# prints writing in brackets and anything saved in the variables "name" and "class_number"
print ("Hello, {} from class {} welcome to my quiz".format(name, class_number))
score = 0 # sets the variable "score" to zero
for op, left, right in questions:
if op == 0: # if the number generated is 0
op("{} + {} = ?".format(left, right)) # prints 'left' + 'right'
ans = left + right # sets "ans" to equal the value of num1 added to 'right'
elif op == 1: # if the number generated is 1
print("{} * {} = ?".format(left, right)) # print 'left' multiplied by 'right'
ans = left * right # sets "ans" to equal the value of num1 multiplied by 'right'
else: # the only possible value is: op == 2
print("{} - {} = ?".format(left, right)) # print 'left' subtracted by 'right'
ans = left - right # sets "ans" to equal the value of num1 subtracted by 'right'
user_ans = get_integer_input()
if user_ans == ans:
score += 1
print("Well done, you are CORRECT")
else:
print("SORRY, you are INCORRECT") # print writing in brackets
print("The correct answer was {}".format(ans))
if score == 10: # run this part of code if "score" equals 10
print("Fantastic {}, you got full marks!".format(name))# print statement and name
elif score >= 6: # run this part of code if "score" is larger than or equal to 6
print("Well done, there's some improvement to be done here, though {} you got {}/10".format(name, score))# then print statement and score
elif score <=5: # run this part of code if "score" is smaller than or equal to 5
print("hmm, maybe some more practise would be beneficial, {}, you got {}/10".format(name, score)) # then print statement and score
return class_number, name, score
Finally, a last improvement that could be done to your code is to use a dict containing the operators:
import operator
operations = {
'+': operator.add,
'*': operator.mul,
'-': operator.sub
}
and then you'd simplify run_qa as follows:
def run_qa(questions):
print("What is your name?") # prints writing in brackets
name = input().title() # Capitalizes the first letter of the word inputted
print("What class are you in? (Enter 1, 2 or 3)") # asks the user to input a number
class_number = get_integer_input(maxval=3)
# prints writing in brackets and anything saved in the variables "name" and "class_number"
print ("Hello, {} from class {} welcome to my quiz".format(name, class_number))
score = 0 # sets the variable "score" to zero
for op, left, right in questions:
# convert from index value into symbol (the "list()" is a needed trick for python3
op = list(operations.keys())[op]
print("{} {} {} = ?".format(left, op, right))
# calculate the operation with operator 'op' using 'left' and 'right'
ans = operations[op](left, right)
user_ans = get_integer_input()
if user_ans == ans:
score += 1
print("Well done, you are CORRECT")
else:
print("SORRY, you are INCORRECT") # print writing in brackets
print("The correct answer was {}".format(ans))
if score == 10: # run this part of code if "score" equals 10
print("Fantastic {}, you got full marks!".format(name))# print statement and name
elif score >= 6: # run this part of code if "score" is larger than or equal to 6
print("Well done, there's some improvement to be done here, though {} you got {}/10".format(name, score))# then print statement and score
elif score <=5: # run this part of code if "score" is smaller than or equal to 5
print("hmm, maybe some more practice would be beneficial, {}, you got {}/10".format(name, score)) # then print statement and score
return class_number, name, score
Nota Bene: I have not tested my code locally, it's been live coding within the answer box
of stackoverflow, so there might be syntax errors and maybe minor bugs. The goal of
my answer is to emphasize what you're doing wrong and show you how to do better. So,
please do not take it "as is" to copy/paste, but read it through, understand what I'm
showing to you, and update your code with what I given you as an improvement.
Here's a full example I just tested:
# make the script both py2 and py3 compatible
from __future__ import print_function
import sys
if sys.version_info.major == 2:
input = raw_input
import operator
import random
import json
import os
def store_results(class_number, name, score):
# this adds '.json' to the end of the file (therefore creating a json file)
class_file = "{}.json".format(class_number)
# first step: load the existing data
# first test if the file exists, and if not, use an empty score dictionary
if not os.path.exists(class_file):
scores = {}
# otherwise populate the dictionary
else:
# open as read only, because we just want the data out of it
with open(class_file, 'r') as f:
# load contents of the json file "f" as object "scores"
scores = json.load(f)
# second step: update the data
scores.setdefault(name, []).append(score)
# third step: update the file
# use 'w' when opening the file to overwrite all data within it
with open(class_file, 'w') as f:
# dump object "scores" within file "f"
json.dump(scores, f, sort_keys=True, indent=4, separators=(',', ': '))
def get_integer_input(maxval=None):
while True:
try:
# print a nice prompt
print("> ", end="")
i = int(input()) # asks for an integer input from user
if maxval:
if i >= maxval:
continue
print("Sorry, input shall be inferior than {}, try again".format(maxval))
break
except ValueError:
print("Sorry, I didn't understand that, please try again") # print statement
return i
operations = {
'+': operator.add,
'*': operator.mul,
'-': operator.sub
}
def generate_qa():
questions = []
while len(questions) < 3: # add a question until we have three generated
op = random.randint(0,2) # randomly generate a number from 1-3 and store as "maths"
num1 = random.randint(1,10) # randomly generate an integer from 1-10 and store as "num1"
num2 = random.randint(1,10) # randomly generate a second integer from 1-10 and store as "num2"
questions.append( (op, num1, num2) )
return questions
def run_qa(questions):
print("What is your name?") # prints writing in brackets
name = input().title() # Capitalizes the first letter of the word inputted
print("What class are you in? (Enter 1, 2 or 3)") # asks the user to input a number
class_number = get_integer_input(maxval=3)
# prints writing in brackets and anything saved in the variables "name" and "class_number"
print ("Hello, {} from class {} welcome to my quiz".format(name, class_number))
score = 0 # sets the variable "score" to zero
for op, left, right in questions:
# convert from index value into symbol (the "list()" is a needed trick for python3
op = list(operations.keys())[op]
print("{} {} {} = ?".format(left, op, right))
# calculate the operation with operator 'op' using 'left' and 'right'
ans = operations[op](left, right)
user_ans = get_integer_input()
if user_ans == ans:
score += 1
print("Well done, you are CORRECT")
else:
print("SORRY, you are INCORRECT") # print writing in brackets
print("The correct answer was {}".format(ans))
if score == 10: # run this part of code if "score" equals 10
print("Fantastic {}, you got full marks!".format(name))# print statement and name
elif score >= 6: # run this part of code if "score" is larger than or equal to 6
print("Well done, there's some improvement to be done here, though {} you got {}/10".format(name, score))# then print statement and score
elif score <=5: # run this part of code if "score" is smaller than or equal to 5
print("hmm, maybe some more practise would be beneficial, {}, you got {}/10".format(name, score)) # then print statement and score
return class_number, name, score
if __name__ == "__main__":
questions = generate_qa()
class_number, name, score = run_qa(questions)
store_results(class_number, name, score)
HTH

PYTHON: Error Message in random.py? Used to work, then it didn't, then it did, now it doesn't again. What's happening?

I've been learning python for a few months now, and usually I've been able to overcome all the problems I face, but now I'm at a loss. I'm writing a program called 'Quizzer' that will be used to generate random questions based on lists of terms and answers that Python is given.
My main problem has with the the gen_question function I've been working on. I wanted Python to receive a term, and output four multiple choice answers: One the actual, and three randomly selected from the pool of all possible answers. I had to include several checks to make sure the selected random answers were not the real answer and were not the same as each other.
I finally got it to work today, and then a bit later I tested it. I got an error message (that I will display in a second). I undid everything back to where I was earlier and I still got the same error message. After a few hours I came back, and I got it again. Out of frustration, I retried, and it worked. Now it isn't working anymore. Please, anyone: What is going on?
Here is my code (I don't know what's necessary so I am including the entire thing):
#import random for generating
import random
#term and definition libraries
terms_ans ={'term1':'answer1','term2':'answer2','term3':'answer3','term4':'answer4','term5':'answer5','term6':'answer6','term7':'answer7','term8':'answer8','term9':'answer9','term10':'answer10','term11':'answer11','term12':'answer12','term13':'answer13','term14':'answer14','term15':'answer15','term16':'answer16','term17':'answer17','term18':'answer18','term19':'answer19','term20':'answer20'}
term_list = ['term1','term2','term3','term4','term5','term6','term7','term8','term9','term10','term11','term12','term13','term14','term15','term16','term17','term18','term19','term20']
answer_list = ['answer1','answer2','answer3','answer4','answer5','answer6','answer7','answer8','answer9','answer10','answer11','answer12','answer13','answer14','answer15','answer16','answer17','answer18','answer19','answer20']
#picks the test questions to ask
def gen_test(amount=len(term_list)):
found_starter = False
test_terms = []
while found_starter == False:
#pick a random starting point in the terms to see if it is suitable
start_point = random.randint(1, len(term_list))
if amount == len(term_list):
#if user inputs max amount of questions possible, just take the term list
test_terms = term_list
found_starter = True
elif len(term_list) - (start_point + amount) >= 0:
#if it is suitable, then append the terms to the test questions
for x in xrange(start_point,start_point+amount):
test_terms.append(term_list[x])
found_starter = True
else:
return test_terms
#scramble list
def list_scrambler(unscrambled_list):
test_terms=[]
countdown = len(unscrambled_list) + 1
for x in range(1, countdown):
transfer_var = random.randint(0,len(unscrambled_list)-1)
test_terms.append(unscrambled_list[transfer_var])
del unscrambled_list[transfer_var]
return test_terms
#ask user for amount of questions needed and get the list
test_terms = list_scrambler(gen_test(int(raw_input("How many questions on your test? (There are " + str(len(term_list)) + " questions in total.) "))))
def gen_question(picked_term, question_num=1, total_amount=len(test_terms)):
#print start of question
print
print "Question " + str(question_num) + " of " + str(total_amount) + ":"
print
print picked_term
print
#gather random multiple choice answers they must a) all be different and b) not be the answer
ans_1_acceptable = False
while ans_1_acceptable == False:
int_rand_ans_1 = random.randint(1, len(term_list)) - 1
if str(term_list[int_rand_ans_1]) != str(picked_term):
#Term accepted; send to output
ans_1_acceptable = True
ans_2_acceptable = False
while ans_2_acceptable == False:
int_rand_ans_2 = random.randint(1, len(term_list)) - 1
if int_rand_ans_2 != int_rand_ans_1 and str(term_list[int_rand_ans_2]) != str(picked_term):
ans_2_acceptable = True
ans_3_acceptable = False
while ans_3_acceptable == False:
int_rand_ans_3 = random.randint(1, len(term_list)) - 1
if int_rand_ans_3 != int_rand_ans_1 and int_rand_ans_3 != int_rand_ans_2 and str(term_list[int_rand_ans_3]) != str(picked_term):
ans_3_acceptable = True
#Decide if the correct answer is A, B, C, or D
correct_ans = random.randint(1,4)
#Print the options using the variables gathered above
if correct_ans != 1:
print "A) " + answer_list[int_rand_ans_1]
else:
print "A) " + terms_ans[picked_term]
if correct_ans != 2:
print "B) " + answer_list[int_rand_ans_2]
else:
print "B) " + terms_ans[picked_term]
if correct_ans != 3:
print "C) " + answer_list[int_rand_ans_3]
else:
print "C) " + terms_ans[picked_term]
if correct_ans == 1:
print "D) " + answer_list[int_rand_ans_1]
elif correct_ans == 2:
print "D) " + answer_list[int_rand_ans_2]
elif correct_ans == 3:
print "D) " + answer_list[int_rand_ans_3]
else:
print "D) " + terms_ans[picked_term]
print
Now, usually it outputs everything like you'd expect. I don't have a feature to automatically generate questions yet so I have to type in the line:
gen_question('term1')
or whatever term I'm using.
Here is the output I've been getting:
How many questions on your test? (There are 20 questions in total.) 20
>>> gen_question('term1')
Question 1 of 20:
term1
Traceback (most recent call last):
File "<pyshell#0>", line 1, in <module>
gen_question('term1')
File "C:\Users\Owner\Desktop\LEARNING PYTHON\scripts\in progress\Quizzer.py", line 69, in gen_question
int_rand_ans_1 = random.randint(1, len(term_list)) - 1
File "C:\Users\Owner\Desktop\LEARNING PYTHON\python 2.7.5\lib\random.py", line 241, in randint
return self.randrange(a, b+1)
File "C:\Users\Owner\Desktop\LEARNING PYTHON\python 2.7.5\lib\random.py", line 217, in randrange
raise ValueError, "empty range for randrange() (%d,%d, %d)" % (istart, istop, width)
ValueError: empty range for randrange() (1,1, 0)
>>> gen_question('term8')
This is what is getting you:
term_list = [...]
is defined at the start of your file, but later on you do the following when the amount entered is the max.
test_term = term_list
This does not create a copy of your array, this creates two variables which both reference the same array. So any further modifications to test_term are actually reflected against the list referenced by both variables.
And since you are defining test_terms at a global level in the script you NUKE it when you make this call
def list_scrambler(unscrambled_list):
test_terms=[]
countdown = len(unscrambled_list) + 1
for x in range(1, countdown):
transfer_var = random.randint(0,len(unscrambled_list)-1)
test_terms.append(unscrambled_list[transfer_var])
del unscrambled_list[transfer_var]
return test_terms
Also to add,
Hungarian notation is a big no-no and python is a strongly typed language anyways. If you are having a hard time keeping track of times don't rely on variable names. Instead get yourself an IDE or use names expressive of what they are doing.
if something == false:
should be rewritten as
if not something
This one is more for preference, but when printing out text that needs to have data floated in, you can save yourself some headache and write
"D) {0}".format(somelist[index])
This will stuff the variable into the {0} and provides you with some formatting context and prevents you from having to str() an object.
Also, globals in general are considered a bad thing, they're debatable. like globals in C sometimes they serve a clear purpose, but for the most part they hide bugs and make issues harder to track. Also sometimes your variable declarations will shadow globals, others (as you saw) will let you screw things up.
Well, it's pretty obvious that randint() is complaining because term_list is empty, right? Then
random.randint(1, len(term_list))
is
random.randint(1, 0)
and randint is stuck. So why is term_list empty? It's because this statement:
test_terms = list_scrambler(gen_test(int(raw_input("How many questions on your test? (There are " + str(len(term_list)) + " questions in total.) "))))
destroys term_list, and that's probably ;-) not intended.
It's hard to follow the code to track down why that happens. The basic problem is that gen_test can set
test_terms = term_list
and then end up returning term_list under the name test_terms. Then term_list is still intact at the start of list_scrambler, but empty by the time list_scrambler ends. The
del unscrambled_list[transfer_var]
deletes all the elements in term_list, one at a time.

Categories

Resources