0

I am about a week into Python. I have looked at various other questions regarding this, and have grown fairly frustrated that my attempts to implement those suggestions are falling flat.

I have tried a few means of passing variables through, based on things I have read. For example (this isn't necessarily representative of a coherent attempt... I have tried many variations and have had to walk back to get code worth posting):

def verify_nums():
    globhours = hours
    globrate = rate
    try:
        globhours = float(globhours)
        globrate = float(globrate)
        return globhours,globrate
    except:
        print("You must provide numbers")

def calc():
    globhours = globhours
    globrate = globrate
    if globhours > 40:
        base = 40 * globrate
        ot = (globhours - 40) * (globrate * 1.5)
        pay = base + ot
        print(pay)
    else:
        pay = globrate * globhours
        print(pay)


hours = input("Enter hours worked: ")
rate = input("Enter hourly rate: ")
verify_nums()
calc()

I am supremely confused on how to transfer the hours and rate variables over to the calc() function.

I eventually figured out I could just merge these into one function...

def paycalc(hours,rate):
    etc.
    etc.

hours = input("Enter hours: ")
hours = input("Enter hours: ")
paycalc(hours,rate)

But for the sake of learning, I really want to get this global/local thing figured out.

Thanks for the help!

6
  • Indentation matters in python. You might want to fix verify_nums first. Commented Jul 28, 2018 at 5:08
  • Sorry - the indentation is completely fine in the actual code. I will edit this post to reflect that. Commented Jul 28, 2018 at 5:10
  • Also, try to avoid global variables as much as possible and leverage the beauty of the encapsulation concept of function or classes. Commented Jul 28, 2018 at 5:11
  • Thanks. Definitely aware of the global variable aversion, which is part of my posting here. As far as the encapsulation - I will take note of that but think it might be a bit advanced for me presently. Will keep it in mind for down the road though. Commented Jul 28, 2018 at 5:14
  • The idea of "encapsulation" isn't that complicated, even if it has a fancy word… but it is hard to explain. What JAponte is suggesting is that you plan your program around what are the responsibilities and duties of each function (and each object, when you get to classes). Then you can writing things so that all the information needed for those responsibilities and duties is clearly and obviously visible, but everything else is under the covers. Commented Jul 28, 2018 at 6:29

1 Answer 1

1

You seem to be trying to get Python to guess which functions are supposed to be global and which local based on their names. Python doesn't do that. If you assign to a variable in a function, and you want that assignment to be global, you need a global statement at the top of the function:

def verify_nums():
    global globhours
    global blograte
    globhours = hours
    globrate = rate
    # ...

Also, globhours = globhours doesn't do anything useful—and, in fact, it causes a problem. If you global globhours in that function as well, the statement is meaningless. Without that, you're creating a local variable, and assigning it… the value of that local variable that doesn't exist yet.

Anyway, if you add the right global declarations to all of your functions, they will work, but it won't be a great design.


You really don't need any global variables here. If you think about values rather than variables, everything gets a lot easier.

Your verify_nums function needs to work on two values. So just pass those values in as parameters. And it needs to return two values—that's easy, you already did that part.

Now the caller has to store those two values that it returned, so it can pass them to the calc function. Which can also take two values as parameters.

Putting that all together:

def verify_nums(hours, rate):    
    try:
        numhours = float(hours)
        numrate = float(rate)
        return numhours, numrate
    except:
        print("You must provide numbers")

def calc(hours, rate):
    if hours > 40:
        base = 40 * rate
        ot = (hours - 40) * (rate * 1.5)
        pay = base + ot
        print(pay)
    else:
        pay = rate * hours
        print(pay)

globhours = input("Enter hours worked: ")
globrate = input("Enter hourly rate: ")
hours, rate = verify_nums(globhours, globrate)
calc(hours, rate)

One problem left: what happens if there's an error with the user's input? Inside verify_nums, you handle the error with an except:, then you print a message and do nothing. That means you return None. So, when the caller tries to do hours, rate = None, it's going to get an error, which you're not handling. And you can't just carry on without values. What can you do?

More generally "return a pair of numbers, or return None" is a confusing contract for a function to fulfill. How do you use that function? With a whole lot of ugly type-checking. But "return a pair of numbers, or raise an exception" is a perfectly good contract. How do you use that function? With a simple try.

That's why it's better to put the exception handling in exactly the right place where you can deal with it. You want to skip calling calc if there's an error, so the except has to be where you call calc.

def verify_nums(hours, rate):    
    numhours = float(hours)
    numrate = float(rate)
    return numhours, numrate

def calc(hours, rate):
    if hours > 40:
        base = 40 * rate
        ot = (hours - 40) * (rate * 1.5)
        pay = base + ot
        print(pay)
    else:
        pay = rate * hours
        print(pay)
try:
    globhours = input("Enter hours worked: ")
    globrate = input("Enter hourly rate: ")
    hours, rate = verify_nums(globhours, globrate)
except ValueError:
    print("You must provide numbers")
else:
    calc(hours, rate)

Another improvement you might want to consider: Have calc return the pay, instead of printing it, and make the caller print the value it returns.

Sign up to request clarification or add additional context in comments.

3 Comments

Thanks abarnet, I appreciate the thorough answer. There were a few things there conceptually that I just was not considering, and this really helped. The error checking bit is especially appreciated - the top line of my file is a comment reminding me to tackle that after the variables aspect. I will get back to work! Much thanks :)
Out of curiosity - what is the reasoning behind returning pay and having the caller print it? I went ahead and did it, but am not sure what functional difference it makes.
@CreateChange Imagine if you wanted to subtract taxes from your pay, or append your pay to a text file, or multiply it by 52 to get an estimated yearly salary if you got this pay every week, or calculate the payroll for a whole company from a list of employees. A function that ends with return pay can do all of those things; a function that does print(pay) can't.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.