0
\$\begingroup\$

Hoping to get feedback on how this program could be written in the most pythonic way. Also welcome any criticisms of styling as I know this is not in accordance with pep-8

from collections import namedtuple
a = namedtuple('assignment', 'weight scores')
def average(scores:list):
    '''average a list of scores'''
    return sum(scores)/len(scores)
def get_grade(grades:list):
    '''compute final grade based on scores and their weights'''
    result = 0
    for a in grades:
        result += average(a.scores) * a.weight
    return result 
def main():
   grades = [a(.30, [18/45, 11/55]),
             a(.20, [3/10, 7.5/10, 10/10]),
             a(.10, [9/10, 9/10, 10/10, 10/10, 8/10,
                6/10, 10/10, 10/10]),
             a(.40, [29/65]), 
             a(.01, [1/1])]
   print(get_grade(grades))
main()
\$\endgroup\$
3
  • 3
    \$\begingroup\$ Just posting a script without any description or what you hope to achieve from posting it is only going to attract downvotes and leave your question unanswered. \$\endgroup\$
    – I0_ol
    Commented Mar 22, 2017 at 4:31
  • 3
    \$\begingroup\$ Welcome to StackExchange Code Review! Please review How do I ask a good Question? Specifically, it is best to explain what the code does. And it is also code to discuss a bit about what your are hoping for from a review. \$\endgroup\$ Commented Mar 22, 2017 at 4:33
  • \$\begingroup\$ Thanks sorry new to the community and will be more descriptive next time. \$\endgroup\$
    – Adele
    Commented Mar 22, 2017 at 22:23

1 Answer 1

2
\$\begingroup\$
from collections import namedtuple

You should put blank lines after imports, and between function/class definitions.


a = namedtuple('assignment', 'weight scores')

Using a namedtuple for a simple data class like this is a good idea, but it should still be named as a class; Assignment.


def average(scores:list):
    '''average a list of scores'''
    return sum(scores)/2

This function has an obvious bug; what about lists of scores with lengths other than two? Also you type the parameter but not the return value, and should again have some more whitespace; I'd write:

def average(scores: list) -> float:
    """Average a list of scores."""
    return sum(scores) / len(scores)

See PEP-257 for docstring conventions.


def get_grade(grades:list):
    '''compute final grade based on scores and their weights'''
    result = 0
    for a in grades:
        result += average(a.scores) * a.weight
    return result 

I think you could simplify this with a bit more OOP. If we subclass the named tuple we can add a read only calculated attribute to it:

class Assignment(namedtuple('Assignment', 'weight scores')):

    @property
    def result(self) -> float:
        return average(self.scores) * self.weight

Then your function becomes:

def get_grade(assignments: list) -> float:
    """Compute final grade based on scores and their weights."""
    return sum(a.result for a in assignments)

Note the revised parameter name, which better describes the input.


def main():
   grades = [a(.30, [18/45, 11/55]),
             a(.20, [3/10, 7.5/10, 10/10]),
             a(.10, [9/10, 9/10, 10/10, 10/10, 8/10,
                6/10, 10/10, 10/10]),
             a(.40, [29/65]), 
             a(.01, [1/1])]
   print(get_grade(grades))
main()

To keep things looking consistent without introducing a lot of indentation, I usually split lists with long elements across multiple lines as follows:

assignments = [
    Assignment(.30, [18/45, 11/55]),
    ...
    Assignment(
        .10,
        [9/10, 9/10, 10/10, 10/10, 8/10, 6/10, 10/10, 10/10]
    ),
    ...
]

Note splitting between, rather than within, the two parameters.

Also you should guard the invocation of the entry point as follows:

if __name__ == '__main__':
     main()

This means you can import this functionality more easily for testing and reuse.

\$\endgroup\$
2
  • \$\begingroup\$ Note that in Python 2.x, your average function has a small bug. If sum(scores) is an integer, it will do integer division (because len(scores) is also an integer), instead of float division. Just wrap either the sum or the len in a float call to avoid it. \$\endgroup\$
    – Graipher
    Commented Mar 22, 2017 at 8:27
  • 1
    \$\begingroup\$ @Graipher you're correct, but given the integer divisions in the rest of the OP's code I assumed they'd have been bitten by that already if they were using 2.x! \$\endgroup\$
    – jonrsharpe
    Commented Mar 22, 2017 at 11:38

Your Answer

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

Not the answer you're looking for? Browse other questions tagged or ask your own question.