.. _more-functions: More on Functions ################# .. seealso:: Text: Chapter 3 (section 3.5-3.6) .. wordcount:: .. vim:ft=rst spell: We introduced the basic concept of a function earlier. Now we want to look a bit deeper at what goes on inside a function and talk about another important topic: Scope (no, not the mouthwash!) As we talked about before, a function is just a container holding a bit of code and local variables it can use to do its work. Most functions have ``parameters`` which are just more variables, but these variable are special, they are give values by the ``caller`` of the function. The idea is simple. The function is designed to work on whatever ``parameter`` values the caller wants to hand to it at any point in their processing. Think about that square root function we used earlier. That function would not be very useful if all it could do is figure out the square root of five. If we ``generalize`` it so it can work on any number, we can make that number a ``parameter`` and decide what number we want the square root of whenever we like. Callers and Callees ******************* Notice that I used the term ``caller`` in the discussion above. Formally, we say that our program ``calls`` a function to make it do work. The ``callee`` is the function we call. (Remember our idea about kicking the box to wake up the troll). The call just refers to the name of the function. The use of the function name causes Python to stop what it is doing at the moment, and activate the function. It waits until the function is done, then it presses on with what it was doing previously. For example, suppose we had a magic function we set up in our program that looks like this: .. code-block:: python def magic(number): ... return magic_number Somewhere inside this ``magic`` function, we expect the function to take the ``parameter`` known as ``number`` and produce a new variable (known only inside the function) called ``magic_number``. That final number is returned to the caller. The ``caller`` of this function can use this function wherever they need to. Since it returns a value, the actual ``call`` to the function can happen in the middle of an expression, in an assignment statement, or even in a call to another function. .. code-block:: python x = magic(5) x = magic(5) + magic(7) x = sqrt(magic(5)) sqrt(6.0) The first two examples should be easy to figure out. But the last two are a bit odd. In the third one, the caller wants the square root of a magic number. We could have done this in two steps: .. code-block:: python x1 = magic(5) x = sqrt(x1) That is correct, but it makes us invent a ``temporary variable`` (one we really only need for a short time) to hold the intermediate value. By placing the call to ``magic`` inside the call to ``sqrt``, we avoid needing another variable. The code works like you would expect. We process the assignment statement, but notice that there is a call to ``sqrt`` on the right side. The ``parameter`` must be available to activate this function. Unfortunately, the ``parameter`` is another call, this one to ``magic``. The call to magic also needs a parameter, and that parameter is in the code as a ``literal`` number. So, we call ``magic`` with its ``parameter`` and wait for it to return a ``magic number``. Once we have that, we can hand it to the ``sqrt`` function and wait for it to give us back an answer. That final answer is placed in the container named ``x``. Phew! Back to the final example above. What if we set up the function to return a value, but we used it in a way that does not seem to want the value. Python actvates the function, fetches the result, and throws it away. In this case, nothing really happens, except a bit of time was used up while the troll did his/her/it's thing! How parameters work ******************* When the caller of a function hands the function a value in the ``parameter list``, that value is passed into the function. The function sees it in a container named whatever the ``def`` line had on it. The names on the ``def`` line are only really known inside the function. That is fine, since the caller has no real need to know what is going on inside the box! This is a key idea in programming. We separate the two worlds: caller's world, and callee's world. Neither needs to know what is going on in the other world to do their jobs. How about multiple parameters ============================= Many functions need more than one ``parameter`` to do their job. We simple list the individual parameter names inside the parentheses, separating each with a comma. When we call this kind of function, we list the parameter values, again separating them with commas: .. code-block:: python def silly_multiply(a,b): return a * b ... x = silly_multiply(4,5) Keyword parameters ================== Python allows calling a function in a kind of odd way: .. code-block:: python def loan_rate(principle, interest, term): ... Here we defined names for the parameter in a normal way. We can call this function in different ways: .. code-block:: python payment = loan_calcualtor(16000.00, 2.5, 60) payment = load_calculator(term=60, interest = 2.5, principle=16000.00) In that last example, Python figures out what is what by the names you provide on the call. Notice that the order of the parameters is not important when we do this. Scope ***** In programming, ``scope`` refers to where physically within the code of your program certain names can be used. Each language has its own rules for setting this up, but most are similar enough that we can explain how it works in a way that will be useful in several languages (including C++, which you will take later). File scope ========== At the highest level of programming, you write code and save it in a file. The tool that processes your code has no idea what other files might be part of a larger program, so any names you use in the file must be completely defined inside the file. This is called ``file scope``. In languages like C++, which is called a ``stongly typed`` language, meaning the compiler must know exactly what kind of thing the name refers to, using a name that has not been defined in the file will generate an error. Python is not so picky. If you refer to a name you have never mentioned before in your code, Python will try to create a new container with that name. If the name is being used as though it was a function, Python will complain, since it needs to know where the function can be found before you can use it. What this means is that Python may create brand new variable containers whenever you invent a new container name, but you cannot create a new function name unless that function is defined in the current file. The import statement -------------------- In our early examples, we used an ``import`` statement to introduce a function name to Python, so we could call that function when we wanted: .. code-block:: python from math import sqrt This line tells Python that it should look in another file (named ``math``) and find a name called ``sqrt`` inside of that file. If that lookup succeeds, we can use that function later in our code: .. code-block:: python x = sqrt(5.0) Let's see Python getting annoyed. Without including the ``import`` line, try using the ``sqrt`` function: .. code-block:: text >>> x = sqrt(5.0) Traceback (most recent call last): File "", line 1, in NameError: name 'sqrt' is not defined Python had no idea what that name was all about. You have something to fix! Finding files to import ----------------------- By default, Python knows to look for files in a set of directories on your system. Most of these directories are set up when you install Python. To see the full list of places where it will look, do this: .. code-block:: python >>> import sys >>> print(sys.path) ['', 'C:\\Windows\\system32\\python32.zip', 'C:\\Python32\\DLLs', 'C:\\Python32\ \lib', 'C:\\Python32', 'C:\\Python32\\lib\\site-packages'] .. note:: The double back-slash stuff is Python dealing with Windows! Windows decided to use the back-slash to separate folder names in a path to a location on the drive. Everyone else uses forward slashes. Ask Bill why! All of the directories in this ``list`` are on the drive where Python was installed. You can add to this list if you create your own useful files, something we will look at later. Finding out what names are available when importing --------------------------------------------------- Once you import something, you can list the names available using this: .. code-block:: python >>> import math >>> dir(math) ['__doc__', '__name__', '__package__', 'acos', 'acosh', 'asin', 'asinh', 'atan', 'atan2', 'atanh', 'ceil', 'copysign', 'cos', 'cosh', 'degrees', 'e', 'erf', 'er fc', 'exp', 'expm1', 'fabs', 'factorial', 'floor', 'fmod', 'frexp', 'fsum', 'gam ma', 'hypot', 'isfinite', 'isinf', 'isnan', 'ldexp', 'lgamma', 'log', 'log10', ' log1p', 'modf', 'pi', 'pow', 'radians', 'sin', 'sinh', 'sqrt', 'tan', 'tanh', 't runc'] It appears like we have a bunch of useful "math" finctions available in that file. .. note:: We shall see later that we can ``import`` either a file, or a specially prepared folder contining a number of files. This later setup Python calls a ``package``. We can ask what kind of things these available names are by doing this: .. code-block:: python >>> type(math.sin) >>> There is that funky notation again. The name ``math`` refers to a file (or directory) containing a bunch of names we can use. The ``math.sin`` notation tells Python which of those names we are interested in. If we use another form of import, we get simpler notation: .. code-block:: python >>> from math import sin >>> type(sin) >>> Global variables ================ If you define variables outside of any function, those variables are called ``global``. We can use them whenever we like in our code. These variables are not available to code in other files unless we tell Python we want to do this explicitly. Global constants ---------------- What is a ``constant`` anyway? Well variables can vary and constants cannot. Many programming languages do not really let you create containers with values that do not vary while a program runs. Python is one of those. (C++ does let you set up such beasts). A common tactic in Python to set up something you should not modify is to yell at yourself. Name the container in all capital letters. (In email, all caps is how you yell!) The capital letters are to remind you this is a special container you should not mess with! Here is a common example: .. code-block:: python PI = 3.1415926 The capital letters warn you that you should not modify this. .. note:: Did you know that a king once got mad at PI, declaring it as an obnoxious number. So he ordered it to be given a value of 3! It really messed up the world of math! Local variables =============== If you are writing the code inside of a function, any variables you create are called ``local variables``. The ``parameters`` are also local variables, except they are given initial values by the caller. Those local variables are only visible to the code inside the function. The outside world cannot use those local names for their own purposes (except in that one special way where we can refer to the names as keywords). This is as it should be. As you write a good function, one you hope will be used in many programs, you do not want to worry that the names you are using might be in use in another part of the program. Testing scope ============= Here is an example demonstrating how this works: .. code-block:: python >>> global_variable = 'test' >>> def func1(): ... local_variable = 'batfeathers' ... print(global_variable) ... print(local_variable) ... >>> func1() test batfeathers >>> print(global_variable) test >>> print(local_variable) Traceback (most recent call last): File "", line 1, in NameError: name 'local_variable' is not defined The local_variable is not known at this point since it is not visible to the outer code. Here is an example of a setup that hides a global variable: .. code-block:: python >>> global_variable = 'test' >>> def func2(): ... global_variable = 'junk' ... print(global_variable) ... func2() junk The function used the local variable (wrongly named ``global_variable``. That effectively hides the real global variable! Suppose we really wanted to access the global variable and give it a new value: .. code-block:: python >>> global_variable = 'test' >>> def func2(): ... global global_variable ... global_variable = 'junk' ... print(global_variable) ... func2() junk print(global_variable) junk This time, we told Python that when we use the name ``global_variable`` inside the function, we really mean for it to use the original, global, name. Explaining scope another way ============================ Long ago and far away, I used a simple analogy to explain how all this scope stuff works. For Python, let's lay it out this way: Pretend that your file is laid out top to bottom and surrounded by a one way glass mirrored wall. The mirror is set up so that no one outside the file can see in. Now, surround each function inside your file by other one-way mirrored walls, set up so you can see out from inside the function, but not in from outside the function. Paint the name of the function on the outside of the wall. With this setup, scope rules are simple. From any point in your code, inside or outside of a function, you can use any name you see by looking toward the top of your code. (You cannot look below where you are at the moment.) By the way, you can see around all the functions to code above them. You can see the names of the functions above you, but cannot see inside those functions. From inside a function, you can see all names defined inside that function (local variables) and can see names defined outside the function above the function definition. If a name is defined in two places, you use the first one you find as you scan your code upward. That means if you use a name that was created inside the function, and that same name exists outside the function, you will use the local version, unless you force things as shown earlier. Phew. After a bit of writing code, you get into the pattern, and do not think about this much. Python can byte you, though, since it creates new variables whenever you assign a value to a new name.