[Tutor] program not doing what I need it to do

Alan Gauld alan.gauld at yahoo.co.uk
Sun Jan 28 19:45:15 EST 2018


On 28/01/18 22:06, Edna Broukhim wrote:

You have tried to give the variables sensible names but
a little bit more work will make your code more readable
and therefore easier to debug. You need to  rethink your
data model slightly.

You have a number of users (which you don't explicitly
store at present).

For each user you have 3 exam results.

The average is (exam1+exam2+exam3)/3

Instead of storing 3 lists of N results why not
store N lists of 3 results? That allows you to
leverage Pythons sum() function so your code
becomes something like:

for user_id in range(num_users):
    average = sum(scores[user_id])/3

Which I think you will agree is easier to read
than the code you currently have?

Here are some other general comments:

> user=int(input("How many students?"))

This is not really the user, it is the number of
users so call it something like

user_count or num_users

or just count.

> Score1=[]
> Score2=[]
> Score3=[]

Again these are lists so not single scores,
Call them scores1..3 or maybe better still
create a list of lists:

scores = []  # three sets of scores.

> name=[]

Again this is not a single name so call it names.

> for i in range (0, user):

This then becomes:

for user_id in range(num_users)

>     name.insert(user, input("What is your name?"))

Here we have a more serious problem.

Your name list has no entries so the first inserts
will ignore the user_num and just insert the name
at the end, so you might as well just use append()
instead. insert is really for use in a populated list.

The other issue is that while you loop over the numbers
up to num_users you always insert at num_users not at i.
In fact you ignore i. (or, as I called it, user_id)

Instead try

for user_id in range(num_users):
    names.append(input(....))

But why only store the names? Why not add the users scores too?

users = []
for user_id in range(num_users):
    user = [input(....),[]]  # empty list for scores
    users.append(user)

> for i in range (0, user):

Again this might be clearer as

for user_id in range(num_users):

>     ExamGrades=['exam1','exam2','exam3']

Notice that you assign this every time you go round the
loop. You probably only need to do it once before the loop.
And again the name is not really reflecting the values.
It is not grades but exam names, so the variable should be
something like:

ExamNames

Although by convention we reserve capitalised names for
classes so better would be:

examNames or exam_names

But to be honest I don't think you really need it!

>     Score1.append(float(input(str(name[i])+ ":What is your score on your first exam?")))

We need to decompose this a bit to see what is happening:

       prompt = names[user_id] + ":What is your score on exam" +
                str(exam)
       value = float(input(prompt))
       Score1.append(value)

Breaking things into smaller chinks makes it easier to read
and you can print the intermediate values if it goes wrong.
Putting too much in one line is usually a bad idea.

However, with the restructuring I suggested above it could
be written as

for user in users:
   print("For user: ",user[0])  # print name as prompt
   for exam in range(1,4):
      score = float(input("Score for exam " + str(exam))
      user[1].append(score)   # add it to the users scores list

> def Average():
>     global average
>     global grade

Its conventional to initialise global variables outside
the function, otherwise they are hard to find when you
come to debug another function that uses them.

>     for i in range (0, user):
>         for j in range(len(Score1)):
>             average=(Score1[j]+ Score2[j]+Score3[j])/3

Using my model this becomes:

for user in users):
    average = sum(user[1])/3

>         if(average>=90):
>             grade = "A"
>         elif(average>=80 and average<90):
>             grade="B"
>         elif(average>=70 and average<80):
>             grade="C"
>         elif(average>=60 and average<70):
>             grade="D"
>         else:
>             grade = "F"
> 
>         return average

That bit is OKI although you can tidy it slightly
with a Python trick:

         if(average>=90):
             grade = "A"
         elif(80 <= average < 90):
             grade="B"
         elif(70 <= average < 80):
             grade="C"
etc


> finalAverage=[]
> for i in range (0, user):
>         finalAverage.append(Average())

Rather than create another list why not add the average
(and grade?) to each users record within the loop above?

user.append( Average() )

You would needto modify the function to return the grade
too - otherwise you just throw it away...

    return (average, grade)    # a tuple of ave and grade

> print(name[i]+ ":Your average is: "+str(finalAverage[i])+ ", which is " + grade)

Since this is outside the loop it only prints the lsat entry...

for user in users:
   print(user[0], "Your average is: ",
         user[2][0], " and your grade is: "
         user[2][1] )

I hope that makes sense.

The data model you choose has a huge impact on how easy
it is to code things. And the names you choose have a
big impact on readability. Make lists plural and numbers
numbers etc.

-- 
Alan G
Author of the Learn to Program web site
http://www.alan-g.me.uk/
http://www.amazon.com/author/alan_gauld
Follow my photo-blog on Flickr at:
http://www.flickr.com/photos/alangauldphotos




More information about the Tutor mailing list