[Tutor] Python help

Cameron Simpson cs at cskk.id.au
Sat Sep 4 22:29:54 EDT 2021


On 04Sep2021 10:16, Sarah Flores <flores.sarahlu at gmail.com> wrote:
>Hello!

Welcome!

>I have never posted to python.org before.

This is a shiny new question; please post these as new messages, not as 
a reply (even an edited reply) with a distinctive subject line (eg "is 
myassigment attempt sensible?").

Not this time!  Next time!

Anyway, comments inline below.

>I am working on an assignment for a class that is meeting virtually and I
>am looking for some feedback before I have to turn this in Sunday night
>(9/5, midnight pacific time)

Hah. Here on (probably) the other side of the planet this means little!  
I'm on GMT+10. What's your GMT offset? Also not very important :-)

(Also, most places on the planet read 9/5 as the 9th day of the 5th 
month.)

[... assigmnnent spec snipped...]
>Code is attached as a txt file.

You may want to paste the code inline next time; some of the python.org 
lists discard attachments, particularly nontext attachments. That said, 
your attachment arrived fine.

># CONSTANTS
>TIER_1 = 1
>TIER_2 = 2
>TIER_3 = 3
>
>TIER1_MAX = 20000
>TIER2_MAX = 30000
>TIER3_MAX = 40000
>
>TIER1_BENEFIT = 2000
>TIER2_BENEFIT = 1500
>TIER3_BENEFIT = 1000

These are all good. Python does not have constants, but we do have a 
convention that values to be treated as constants are named in UPPERCASE 
as you have done here.

Broadly your code looks like a sensible approach. There are some bugs, 
and there are many comments I would make, again inline below:

># Variables
>childNum = 0
>income = 0
>aid = 0

These are "global" variables. I recommend avoiding them. The problem 
with globals has a few aspects:
- it means you've only got one piece of state; if you were doing this 
  work for several situations instead of just one this will be a problem
- it means this name is known in every function; often that is not 
  desireable - you might use it by accident, particularly when the name 
  represents something your programme does a lot
- the local variables in a function _shadow_ (hide) the global 
  variables; this means that assigning to such a name inside a function 
  affects the local variable, and _not_ the global one - sometimes that 
  isn't what you want, and is confusing as well

Because of all of this, we usually try to avoid global variables.  
Instead we pass each function the values it needs to work with.

All of that said, the "constants" you defined earlier? They're just 
global variables. We treat them differently because:
- they are visually different because of their UPPERCASE names, so 
  confusion is less likely
- because we treat them as constants, we're _not_ assigning new values 
  to them later

Also, in Python you do not need to "declare" variables - just use them.  
You could just discard these lines.

Note that variables have scopes - namespaces where they are defined.  If 
you assign to a name, that name is defined in the current scope. The 
assignments above therefore define names in the global scope.  That is 
_distinct_ from the function local scopes which occur when you assign to 
a name inside a function.

The flipside of Python not declaring variables is that assigning to a 
variable _implies_ it is part of the current scope. Example:

    # global variable "x"
    x = 1

    def f():
        # local or global? local, because we assign to it
        x = 2
        print(x)

    print(x)    # prints the global
    f()         # calls the function, which prints its local
    print(x)    # prints the global again

That would print

    1
    2
    1

because the function only affects its local variable. This is how 
globals can be confusing. If you don't use then, this situation won't 
occur.

># Defining the function
>def main():
>    print("Your household income is $", income)
>    if income >= TIER3_MAX:
>        print("Sorry, you are ineligible for financial aid.")
>    else:
>        financialAid(income,aid)
>    return financialAid

Conventionally the "main" function is the one which runs the entire 
programme. Just convention though.

There seems to be confusion about what this function returns. In the 
loop at the bottom of the script you just call main(), so anything it 
returns would be discarded. So you could just remove the "return 
financialAid" statement. (Also, "return financialAid" means to return 
the function "financialAid" itself - functions are objects and can be 
returned. But I doubt that is what you intend.)

Also, financialAid computes the aid. There is no need to pass "aid" in 
as a parameter. (And if you drop the "declaration" assignments from 
earlier Python will complain that "aid" is unknown, which highlights 
that there is not a meaningful preexisting value to pass in. So don't!)

># This function determines whether an applicant will qualify for aid & how much.
>def financialAid(income,aid):
>    childNum = int(input("How many children live in your household? "))
>    if income < TIER1_MAX:
>        aid = childNum * TIER1_BENEFIT
>        print("You are eligible for $",TIER1_BENEFIT," per child of assistance")
>        print("Your financial aid is $", aid)
>        return aid
>    elif TIER2_MAX > income >= TIER1_MAX:
>        if childNum >= TIER_2:
>            aid = childNum * TIER2_BENEFIT
>            print("You are eligible for $",TIER2_BENEFIT," per child of assistance")
>            print("Your financial aid is $", aid)
>            return aid
>        else: print ("Sorry. You are ineligible for financial assistance.")
>    elif TIER3_MAX > income >= TIER2_MAX:
>        if childNum >= TIER_3:
>            aid = childNum * TIER3_BENEFIT
>            print("You are eligible for $",TIER3_BENEFIT," per child of assistance")
>            print("Your financial aid is $", aid)
>            return aid
>        else: aid = 0
>        print("Sorry. You are ineligible for financial assistance.")
>        return aid

This function computes aid and returns it at several points. That can be 
fine, but a common approach is to compute aid but return just the once 
at the very bottom of the function:

    def financialAid(income):
        if/elif/elif....
        return aid

You've got good use of Python's compound comparisons here. i would be 
inclined to keep their ordering consistent. For example:

    if income < TIER1_MAX:
    elif TIER1_MAX <= income < TIER2_MAX:
    elif TIER2_MAX <= income < TIER3_MAX:

and so on, making the brackets more clear. I'd also start by offering no 
aid (aid = 0) before the if-statement and have the variable aid brakcets 
set it as appropriate.

The find else aboev looks like this:

    else: aid = 0
    print("Sorry. You are ineligible for financial assistance.")

I expect both of these should have been in the else: clause, but as 
written the print() is not - it is unconditional and always happens. You 
want:

    else:
        aid = 0
        print("Sorry. You are ineligible for financial assistance.")

># This portion includes the while loop and sentinel value (-1)
># This portion also calls the main function
>income = int(input("What is the total household income for the first applicant? (enter -1 to end) $"))
>while income != -1:
>    main()
>    income = int(input("What is the total household income for the next applicant? (enter -1 to end) $"))

A common way to write this which avoids doubling up the input call is 
like this:

    while True:
        income = int(input("What is the total household income for the first applicant? (enter -1 to end) $"))
        if income == -1:
            break  # leave the loop
        main()

Notice that you get an income value but do not use it. Probably you 
should pass it to main():

    main(income)

and main in turn should pass it to financialAid (which you already do):

    def main(income):
        .................
        financialAid(income,aid)
        .................

In fact main() does little enough that personally I would put its 
if-statement in the main "while" loop at the bottom, or better still in 
the financialAid() function. Then you wouldn't need main() at all.

Cheers,
Cameron Simpson <cs at cskk.id.au>


More information about the Tutor mailing list