Programming Digital Media

Making Python scripts

When you type "python" at the command line, you enter the Python interpreter. (We'll use the percent character (%) as the command line prompt.)
    % python 
    Python 2.3.2 (#1, Nov  8 2003, 14:11:45) 
    [GCC 2.95.3 20010315 (SuSE)] on linux2 
    Type "help", "copyright", "credits" or "license" for more information. 
    >>> 3 + 2 
    5 
    >>> 
To exit the Python interpreter, you hold down the control key and type the d character for OS X.

You can enter Python statements that contain more than one level of nesting (indentation), and whenever you return to the "top level" the statements will be executed.

    % python 
    Python 2.3.2 (#1, Nov  8 2003, 14:11:45) 
    [GCC 2.95.3 20010315 (SuSE)] on linux2 
    Type "help", "copyright", "credits" or "license" for more information. 
    >>> a = 1 
    >>> if a: 
    ...     print 'Value of a is', a 
    ... 
    Value of a is 1 
    >>> 
This is obviously impractical for a lot of Python statements, so you can put all your Python code in a file, and then process that file with Python just like you had typed it into the Python interpreter.

Here's a file called test.py:

    a = 1 
    if a: 
        print 'Value of a is', a 
Now I can evaluate the Python statements in test.py by supplying the filename as an argument to the Python interpreter:
    % python test.py 
    Value of a is 1 
    % 
Even though you could define functions running Python at the command line, it's so much easier to put them in a file that we always do it that way.

Here's a file called twopi.py contains a function definition. (We want to use the value of pi from the "math" module, so we'll import that first.)

    import math 
 
    def times_pi(value): 
        return math.pi * value 
 
    print 'Two times pi is', times_pi(2) 
Evaluating this file of Python commands (a Python script ) causes the function to be defined and then used.
    % python test2.py 
    Two times pi is 6.28318530718 
    % 
What if you want to supply arguments to the Python script? The sys module contains a variable called argv. It is an array that contains the name of the Python file and any command line arguments that followed.

For example, let's define a file called show_args.py;

    import sys 
    print sys.argv 
Now when we evaluate show_args.py with Python, we'll simply see the arguments we entered on the command line, along with the filename of the script:
    % python show_args.py 1 2 3 4 5 
    ['show_args.py', '1', '2', '3', '4', '5'] 
    % 
Notice that sys.argv is an array, so you can refer to individual commands using the [] array element syntax. You can also use any array function on sys.argv or on a part of it (using the [:] syntax).

Here's file show_args_2.py that extracts elements from the sys.argv array:

    import sys, string 
 
    print 'The arguments of %s are "%s"' % \ 
        (sys.argv[0], string.join(sys.argv[1:])) 
(The "\" character lets me continue the print command to the next line by nullifying the "newline" character that would otherwise create a new line.) We'll run this script with the same command-line arguments we used for show_args.py:
    % python show_args_2.py 1 2 3 4 5 
    The arguments of show_args_2.py are "1 2 3 4 5" 
    % 
The sys.argv array consists of strings, so you will need to convert number arguments to numbers using the conversion functions int or float. For example, let's make our pi multiplying script take an argument. We'll call it pi_mult.py:
    import math, sys 
 
    def times_pi(value): 
        return math.pi * value 
 
    value = float(sys.argv[1]) 
 
    print '%g times pi is %g' % (value, times_pi(value)) 
Now when we run it with a command-line argument, that argument is changed into a float before it is multiplied by math.pi:
    % python pi_mult.py 2 
    2 times pi is 6.28319 
    % 
But what if we forget to enter an argument on the command line? We'll get an error message (since there is no second element to the sys.argv array) and Python will stop evaluating the script file:
    % python pi_mult.py 
    Traceback (most recent call last): 
      File ``pi_mult.py'', line 6, in ? 
        value = float(sys.argv[1]) 
    IndexError: list index out of range 
    % 
By convention, Unix commands will provide a ``usage'' message if the arguments are wrong. The usage message lists descriptions of the arguments (enclosed in "<" and ">" characters) so you know what kind of arguments the command requires. We can add a check for the right number of arguments to our command, and print out the usage message if the argument count is incorrect.

We'll make a new version, called pi_mult_2.py, in which we add the argument check and the usage message:

    import math, sys 
 
    if len(sys.argv) != 2: 
        print 'Usage: pi_mult_2.py <number>' 
        sys.exit(1) 
 
    def times_pi(value): 
        return math.pi * value 
 
    value = float(sys.argv[1]) 
 
    print '%g times pi is %g' % (value, times_pi(value)) 
Now when we try to run pi_mult_2.py without arguments, the number of command line arguments is wrong; it should be 2: one for the script filename and one for the number to be multipled by pi. The usage message will be printed instead of causing a Python error:
    % python pi_mult_2.py 
    Usage: pi_mult_2.py <number> 
    % 
Usually, a Python script will contain many function definitions that make the code clearer to read and easier to understand. But what if you also want to use those function definitions in another script? You can create a Python module, a separate Python file that contains function definitions, and then import it, just like the way we imported the sys and math packages in the scripts above.

For example, we could put our function times_pi in a separate file that we'll call pi_arithmetic.py and then import it into our script. Here's pi_arithmetic.py:

    import math 
 
    def times_pi(value): 
        return math.pi * value 
So our pi multiplying script will now look like this, named pi_mult_3.py:
    import math, sys, pi_arithmetic 
 
    if len(sys.argv) != 2: 
        print 'Usage: pi_mult_3.py <number>' 
        sys.exit(1) 
 
    value = float(sys.argv[1]) 
 
    print '%g times pi is %g' % (value, pi_arithmetic.times_pi(value)) 
Notice that a function imported from a module has the module named affixed to the beginning of the function name, separated by a period character. That's why our times_pi function is called pi_arithmetic.times_pi in this script. This is just like sys.argv — it's the argv global variable in the sys module.

In Unix, a file that contains #! as the first two characters can be made executable (using chmod after the file has been created), and the rest of that first line is treated as the script or command to be executed. You can do this in Python, too. On the OS X system I'm using, the complete pathname of the Python executable is /usr/bin/python. (You can find the complete pathnam in OS X by entering the command which python at the command line for the csh and tchs shells, and with type python in the bash shell.)

I can make the previous script an executable command called pi_mult_4.py by adding #! and the complete Python path to the first line:

    #!/usr/bin/python 
 
    import math, sys, pi_arithmetic 
 
    if len(sys.argv) != 2: 
        print 'Usage: pi_mult_4.py <number>' 
        sys.exit(1) 
 
    value = float(sys.argv[1]) 
 
    print '%g times pi is %g' % (value, pi_arithmetic.times_pi(value)) 
Now when I make it executable, I can treat it like a regular command, without first typing ``python'':
    % chmod a+x pi_mult_4.py 
    % pi_mult_4.py 
    Usage: pi_mult_4.py <number> 
    % 
Once I've changed the file to be executable with chmod, that executable status remains in effect, so I only need to do it once.

You'll notice that I put the command name in the usage string. But what if I change the name of the command? Then the usage string will contain the wrong command name. Since the first element in the sys.argv array is the script filename, I can use that to make sure the names are always in sync. Here's pi_mult_5.py:

    #!/usr/bin/python 
 
    import math, sys, pi_arithmetic 
 
    if len(sys.argv) != 2: 
        print 'Usage: %s <number>' % (sys.argv[0]) 
        sys.exit(1) 
 
    value = float(sys.argv[1]) 
 
    print '%g times pi is %g' % (value, pi_arithmetic.times_pi(value)) 
Now when I enter this script without arguments, it automatically prints the correct script filename in the usage message (including the directory in which the script was located).
    % chmod a+x pi_mult_5.py 
    % pi_mult_5.py 
    Usage: ./pi_mult_5.py <number> 
    % 
These Python functions and scripts are so short that the examples may seem more complicated than the way we were doing it in the beginning. But you can see how you could develop a set of Python modules that other people could use in their scripts. If the functions are well documented, the scripts other people write using your functions could be much more sophisticated than any they could write on their own. A set of commonly used modules can also provide production standards (for example, for filename conventions) that are naturally enforced when the modules are included in everyone's scripts.