While Loop increment issue - python

Right now I am trying to make a simple program to separate the javascript links of a website but I'm running into issues with a while loop.
Here would be an example of an input:
001_usa_wool.jpg
002_china_silk.jpg
003_canada_cotton.jpg
004_france_wool.jpg
done
A simplified version of my code with just 3 parts is the following:
def ParseData(input):
data = input.split('_')
d = {}
d['sku'] = data[0]
d['country'] = data[1].capitalize()
d['material'] = data[2].capitalize()
return d
def Sku():
myData = ParseData(input)
sku = myData['sku']
return sku
def Country():
myData = ParseData(input)
country = myData['country']
return country
def Material():
myData = ParseData(input)
material = myData['material']
return material
def Output():
print (Sku()+'\t'+
Country()+'\t'+
Material()+'\t'+
'\n')
Now here is how I tried to read it line by line:
def CleanInput(input):
clean = input.split('.jpg')
count = 0
while (clean[count] != 'done'):
ParseData(clean[count])
Output()
count = count+1
input = input('Enter your data: ')
CleanInput(input)
I believe I am not implementing the while loop correcting since my output is similar to:
001 Usa Wool
001 Usa Wool
001 Usa Wool

The issue is not exactly in your while loop , but in your functions - Output() , Sku() , Material() and Country() .
In the function Output() , you are printing the values by directly calling Sku(), etc.
In each of the function, I will take one as example - Sku() , you are calling parseData on input (Though this is a very bad naming, please use a better name, with this name you are overwriting the built-in input function, and later on you cannot call input() to take inputs from user)
The input always contains the entire string you inputted , and hence it contains all the .jpg names, when parseData goes through it, it always only picks up the first one.
Instead of using input in each function, we should make the functions parameterized and send in the value that needs to be printed as a parameter, as you are doing for parseData . And Example code -
def Sku(toprint):
myData = ParseData(toprint)
sku = myData['sku']
return sku
.
.
.
def Output(toprint):
print (Sku(toprint)+'\t'+
Country(toprint)+'\t'+
Material(toprint)+'\t'+
'\n')
And in the while loop send in the current value to print as parameter to Output() -
def CleanInput(input):
clean = input.split('.jpg')
count = 0
while (clean[count] != 'done'):
ParseData(clean[count])
Output(clean[count])
count = count+1
Also , please do not use input as the name of the variable , it can cause issues , as i previously stated , as you are overwriting the built-in input function with that.

Personally, I would make it more pythonic:
def CleanInput(input):
clean = input.split('.jpg')
for count, elem in enumerate(clean):
if elem == 'done':
break
ParseData(elem)
Output()
return count
input_data = input('Enter your data: ')
how_many = CleanInput(input_data)
Assuming you really need count. By the way: you aren't using the return value of ParseData

You have too many functions that call each other and take on vague requirements. It's hard to see what returns something, what prints, and so on. For example, your CleanInput calls ParseData and Output, but Output calls Sku, Country, and Material, each of which also calls ParseData. Oh, and capitalized variables should be reserved for classes - use snake_case for functions.
>>> s = "001_usa_wool.jpg002_china_silk.jpg003_canada_cotton.jpg004_france_wool.jpgdone"
>>> print(*("{}\t{}\t{}".format(*map(str.capitalize, item.split('_')))
... for item in s.split('.jpg') if item != 'done'), sep='\n')
001 Usa Wool
002 China Silk
003 Canada Cotton
004 France Wool

Related

Printing user output with while loop/beginner Python

hopefully there is a simple answer for this. How can I take user input from a while loop and print all of the input (without using lists when writing this)? Is there a way to output this information when the number of executions from the user is unknown? I am only a beginner so if there is a simple way to do this just using basics please let me know!
Here is an example of what I am trying to ask:
Say I am asking the user for the name and price of an item. They have the option to enter more items, or stop the execution. Then the output would list the name of the items, the prices, and their total amount purchased. I would like to print out all of these items that they have inputted.
Output formatted something like this:
Item Name Item Price
Soap $ 3.98
Detergent $ 6.99
Chips $ 2.50
....
....
Your Total: $xx.xx
I know how to do everything else, just having issues displaying all of their given input after they stop the execution. Just needing some guidance in this area. Please let me know if there is anything I can clarify. Thanks in advance!
I am using Python 3.
So I hope this matches what you were asking for.
I used nested dictionries, incase you want more info per item, but I mean you can also use tuples if you want.
prompts = ["Item name", "Item price"]
flag = True
items = {}
indx = 0
while flag:
inp = input("Type 'new' to register a new item or 'exit' to exit ")
if inp == "new":
output = {}
for p in prompts:
inp = input(p)
output[p] = inp
indx += 1
items[indx] = output
if inp == "exit":
flag = False
This is fairly simple, but to explain the steps;
Typing new allows you to start entering a new item.
It asks the prompts that are listed in the prompts list.
(It also uses these prompts to name the values in the output dict)
Once all the data is in, it'll be indexed and added into the items dict.
Then the loop begins again. You can add the new item, or type exit to stop entering items.
Edit:
Also I decided to use a flag instead of breaks so you can modify if you want more stop conditions.
I didn't understand so well but I think you can use:
bill = ''
total_price = 0
while True:
item = input('(If Done Write Exit) Item Name: ')
if item == "exit":
break
price = input("price: ")
bill = bill + "Item: "+item + '\nPrice: ' + price + '\n\n'
total_price += int(price)
print('\n'*4)
print(bill+'\n\nTotal Price:',total_price)

Self Limiting Repition Function

I'm writing a program that is basically a study guide/ practice test for the current section of my A&P class (it keeps me more engaged than just rereading notes over and over). The test works without any problems, but I have an issue where some of my questions use an "enterbox" input, I can have the question loop if the answer is incorrect, but I can't get it to break without a correct answer.
I figured out a way to make it work by putting the entire function back into the initial "else" tree, so that right or wrong you advance to the next question but it looks incredibly ugly and I can't believe there isn't a better way.
So my "solution" looks like such:
def question82():
x = "which type of metabolism provides the maximum amount of ATP needed for contraction?"
ques82 = enterbox(msg = x, title = version)
#version is a variable defined earlier
if ques82.lower() in ["aerobic"]:
add() #a function that is explained in the example further below
question83()
else:
loss() #again its a housecleaning function shown below
ques82b = enterbox(msg = x, title = version)
if ques82b.lower() in ["aerobic"]:
add()
question83()
else:
loss()
question83()
Okay so it worked, but using a nested if tree for each "enterbox" question looks kinda sloppy. I'm self taught so it may be the only solution but if there is something better I would love to learn about it.
So here is a complete section from my program:
from easygui import *
import sys
version = 'A&P EXAM 3 REVIEW'
points = 0
def add():
global points
msgbox("Correct", title = version)
points = points + 1
def loss():
global points
msgbox("Try Again", title = version)
points = points - 1
def question81():
x = "What chemical is stored by muscle as a source of readily available energy for muscle contractions"
ques81 = enterbox(msg = x, title = version)
if ques81.lower() in ["creatine"]:
add()
question82()
else:
loss()
question81()
It works as is so any errors from what's provided are probably my fault from copy and pasting.
Also I'm running it in python 2.7rc1 if that helps.
Thanks for any help in advance.
I don't know if there is a way to combine "enterbox" that has a button for "skip" as that would also be a solution.
Consider the following approach:
We define a list of question and answer pairs. We do this in one place so it's easy to maintain and we don't have to search all over the file to make changes or re-use this code for a different questionset.
We create an ask_question function that we can call for all of our questions. This way, if we want to make a change about how we implement our question logic, we only have to make it in one spot (and not in each of the questionXX functions).
We compare user input to our answer using == and not in (in will do something else, not what you expect).
We create an object to keep track of our answer results. Here, it's an instance of ResultsStore, but it can be anything really, let's just try to get away from global variables.
Use a loop when prompting for answers. The loop will repeat if the answer given was incorrect (and if retry_on_fail is False).
Allow for the user to enter some "skip" keyword to skip the question.
Display the results once the "test" is complete. Here, we do that by defining and calling the store.display_results() method.
So, what about:
from easygui import enterbox
question_answer_pairs = [
("1 + 1 = ?", "2"),
("2 * 3 = ?", "6"),
("which type of metabolism provides the maximum amount of ATP needed for contraction?", "aerobic")
]
VERSION = 'A&P EXAM 3 REVIEW'
class ResultStore:
def __init__(self):
self.num_correct = 0
self.num_skipped = 0
self.num_wrong = 0
def show_results(self):
print("Results:")
print(" Correct:", self.num_correct)
print(" Skipped:", self.num_skipped)
print(" Wrong: ", self.num_wrong)
def ask_question(q, a, rs, retry_on_fail=True):
while True:
resp = enterbox(msg=q, title=VERSION)
# Force resp to be a string if nothing is entered (so .lower() doesn't throw)
if resp is None: resp = ''
if resp.lower() == a.lower():
rs.num_correct += 1
return True
if resp.lower() == "skip":
rs.num_skipped += 1
return None
# If we get here, we haven't returned (so the answer was neither correct nor
# "skip"). Increment num_wrong and check whether we should repeat.
rs.num_wrong += 1
if retry_on_fail is False:
return False
# Create a ResultsStore object to keep track of how we did
store = ResultStore()
# Ask questions
for (q,a) in question_answer_pairs:
ask_question(q, a, store)
# Display results (calling the .show_results() method on the ResultsStore object)
store.show_results()
Now, the return value currently doesn't do anything, but it could!
RES_MAP = {
True: "Correct!",
None: "(skipped)",
False: "Incorrect" # Will only be shown if retry_on_fail is False
}
for (q,a) in question_answer_pairs:
res = ask_question(q, a, store)
print(RES_MAP[res])
Quick and dirty solution could be using the default value "skip" for the answer:
def question81():
x = "What chemical is stored by muscle as a source of readily available energy for muscle contractions"
ques81 = enterbox(msg = x, title = version, default = "skip")
if ques81.lower() == 'creatine':
add()
question82()
elif ques81 == 'skip':
# Do something
else:
loss()
question81()
But you should really study the answer given by jedwards. There's a lot to learn about
program design. He's not giving you the fish, he's teaching you to fish.

Python: How to get data from other defined function to the other?

I have this program:
def NoOfPeople(people):
if people.isdigit() and (int(people)>=1) and (int(people)<=1000):
return True
else:
print('invalid')
while True:
people = input('No. of people:')
if NoOfPeople(people):
break
How do I use data from it to a new defined function as I have this menu and program which I am stuck at right now on how to continue to get the data from the previous one.It is to check if the selected room can accommodate to the number of people that are entered in NoOfPeople(people). So how can I compare the number of people with which room it can accommodate:
Room
[1] Room A (10 person)
[2] Room B (30 person)
[3] Room C (50 person)
venue = input('Please select a venue:')
def validateVenue(venue):
if venueList == '1':
(what should I continue from here?)
Help and suggestion please as I am new in using python.Thanks
This is pretty open ended but I will try and help you out.
in general you can pass a variable / data_structure to another function and return it out of the function back to whatever called the function like this...
def function_name(var1, var2):
var3 = var1*var2
return var3
new_var = function_name(3, 5)
print(new_var)
output
15
1) I don't know what venuList looks like you might want to make a Key value pair (dictionary)
def select_venue(num_of_people):
#this is your venue key value dictionary, IE your available venues and their size
venues = {10:"Venue Small Conf Room" , 30:"Venue Ballroom", 50:"Venue Large Conf Room", 3:"Venue Office"}
venue_options = []
for occupancy in venues:
if occupancy >= num_of_people:
venue_options.append(occupancy)
venue = min(venue_options)
venue_name = venues[venue]
print("Please Use {} for your event with {} people (max occupancy {})".format(venue_name,num_of_people, venue))
if __name__ == "__main__":
people = 4 #change this value to test
select_venue(people)
your output should look like this
Please Use Venue Small Conf Room for your event with 4 people (max
occupancy 10)
You will notice the key is the max occupancy of a venue and the value is the name of the venue.
You should also take note they don't have to be in order (small office is last)

Auto append list items in dictionary with default values python

I am trying to create an attendance logger where I create a dictionary which I fill with student names. The names will be lists where I append their class attendance data (whether they attended class or not). The code I have so far is displayed below`
#! /bin/python3
#add student to dict
def add_student(_dict):
student=input('Add student :')
_dict[student]=[]
return _dict
#collect outcomes
def collector(student,_dict, outcome):
_dict[student].append(outcome)
return _dict
#counts target
def count(_dict,target):
for i in _dict:
# records total attendance names
attendance_stat = len(_dict[i])
# records total instances absent
freq_of_absence=_dict[i].count(target)
# records percentage of absence
perc_absence = float((freq_of_absence/attendance_stat)*100)
print(i,'DAYS ABSENT =',freq_of_absence)
print('TOTAL DAYS: ', i, attendance_stat)
print('PERCENTAGE OF ABSENCE:', i, str(round(perc_absence, 2))+'%')
#main function
def main():
#date=input('DATE: ')
outcomes=['Y','N']
student_names = {}
try:
totalstudents = int(input('NO. OF STUDENTS: '))
except ValueError:
print('input an integer')
totalstudents = int(input('NO. OF STUDENTS: '))
while len(student_names) < totalstudents:
add_student(student_names)
print(student_names)
i = 0
while i < totalstudents:
i = i + 1
target='Y'
student=str(input('student :'))
outcome=str(input('outcome :'))
collector(student,student_names,outcome)
count(student_names,target)
if __name__=='__main__':
main()
`
The code works well so far but the problem is when the number of students is too large, time taken to input is extensive cutting in on class time. Since the number of absentees is usually less than those present, is it possible to select from the dictionary students absent which will append the value Y for each selected absent, while appending N to the remaining lists in dictionary.
This isn't exactly what you're asking for, but I think it will help. Instead of asking the user to input a name each time for the second part, why not just print the name yourself, and only ask for the outcome? Your last while loop would then become a for loop instead, like this:
for student_name in student_names:
outcome = input("Outcome for {}: ".format(sudent_name))
collector(student_name, student_names, outcome)
You could also add some logic to check if outcome is an empty string, and if so, set it to 'N'. This would just allow you to hit enter for most of the names, and only have to type in 'Y' for the certain ones that are absent. That would look like this:
for student_name in student_names:
outcome = input("Outcome for {}: ".format(sudent_name))
if outcome = "":
outcome = "N"
collector(student_name, student_names, outcome)

How to call in a specifc csv field value in python

I am so new to python (a week in) so I hope I ask this question properly.
I have imported a grade sheet in csv format into python 2.7. The first column is the name of the student and the column titles are the name of the assignments. So the data looks something like this:
Name Test1 Test2 Test3
Robin 89 78 100
...
Rick 72 100 98
I want to be able to do (or have someone else do) 3 things just by typing in the name of the person and the assignment.
1. Get the score for that person for that assignment
2. Get the average score for that assignment
3. Get that persons average score
But for some reason I get lost at figuring how to get python to recognize the field I am trying to call in. So far this is what I have (so far the only part that works is calling in file):
data = csv.DictReader(open("C:\file.csv"))
for row in data:
print row
def grade()
student= input ("Enter a student name: ")
assignment= input("Enter a assignment: ")
for row in data:
task_grade= data.get(int(row["student"], int(row["assignment"])) # specific grade
task_total= sum(int(row['assignment'])) #assignment total
student_total= #student assignments total-- no clue how to do this
task_average= task_total/11
average_score= student_total/9
You can access the individual "columns" of your csv this way:
import csv
def parse_csv():
csv_file = open('data.csv', 'r')
r = csv.reader(csv_file)
grade_averages = {}
for row in r:
if row[0].startswith('Name'):
continue
#print "Student: ", row[0]
grades = []
for column in row[1:]:
#print "Grade: ", column
grades.append(int(column.strip()))
grade_total = 0
for i in grades:
grade_total += i
grade_averages[row[0]] = grade_total / len(grades)
#print "grade_averages: ", grade_averages
return grade_averages
def get_grade(student_name):
grade_averages = parse_csv()
return grade_averages[student_name]
print "Rick: ", get_grade('Rick')
print "Robin: ", get_grade('Robin')
What you are trying to do is not meant for Python because you have keys and values. However...
If you know that your columns are always the same, no need to use keywords, you can use positions:
Here is the easy, inefficient* way to do 1 and 3:
students_name = ...
number = ...
for line in open("C:\file.csv")).readlines()
items = line.split()
num_assignments = len(items)-1
name = items[0]
if name = students_name:
print("assignment score: {0}".format(items[number]))
asum = 0
for k in range(0,num_assignments):
asum+= items[k+1]
print("their average: {0}".format(asum / num_assignments)
To do 2, you should precompute the averages and return them beucase the averages for each assignment is the same for each user query.
I say easy *innefficnet because you search the text file for each user query each time a name is entered. To do it properly, you should probably build a dictionary of all names and their information. But that solution is more complicated, and you are only a week in! Moreover, its longer and you should give it a try. Look up dict.
I believe the reason you are not seeing the field the second time around is because the iterator returned by csv.DictReader() is a one-time iterator. That is to say, once you've reached the last row of the csv file, it will not reset to the first position.
So, by doing this:
data = csv.DictReader(open("C:\file.csv"))
for row in data:
print row
You are running it out. Try commenting those lines and see if that helps.

Categories

Resources