Here's a good way to think about it. A function is a "black box" that takes in some number of values, called arguments, in a specific order and does something with them to produce some other value. When I say "black box," I mean that when you use the function, you don't have to care how it does what it does, you just give it some values and you get a value back.
Let's consider a very simple function that just subtracts the two numbers it's given: the first minus the second. In other words, we'll make a function that implements the rule "argument #1 - argument #2." Now, when you're writing the code for this function, you need some way to tell the computer when you want to use argument #1 and when you want to use argument #2. In some other programming languages, you have to do it by explicitly specifying the number of the argument you want to use (#1 or #2), but it's a lot easier to write code if you can give these values names. So Python, like most other languages, lets you refer to the arguments of a function using names of your choosing. For example, suppose you want argument #1 to go under the name x
, and argument #2 to go under the name y
. You could indicate that by writing this:
def subtract(x, y):
This would be followed by the code that constitutes the function. For the subtraction example, it would be
def subtract(x, y):
return x - y
When the Python compiler encounters this, it translates the code into its internal representation of "calculate value #1 - value #2 and send that back to my caller." It then packs up that block of code and saves it under the name subtract
(because that's what you told it you wanted to name the function).
Hopefully it makes sense that once this block of code finishes executing, it no longer makes any sense to refer to "argument #1" or "argument #2," because you can't have arguments without a function! So similarly, once the function has done its thing, the labels that you gave to the arguments, x
and y
, no longer have any meaning. The labels only exist for the duration of the function's code. This is called scoping: limiting labels to the part of the code where they mean something.
Because the labels x
and y
are locally scoped, as one might say, in a way it doesn't even matter what they are. For instance, if you had that definition of subtract
in your code, you could arbitrarily decide to change them to first
and second
, and all you would have to change would be the code within that one function. You would just change the definition to
def subtract(first, second):
return first - second
and that's it - your code is functionally exactly the same. Anywhere else in the program that x
and y
occur, they're referring to something other than the arguments of this function, so you don't have to change them when you rename the arguments.
What's happening in your case is that you tried to use the label length
somewhere outside of the function it was defined for (namely, the function that you've stored as draw_rectangle
). Python knows that you can't be referring to the argument of a function you're not in, so it expects you to have already defined length
to mean something else. But you didn't. That's why you're getting an error. (Well, that one error, anyway)