dpl47
dpl47

Reputation: 184

Variable Referenced Before Assignment

I am learning Python, and trying to write a tip calculator as a first small project.

I came up with the following code:

meal = raw_input("Cost of meal: ")
tax = raw_input("Tax: ")
tip = raw_input("Tip: ")

def tipCalc(meal, tax, tip):

    def convertInput(meal, tax, tip):
        try:
            retMeal = int(meal)
            retTax = int(tax)
            retTip = int(tip)
        except ValueError:
            retMeal = float(meal)
            retTax = float(tax)
            retTip = float(tip)
        return retMeal
        return retTax
        return retTip

    convertInput(meal, tax, tip)

    retTax = retTax / 100
    retTip = retTip / 100
    total = retMeal + retTax + retTip
    print total

tipCalc(meal, tax, tip)

However, I am getting the following error:

Traceback (most recent call last):
   File "/Users/dustin/Desktop/tipcalc.py", line 27, in <module>
     tipCalc(meal, tax, tip)
   File "/Users/dustin/Desktop/tipcalc.py", line 22, in tipCalc
     retTax = retTax / 100
UnboundLocalError: local variable 'retTax' referenced before assignment

This seems like a simple error to fix, but I can't seem to find an error in my logic.

Upvotes: 1

Views: 1732

Answers (4)

rmunn
rmunn

Reputation: 36678

The other answers have covered the reason for the exception you're getting, so I'm going to skip over that. Instead, I want to point out a subtle mistake that's going to bite you sooner or later and make your code calculate the wrong values if either the tax or the tip are ever entered as whole numbers.

IMPORTANT: This answer is true for Python 2.x (which I see you're using). In Python 3.x, the default behavior of division changes and this answer would no longer be true (and your code would work correctly).

With that in mind, let's look at something in the Python interpreter for a minute.

Python 2.7.4 (default, Apr 19 2013, 18:28:01) 
[GCC 4.7.3] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> 2.0 / 3.0
0.6666666666666666
>>> 2 / 3
0

In the first example, we see what happens when we divide two floats together: we get a float with the best approximation of the result we can. (2/3 will never be exactly represented by a float, but it's a close enough result for nearly all purposes). But see the second example? Instead of 0.666..., it returned 0. Why is that?

That's because in Python 2.x, dividing two ints together is guaranteed to always return an int, so it rounds down (always down) to the int. In combination with the % operator (which returns remainders), this lets you do "grade school" division, where 3 goes into 7 just 2 times, with 1 left over. This is quite useful in many algorithms, so it's kept around. If you want to use floating-point division (where you get a float result), you need to make sure that at least one of your numbers in the division is a float.

Thus, you should do 2.0 / 3 or 2 / 3.0 if you want the 0.6666... result in our example. And in your code, you should either do:

retTax = retTax / 100
retTip = retTip / 100

or else you should change your convertInput() function to always return floats no matter what. I suggest the latter:

def convertInput(meal, tax, tip):
    retMeal = float(meal)
    retTax = float(tax)
    retTip = float(tip)
    return retMeal, retTax, retTip

That way when you divide them by 100 later, you'll get a fraction. Otherwise, you could have ended up with an int result in tax or tip, and gotten a rounded-off value in your calculation.

There's another way to change Python's behavior regarding division, with the from __future__ import division statement in Python 2.x (which applies some of the Python 3.x rules). I won't go into it in detail now, but now you know what to Google if you want to read more about it.

Upvotes: 3

Uku Loskit
Uku Loskit

Reputation: 42040

Other than what the others have pointed out, I really see no real need for the function

def convertInput(meal, tax, tip):

Just use floats from the beginning

Upvotes: 0

karthikr
karthikr

Reputation: 99620

You probably mean this:

def tipCalc(meal, tax, tip):

    def convertInput(meal, tax, tip):
        try:
            retMeal = int(meal)
            retTax = int(tax)
            retTip = int(tip)
        except ValueError:
            retMeal = float(meal)
            retTax = float(tax)
            retTip = float(tip)
        return retMeal, retTax, retTip

    retMeal, retTax, retTip = convertInput(meal, tax, tip)

    retTax = retTax / 100
    retTip = retTip / 100
    total = retMeal + retTax + retTip
    print total

tipCalc(meal, tax, tip)

If you wish to return multiple values from a class method, multiple return statements do not work. You need to have 1 return statements, and send back as many values as you wish to.

Also, the error was, at the time of calculating retTax = retTax / 100, the variable was not already declared.

Upvotes: 4

Guillaume
Guillaume

Reputation: 10961

The retMeal, retTax and retTip variable are local to convertInput and you're not using the value returned by the function, add an assignement:

retMeal, retTax, retTip = convertInput(meal, tax, tip)

Upvotes: 2

Related Questions