2
\$\begingroup\$

I wrote up a simple averages calculator for student grades, based on this daily programming challenge.

First, the script asks for the file name for the text file containing the student names and grades. It is formatted like so:

JON 19 14 15 15 16
JEREMY 15 11 10 15 16
JESSE 19 17 20 19 18

The user is then asked how many assignments there are.

While my solution works, it seems awfully verbose. Can you give me tips on "pythonic" ways to simplify the script?

grade_avg_generator.py

"""Simple student grade manager"""

from fileinput import input


def import_data(grades_file, assignments):
    """Split the data into key value pairs."""
    student_grades = dict()
    for line in input(grades_file):
        this_line = line.split()
        student_grades[this_line[0]] = [int(item) for item in this_line[1:assignments + 1]]
    return student_grades


def student_means(student_grades):
    """Generate the average grade for each student and append it."""
    for k, v in student_grades.iteritems():
        grades = v
        grades_mean = float(sum(item for item in grades)) / len(grades)
        v.append(grades_mean)
    student_grades_mean = student_grades
    return student_grades_mean


def class_mean(student_grades_mean):
    """Generate the class average."""
    class_grades = list()
    for k, v in student_grades_mean.iteritems():
        this_avg = v[-1]
        class_grades.append(this_avg)
    class_mean = float(sum(item for item in class_grades)) / len(class_grades)
    return class_mean


def main():
    grades_file = raw_input('File name: ')
    assignments = int(raw_input('How many assignments are there? '))

    student_data = import_data(grades_file, assignments)
    student_data_avg = student_means(student_data)
    class_avg = class_mean(student_data_avg)
    print 'class average: %0.2f' % class_avg
    for k, v in student_data_avg.iteritems():
        grades = v[:-1]
        grades = ' '.join(str(i) for i in grades)
        avg = str(v[-1])
        print 'name: %s | grades: %s | average: %s' % (k, grades, avg)


if __name__ == '__main__':
    main()
\$\endgroup\$

1 Answer 1

2
\$\begingroup\$

For conciseness in Python, the key is to think in terms of applying functions using map and list comprehensions, rather than thinking procedurally. In this problem, it is very important to define a mean() function. Once you do that, the calculations themselves are one-liners.

The main objection I had to your code was that you mangled the data structure by appending each student's average at the end of his grades. Don't do that — student_means() should just return a fresh dict instead.

I've taken out the non-essential prompt for the number of assignments; you can easily put it back.

""" Simple student grade manager"""

from fileinput import input

def mean(list):
    return sum(map(float, list)) / len(list) \
      if len(list) > 0 else float('nan')

def import_student_grades(grades_file):
    """
    Given a filename, reads data into a dict whose keys are student names
    and whose values are grades (as strings).
    """
    line_words = [line.split() for line in input(grades_file)]
    return dict((words[0], words[1:]) for words in line_words)

def main():
    grades_file = raw_input('File name: ')
    student_grades = import_student_grades(grades_file)
    student_means = dict((k, mean(v)) for k, v in student_grades.iteritems())

    print 'class average: %0.2f' % mean(student_means.values())
    for name, grades in student_grades.iteritems():
        print 'name: %(name)s | grades: %(grades)s | average: %(avg)0.2f' % {
            'name': name,
            'grades': ' '.join(grades),
            'avg': student_means[name],
        }

if __name__ == '__main__':
    main()
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.