[Tutor] cases with single if and an else clause
dn
PyTutor at DancesWithMice.info
Tue Oct 5 15:08:56 EDT 2021
On 06/10/2021 07.39, Dennis Lee Bieber wrote:
> On Tue, 5 Oct 2021 07:22:24 +0530, Manprit Singh
> <manpritsinghece at gmail.com> declaimed the following:
>
>> Dear sir,
>>
>> Now there is one more problem , Kindly loom at below written functions :
>>
>> def grade(percent):
>> if percent < 0 or percent > 100:
>> ans = "Invalid Input"
>> elif percent >= 90:
>> ans = "A"
>> elif percent >= 70:
>> ans = "B"
>> elif percent >= 60:
>> ans = "C"
>> else:
>> ans = "Fail"
>> return ans
>>
>
> My biggest concern is that you are using the same "ans" return to
> indicate invalid input, letter grades (you have a big gap between A and B
> -- at least in US you'd commonly have breakpoints A:90, B:80, C:70, D:60,
> and E (or F) at 50).
>
> Presuming the numeric grade is being entered by the user of some
> program (say a teacher's electronic grade book) the input should have been
> validated at the place it was entered, NOT in a function that just
> translates number ranges into a letter grade.
>
> If you really need to validate /in/ this function, you should be
> raising an exception... and the caller needs to catch this exception and do
> whatever is needed to correct the data before retrying the operation. As
> is, the caller is forced to test every return from the function for a
> SPECIFIC string...
>
> aGrade = grade(score)
> if aGrade == "Invalid Input":
> #do something to correct the input and retry the call to grade()
> else:
> #record the returned letter grade
>
>
>
> There are multiple ways to avoid a chain of if/elif/else comparisons...
> But for that you need to study /data structures/ and the algorithms using
> them rather than just playing around with simple examples that would seldom
> be used in actual applications. Especially examples that are mostly
> concerned with /coding style/ than on flexibility (algorithm reuse). If the
> code runs, it is technically correct -- but may look ugly and locked in to
> just the one application. Coding style, these days, tends to be defined by
> the company one works at, and one has to follow some guide book that
> already exists at that company. If you want to follow the One-In/One-Out of
> Structured Programming -- try to lay out your code using
> https://en.wikipedia.org/wiki/Nassi%E2%80%93Shneiderman_diagram
> (even in OOP, once one gets down to coding class methods, one should be
> back to structured programming)
>
> -=-=-=-
> C:\Users\Wulfraed\Documents\_Hg-Repositories\Python Progs>letterGrade.py
> Enter percentage grade for Abigail: -5
> Invalid numeric grade
> Please try again
> Enter percentage grade for Abigail: 45
>
> Letter grade for Abigail is F
>
>
> Enter percentage grade for Bertram: 110
> Invalid numeric grade
> Please try again
> Enter percentage grade for Bertram: 99
>
> Letter grade for Bertram is A
>
>
> Enter percentage grade for Charlie: xyz
> invalid literal for int() with base 10: 'xyz'
> Please try again
> Enter percentage grade for Charlie: 68.5
> invalid literal for int() with base 10: '68.5'
> Please try again
> Enter percentage grade for Charlie: 68
>
> Letter grade for Charlie is D
>
>
> Enter percentage grade for Edith: 77
>
> Letter grade for Edith is C
>
>
> Enter percentage grade for Francisca: 81
>
> Letter grade for Francisca is B
>
>
>
> C:\Users\Wulfraed\Documents\_Hg-Repositories\Python Progs>
> -=-=-=-
> STUDENTS = [ "Abigail",
> "Bertram",
> "Charlie",
> "Edith",
> "Francisca" ]
>
> GRADES = [ (60, "F"),
> (70, "D"),
> (80, "C"),
> (90, "B"),
> (100, "A") ] #ordered list of break points
>
> def grade(percent):
> if percent < 0 or percent > 100:
> raise ValueError("Invalid numeric grade")
> for pc, ltr in GRADES:
> if percent <= pc: return ltr
>
> for st in STUDENTS:
> while True:
> try:
> pcent = input("Enter percentage grade for %s: " % st)
> pcent = int(pcent)
> ltrGrade = grade(pcent)
> print("\nLetter grade for %s is %s\n\n"
> % (st, ltrGrade))
> break
> except ValueError as ve:
> print(str(ve))
> print("Please try again")
>
> -=-=-=-
>
> For the example, I've left GRADES as a read-only "global". For reuse
> purposes, it should be passed as an argument to the grade() function.
>
> This is easily modifiable for use when grading "by the curve".
> Simplified version: "C" is the mean of the scores, and one then works
> outwards; all that needs to be changed is the GRADES list; so if the mean
> came in at 60 then GRADES would look like:
>
> GRADES = [ (45, "F"),
> (55, "D"),
> (65, "C"), #allow 5 below to 5 above
> (75, "B"),
> (100, "A") ] #don't change to catch outliers
>
> If you input the scores per student first, you can have the program
> calculate the mean and modify GRADES as needed.. Granted, this simplified
> version will have problems if the scores are near the extremes... mean of
> 85 makes GRADES look like
>
> GRADES = [ (70, "F"),
> (80, "D"),
> (90, "C"), #allow 5 below to 5 above
> (100, "B"),
> (100, "A") ] #no one gets a "A"
>
> The more complex version would compute %iles using mean&std.dev. and use
> those to set the break points for letter grades.
+1
Those of us who learned "Structured Programming" when it was 'new'
probably still think of it as a 'thing'. (I'm not sure how often such is
mentioned in 'today's training', and haven't seen anyone under thirty
using a Nassi-Schneiderman chart for ages) We also take quite-naturally
to the SRP (Single Responsibility Principle)!
The OP named the function "grade" - in a conversation about 'style'.
Choosing names is important. It's also a matter of style - a weak name
requires a strong comment/docstring! In this case, the function lacks a
descriptive name.
As soon as one describes the (original) functionality as 'check validity
of numeric grade input AND convert to a letter-grade, if possible' the
problem @wulfraed illustrates becomes starkly-evident.
Secondly, we refer to successive mutually-exclusive if-statements as a
"ladder". It is logical that the conditions proceed systematically from
low-to-high, or vice-versa. Certainly "method" is better than "madness",
and testing becomes more methodical. (easier?) Accordingly, the first
if-statement is discordant because it can't make up its mind - is it the
'top' of the ladder, or the bottom? Such is NOT as readable as it
(easily) could/should be!
If the function includes an if-statement (to trap invalid data), won't
the calling code also have to cope with the same possibility? Does this
mean that there will be two if-statements which ask essentially the same
question, in two different scopes? Is that a good idea?
Instead of over-loading a data-object (see earlier post), is it time to
look at using Exceptions to convey meaning and handle unwanted
'edge-cases'?
(however, I'd prefer the 'solution' mooted above)
--
Regards,
=dn
More information about the Tutor
mailing list