.. _module-intro: Functions and Modules ##################### .. seealso:: Text, Chapter 8 .. wordcount:: .. vim:filetype=rst spell: We introduced Functions earlier in the course, since the concept of packaging a block of code we can use over and over is critical to programming. In this section, we will complete our discussion on functions and show how to package them into a useful form we really can use in other programs. The form is called a ``module``. Reviewing the function ********************** As you recall, a function is a block of code that we give a name. That block can have ``parameters``, which provide data for the function to work with, and it can ``return`` a value to whatever code ``called`` it. Both ``parameters`` and ``return values`` are optional. Here is a simple function, demonstrating all the parts: .. code-block:: python def adder(val1, val2): sum = val1 + val2 return sum We can ``call`` this function from any chunk of code that can ``see`` the definition. You should review the lecture on ``scope`` to make sure you can identify where the function can be called. Here is some code that will work: .. code-block:: python print("The sum of 5 and 7 is",adder(5,7)) In this statement, we are calling another function, ``print``, which is being handed two ``parameters``. The first is a string, and the second is a call to our ``adder`` function. Python will halt processing or the ``print`` function, and call the ``adder`` function. When that function returns, Python will hand that value to the ``print`` function so it can display the results. Here is what we see if we test this in the Python interpreter: .. code-block:: text >>> def adder(val1, val2): ... sum = val1 + val2 ... return sum ... >>> print("The sum of 5 and 7 is",adder(5,7)) The sum of 5 and 7 is 12 Looks like it worked! .. note:: Do any of you see something interesting about how ``print`` works? It seems that it can take a different number of parameters, depending on what we want to print. We will see how to do this later. What would happen if we ran this code: .. code-block:: text adder(7,9) Actually, Python would call the function and get the result back. However, since we are not doing anything with the returned value, it is just discarded! Returning multiple values ************************* In some cases, you might want to create a function that returns more than one value. Python supports this as long as you do things properly. Here is a (way too) simple example: .. code-block:: python def funny(): return 5,7 a,b = funny() print(a,b) Running this gives: .. code-block:: text >>> def funny(): ... return 5,7 ... >>> a,b = funny() >>> print(a,b) 5 7 How about this: .. code-block:: text >>> print("The sum of 5 and 7 is",adder(funny())) Traceback (most recent call last): File "", line 1, in TypeError: adder() takes exactly 2 arguments (1 given) Phooey! That did not work, but this does: .. code-block:: text >>> a,b = funny() >>> print("The sum of 5 and 7 is",adder(a,b)) The sum of 5 and 7 is 12 The ``funny`` function is returning multiple values, so we need to identify multiple places to put the results. Later we will see that Python supports a special kind of data container that can hold a bunch of values. It is called a ``list``, but we are not ready for that yet. Creating a module ***************** A module is a file containing variables and functions we can use in other parts of a Python program. We have been using modules whenever we ``import`` something into our program, like when we used the ``sqrt`` function contained in the standard Python ``math`` module. What we want to do now is to figure out how to build our own module. We will do this by writing a super simple version of ``math`` called ``mymath``. This module will do only two things, define a value for ``PI`` and figure out the trigonometric ``sine`` of an angle expressed in degrees. (The real ``math`` module wants angles in something called ``radians`` which are hard for people to figure out until you take a trig class! (Or work this week's lab assignment!) Start off by building a directory to hold the test code we will produce in this lecture: .. code-block:: text C:\COSC1336> mkdir modules C:\COSC1336> cd modules C:\COSC1336\modules> To build our module, we start off by creating a new file, ``mymath.py``, that looks like this: .. literalinclude:: code/mymath1.py .. note:: That last chunk of code is a common pattern in Python modules. If we ``import`` ``mymath`` into another program, Python will ignore the code at the end and you will see no output from that code. If, however, you ask Python to process the module file directly (using ``python mymath.py``), then Python will process the code t the bottom and you will see the test output you placed there. This is handy when developing the module. Let's test our new module by asking Python to run the module file: .. code-block:: text python3 mymath.py Testing the mymath module The sin of 45 should be 0.5, and we get 0 If this is not right, we have more work to do on this module! It looks like we have more work to do. Setting up a better test ======================== Since we are trying to build a function that works something like the real ``math`` module, let's use that real module to test our new one. Sounds silly, but it will work: .. literalinclude:: code/mymath2.py Look closely at how this code is set up. We have not really changed the module code, but we have changed the test code at the bottom. Testing the module will help us know when we have our module code written correctly (at least for one case). We will do a better job of testing later. Referencing module names ======================== Do you see how we referenced named from the real ``math`` module? In this example, we only ``imported`` the name of the module. In order to access the names in that module, we use ``modulename.name`` notation. In the ``math`` module we can access a better value for ``PI`` by using ``math.pi`` and get the real ``sin`` function using ``math.sin``. Remember how we used the ``sqrt`` function in the ``math`` module earlier: .. code-block:: python from math import sqrt x = sqrt(5) In this code, we used a different form of ``import``. This form specifically makes a single name(in this example) available without needing to add the module name to the front. You choose which approach you like better. There is another form we can use: .. code-block:: python from math import * x = sqrt(5) In this form, we ask Python to make all names defined in the module available to the program file with this ``import`` line. .. note:: If you want to see all the names a module has in it, you can do this from the Python interpreter: .. code-block:: text >>> import math >>> dir(math) ['__doc__', '__file__', '__name__', '__package__', 'acos', 'acosh', 'asin', 'asinh', 'atan', 'atan2', 'atanh', 'ceil', 'copysign', 'cos', 'cosh', 'degrees', 'e', 'erf', 'erfc', 'exp', 'expm1', 'fabs', 'factorial', 'floor', 'fmod', 'frexp', 'fsum', 'gamma', 'hypot', 'isfinite', 'isinf', 'isnan', 'ldexp', 'lgamma', 'log', 'log10', 'log1p', 'modf', 'pi', 'pow', 'radians', 'sin', 'sinh', 'sqrt', 'tan', 'tanh', 'trunc'] Boy, that is a lot of math stuff! How does Python find modules **************************** For our experiments, Python is finding our ``mymath`` module by looking in the current directory (where you are working) for the file ``mymath.py``. If it fails to find it, it also looks in standard places in the Python distribution on your systems. We will see how this is done a bit later in the course. The ``math`` module is a bit different, since Python, itself, is written in C++, and most of the math routines are part of that language. Other standard modules that come with Python are found in lives in ``c:\Python33\Lib``. There are good references to the tools found there. My favorite is Doug Hellmann's ``Python Module of the Week`` found at http://www.doughellmann.com/PyMOTW. We will learn a bit more about modules are we work through the rest of the course! Using your module ***************** We have enough code in ``mymath.py`` to show how to use it. Let's create a test program in ``test.py``: .. code-block:: python # test.py - a program that uses our mymath module import mymath def main(): print("Testing mymath") sinx = mymath.sin(30.0) print("The sin of 30 degrees is", sinx) main() In this version, we did not import the names, so we need to use the module.function form to call the new function. We could have done this instead: .. code-block:: python # test.py - a program that uses our mymath module from mymath import sin def main(): print("Testing mymath") sinx = sin(30.0) print("The sin of 30 degrees is", sinx) main() We need to be careful if we try to import two modules that provide the same functions. If we imported the tried to import the ``sin`` function from both ``math`` and ``mymath`` Python would not be able to figure out which is which: The solution is to use the module.name approach for one or both. This would work: .. code-block:: python # test.py - a program that uses our mymath module import mymath from math import sin def main(): print("Testing mymath") sinx = mymath.sin(30.0) print("The sin of 30 degrees is", sinx) print("Python says the sin of 30 degrees is", math.sin(30*math.pi/180.0)) main() In that last line I an converting degrees into radians (we will look at that in this week's lab) before handing the result to the ``math`` ``sin`` function. Phew! .. vim:filetype=rst spell: