1

I made a program that is supposed to calculate the perimeter of a wall, and then calculate the cost of the paint needed to paint that wall based on the input.

Code:

def main():
length = float(input('Enter length: ')) #Get length.
width = float(input('Enter width: ' )) #Get width.
height = float(input('Enter height: ')) #Get height.
Paint_cost()
def Paint_cost (length, width, height): #Find total paint cost.
    perimeter = length + width *4 #Find perimiter.
sq_ft = perimeter * height #Find total sq. ft. amount.
Paint = sq_ft / 300 #Calculate paint gallons to nearest int.
round(Paint)
Total= Paint*40 #Calculate total cost.
return total #Display total.

main()

However Python keeps saying "UnboundLocalError: local variable 'Paint_cost' referenced before assignment". What am I doing wrong here?

abarnert
  • 354,177
  • 51
  • 601
  • 671
KernelPanic
  • 211
  • 1
  • 3
  • 9
  • 1
    Please fix the indentation. Besides the fact that it's always good for readability of any code in any language, and always essential for being able to debug Python code, in this case it's quite possible that the actual problem with your code is indentation, so we _especially_ need to see how you've indented it. – abarnert Apr 17 '15 at 19:26
  • 2
    @Tutleman: I'm about 80% sure that your edit actually removed the OP's actual problem, making this a useless question. Instead of trying to guess what his indentation _might_ be, leave it for him to tell us what it actually _is_. – abarnert Apr 17 '15 at 19:30
  • @abarnert You're absolutely right. Just as you made the reversion, I was about to do the same thing. Given the error he's getting, I'd agree with you that this mistake is likely one of indentation. – Tutleman Apr 17 '15 at 19:32
  • I would be interested to know the platform and version of python you are running because I get a different result when running this. – NKamrath Apr 17 '15 at 19:32
  • @NKamrath: Well, when run as posted, you're going to get an `IndentationError` on any version of Python. Until he shows us his actual code, it's not very useful. – abarnert Apr 17 '15 at 19:32
  • The indentation looks correct in Python, but when I put it in the post here it screws it up. I've removed the Paint_cost(), because it was interfering. – KernelPanic Apr 17 '15 at 19:33
  • @abarnert True! However, I made the (perhaps misguided) assumption the indentation was correct. Even then, the error he reported is not what I get. The way this runs is not an error of the function being unbound or a misorder because python will interpret it and get a value for that function correctly. However, he is calling it wrong I believe – NKamrath Apr 17 '15 at 19:34
  • @KernelPanic: But the `Paint_cost()` was the line that was causing the error, so removing it makes the question pointless. Also, if posting here seems to screw up your indentation, that's often a sign that you're mixing tabs and spaces in your source code, which is a huge problem in itself—it means the actual indentation of your code, as far as Python is concerned, is often completely different from how it looks to you in your text editor. The best way to fix that is to get a better text editor. Also try running Python with the `-tt` flag. – abarnert Apr 17 '15 at 19:45
  • @NKamrath: Even _when_? If you fix the `IndentationError` in a way that removes his original problem, then of course you're not going to see his original problem. You have to reproduce his actual indentation to reproduce his actual problem, and that's not easy to guess at. – abarnert Apr 17 '15 at 19:48
  • @abarnert Well if you want to keep it for the sake of prosperity then be my guest. This monstrosity I made belongs in /r/badcode, or something. – KernelPanic Apr 17 '15 at 19:51
  • @KernelPanic: If you don't want the question to be here at all, close and/or delete it, or ask someone else to. But if you're going to leave it here, it should include the code you were actually asking about; otherwise it's definitely useless. – abarnert Apr 17 '15 at 20:25

3 Answers3

2

You have a few problems:

  • First, you're defining the function Paint_cost() inside main(). You can define this outside of main(), and as long as it's defined before you call the main() function, it will work properly.
  • Second, return returns a value from a function, not print it.
  • Third, your indentation is off. Regardless of the other two errors, Python will raise an IndentationError if you tried to run this.
  • Fourth, total is undefined (you wrote it as Total.)
  • Finally, you're calling Paint_cost() without any arguments. You need to call it with Paint_cost(length, width, height).

This code works perfectly in Python 3:

def Paint_cost (length, width, height): #Find total paint cost.
    perimeter = length + width * 4 #Find perimiter.
    sq_ft = perimeter * height #Find total sq. ft. amount.
    Paint = sq_ft / 300 #Calculate paint gallons to nearest int.
    int(Paint)
    total = Paint*40 #Calculate total cost.
    return total #Display total.
def main():

    length = float(input('Enter length: ')) #Get length.
    width = float(input('Enter width: ' )) #Get width.
    height = float(input('Enter height: ')) #Get height.
    print(Paint_cost(length, width, height)) # Print the cost of the paint.

main()

This one is for Python 2:

def Paint_cost (length, width, height): #Find total paint cost.
    perimeter = length + width * 4 #Find perimiter.
    sq_ft = perimeter * height #Find total sq. ft. amount.
    Paint = sq_ft / 300 #Calculate paint gallons to nearest int.
    int(Paint)
    total = Paint*40 #Calculate total cost.
    return total #Display total.
def main():

    length = float(input('Enter length: ')) #Get length.
    width = float(input('Enter width: ' )) #Get width.
    height = float(input('Enter height: ')) #Get height.
    print Paint_cost(length, width, height)  # Print the cost of the paint.

main()

In this code specifically, print is the only change between Python 2 and 3. The function works without print in either version.
Let me know if something is wrong.

ASCIIThenANSI
  • 865
  • 1
  • 9
  • 27
  • This fixed it. Just out curiosity though why did you swap the two definition places? I assume that they have to be this way? – KernelPanic Apr 17 '15 at 19:41
  • @KernelPanic Which two definition places? – ASCIIThenANSI Apr 17 '15 at 19:42
  • Actually this is an assumption a lot of answers make here, but python will run this with the functions out of order because it will bind Paint_cost before main runs which is what calls it. I would advise @ASCIIThenANSI change this part of his answer. This is technically legal and will run. Pop it in a file and give it a shot. – NKamrath Apr 17 '15 at 19:43
  • @NKamrath You mean moving the `Paint_cost` function below `main`? Or something else? – ASCIIThenANSI Apr 17 '15 at 19:45
  • I mean the order of the functions does not effect it. Try running it with main defined above paint cost and it will run. Because main is called after paint cost is defined. – NKamrath Apr 17 '15 at 19:46
  • @NKamrath Oh, OK. I was originally keeping `def Paint_cost` inside `main`, but I reformatted that. Thanks for the help! – ASCIIThenANSI Apr 17 '15 at 19:46
  • @ASCIIThenANSI Yeah, no problem! Just change your first bullet in this answer pls. Functions can be called by other functions before definition in python as long as they are not RUN before definition. It was just a misconception in a lot of the posts on this thread and it was bothering me lol. This is ok in python although maybe not the best of practices. – NKamrath Apr 17 '15 at 19:48
  • @ASCIIThenANSI Oh... I see it's the same order lol. I am just over tired. Anyway than you all for the help! :) – KernelPanic Apr 17 '15 at 19:49
  • @NKamrath: Yes, if you define `Paint_cost` globally, it doesn't matter whether you define it before or after `main`. But if you define it locally, inside `main`, it _does_ matter whether you define it before or after calling it. So, "the order doesn't matter" is a bit misleading, but the correct way to put it in a way that won't confuse a novice may be hard to get right… – abarnert Apr 17 '15 at 19:50
  • @abarnert It really is about the call stack. That is all that matters since python is interpreted as long as the function is bound to memory at the time the call is made, it will work. I understand what you are saying, my only argument is it is wrong to say that you can not have a function called by another function before it is defined. This is not strictly true. – NKamrath Apr 17 '15 at 19:53
  • @NKamrath: Well, in this particular case it isn't even about the call stack; (Python knows at compile time, not call time, or even function definition time that `Paint_cost` is defined inside `main`; that's how it knows it's a local name), but I understand what you're saying too. I just don't know how the answer should try to explain it that's clear but novice-friendly at the same time. :) – abarnert Apr 17 '15 at 20:24
  • @ASCIIThenANSI Could you help me with one more thing? I am trying to get my program to also round the number up so that it looks neater. I tried [this](http://stackoverflow.com/a/3348866/4802624), but had no luck with it. – KernelPanic Apr 17 '15 at 20:47
  • @KernelPanic OK, it should just print an integer now. I assume this is what you meant? – ASCIIThenANSI Apr 17 '15 at 21:07
  • @ASCIIThenANSI Yes. Everything works, but I just want to round it up so it looks neater. – KernelPanic Apr 17 '15 at 21:19
  • @abarnert yeah, too bad python isn't compiled :) – NKamrath Apr 20 '15 at 12:07
  • @NKamrath: Sure it is. CPython and PyPy compile to CPython bytecode, which PyPy then JITs to machine code as needed, while CPython just interprets the bytecode; Jython and IronPython are a bit more complicated to describe in a comment, but they also compile. – abarnert Apr 22 '15 at 17:00
  • @NKamrath: And the fact that Paint_cost is determined to be a local variable at compile time, not at runtime (not even when the function definition is executed at runtime) is crucial to understanding why it's UnboundLocalError (and, more generally, how the LEGB rules actually work). – abarnert Apr 22 '15 at 17:01
  • @abarnert so what you mean is python CAN be compiled because it is actually an interpreted language by nature. Sure you can compile it, this is easy. But you missed my entire original point which is depending on the order in which the function calls occur, you can write a function funA that calls a function funB so long as funB is defined before funA is run (not defined). That is all I was saying. Thus the explanation originally was incorrect because this can happen due to the way the code was run. Thus, the call stack. I was addressing the over generalization of the original answer. – NKamrath Apr 22 '15 at 21:22
  • @NKamrath: No, what I mean is that Python _is_ compiled, by every Python implementation I know of, including the standard CPython one. And even if there were an implementation that didn't have a compiler, it would still have to act as if it did, because the language defines certain things to happen at compile time. In particular, it's when compiling a module, not when building the function at runtime, that Python decides which variables in the function are locals. So, you can't do that if the two functions are in the same local scope; runtime is way too late. – abarnert Apr 22 '15 at 21:34
  • @NKamrath: (And if they're not in the same local scope, the call stack has nothing to do with anything; both definitions are executed at the top level of the stack… unless you stick an `import` inside a function in another module or something…) – abarnert Apr 22 '15 at 21:35
  • @abarnert Ubuntu 12.04LTS running python 2.7 will do what you said isn't possible. Write some code and test it. Define a function main and in that function call a function B. Then, below the main (not inside main) define the function B that main calls. Then, in __ main __ call main. This WILL work. It is always good to write code to prove your statements instead of blindly posting things that you think to be true and talking about things like the call stack when it is clear that you obviously don't know how they work. I would suggest wiki :) – NKamrath Apr 23 '15 at 12:05
  • @NKamrath: I'm sorry, but you seem to have a problem with basic English comprehension, and I don't know how to deal with that, so I'm giving up trying to explain this. – abarnert Apr 23 '15 at 21:50
  • @abarnert It's ok, I can literally run the code I just explained which obviously justifies my point. If you can't enter that into a file and run it in python, then I am also going to give up. – NKamrath Apr 24 '15 at 15:35
0

You're calling the function Paint_cost before it's defined. Move the Paint_cost function outside of main, and your program should run without an issue.

def Paint_cost (length, width, height): #Find total paint cost.
    return length + width *4 #Find perimiter.
    sq_ft = perimeter * height #Find total sq. ft. amount.
    Paint = sq_ft / 300 #Calculate paint gallons to nearest int.
    round(Paint)
    return Paint*40 #Calculate total cost.

def main():
    length = float(input('Enter length: ')) #Get length.
    width = float(input('Enter width: ' )) #Get width.
    height = float(input('Enter height: ')) #Get height.
    return Paint_cost(length, width, height)

main()

Other notes: You return total in the last line of main(), but that's also not defined anywhere. Python is case-sensitive, so total and Total are not the same thing.

Dan Loewenherz
  • 10,879
  • 7
  • 50
  • 81
  • Well that fixed one issue. Now Python is saying "NameError: name 'perimeter' is not defined". Even though it is. – KernelPanic Apr 17 '15 at 19:29
  • Try running the exact code I've got above. I changed it to set the value of `perimeter` to the return value of `Paint_cost`. LMK if that works! – Dan Loewenherz Apr 17 '15 at 19:30
  • @KernelPanic: If you have multiple bugs, don't expect someone to do all your debugging for you on StackOverflow. If this problem is solved, accept an answer, try to solve the next problem yourself, and create a new question (linking to this one, if it's relevant) if/when you get stuck. – abarnert Apr 17 '15 at 19:31
0

However Python keeps saying "UnboundLocalError: local variable 'Paint_cost' referenced before assignment". What am I doing wrong here?

Exactly what it says:

Paint_cost()
def Paint_cost (length, width, height): #Find total paint cost.
    perimeter = length + width *4 #Find perimiter.
    # ...

You call Paint_cost() before you've defined it, so it has nothing to call yet, so it raises an exception.


To fix it, just don't do that. Move the call to Paint_cost after the definition. Or move the definition of Paint_cost out to global level. (If it's not accessing any local variables from main, it doesn't need to be defined inside main.)


The message may be slightly confusing because it refers to "assignment" and what you're doing is def Paint_cost(…):, not Paint_cost = …. But def is a kind of assignment, in that it's binding a new name.

abarnert
  • 354,177
  • 51
  • 601
  • 671