CS 2120: Class #3



  • Probably the most important things in a programming language.

  • If you’re going to pay attention only once this term... now’s the time.

  • Script/program files are a nice way to organize many statements

  • You eventually find yourself writing the same series of statements over and over
    • (or cutting and pasting in your editor)
  • There’s gotta’ be a better way!

  • We want a way to group together sequence of statements that we frequently reuse

  • In Python, we do this with a function. Here’s one now:

    def my_function(a_parameter):
            b = a_parameter * 2
            print b
  • Once you’ve defined a function, you can call it from the Python interpreter in exactly the same way you’d call a built-in function like print.

  • So let’s use our function:
    >>> my_function(2)
    >>> my_function(7)
    >>> my_function("Friends")
  • When we call my_function, the Python interpreter executes the statements that make up the function, in order.

  • Functions make code easy to reuse, and easy to read. More importantly they facilitate abstraction.


Quick Activity

Write your own function to do something with math.

Function Parameters

  • Note carefully the parameter (a_parameter) in the definition of my_function. When you are defining a function, you want the function to be very general. You want it to work with any possible parameter that someone might want to give it.

  • Imagine an add_print(a,b) function that adds two numbers and prints the result. You want it to add any two numbers, not just two specific numbers. A function add_print(3,5) that can only add 3 to 5 wouldn’t be very useful. It would only ever print 8. So we introduce parameters.

  • Parameters are like variables. When you call the function, the first thing that happens is the parameter values get set. Let’s go ahead and build our add_print function:

    def add_print(a,b):
        print a+b
  • Now that the function is defined, we can call it. Like this:

    >>> add_print(5,2)
  • The call add_print(5,2) gets handled like this:

    • The interpreter checks to see if it knows about a function named add_print
      • We just defined add_print, so it does.
    • When we defined it, we told the interpreter it should have two parameters: a and b.

    • The interpreter now takes the values in the call (in this case, 5 and 2) and assigns those values to the function parameters a and b.

      • In other words, the first thing the interpreter does in this case is set a = 5 and b = 2
    • Then the interpreter executes the body of the function, with the parameters having their new values.

    • What happens if we don’t give it enough, or too many parameters?

Abstraction: first steps

  • Why is abstraction important?


Write down a “program” to make spaghetti (not in python, like on paper). You can only use the following statements:

  • locate [object]
  • grasp [limb]
  • release [limb]
  • move_limb_to [location]
  • wait [time in seconds]

Assume you start from a clean, empty, kitchen.


Write down a “program” to make spaghetti (not in python, like on paper). You can use plain English prose and assume you are addressing a human being.

  • You’ve now written programs at two levels of abstraction. Which was easier?

  • Functions allow us to build towers of abstraction.
    • A low level function might worry about how to set the individual pixels of the display to show the letter A .
    • Would you want to cut-and-paste that code every time you needed to print A?
    • Instead, we have a function called print that hides all those messy details from us.
    • We call print, print calls other functions, which call other functions, which call other functions...
  • Without organizing things into levels of abstraction writing complex software would be impossibly difficult.

  • Forget programming. In the rest of your scientific life, learning to think in terms of levels of abstraction is a hugely important skill.

    • e.g., if you’re a Neuroscientist doing an fMRI experiment... should you be worrying about the state of a particular type of serotonergic receptor in a single neuron in the cortex?

Back to concrete things...

  • The general format for defining a function is:

    def function_name(p1,p2,p3,p4, ... ):
            statement 1
            statement 2
            statement m
  • function_name is... the name of the function

  • p1, p2 , etc. are called the parameters, you can have as many as you like

  • You tell Python which statements make up the body of the function by using indentation.
    • This is a somewhat unique feature of Python. Many other languages use pairs like begin, end , do, done or {, } to delimit the body of a function.

    • Using whitespace might take some getting used to, but there is a huge benefit: your code “runs the way it looks”. If you’ve ever wasted time counting } s... you know what I mean.

      • However, white spaces can be uncomfortable too... sorry... deal with it...


Write a function catstr which takes two strings as parameters and then prints out the concatenation of the strings. e.g., if I call catstr('Hello ','world!') it will print Hello world!.


Now write a function crosscat that will take four strings and print out the concatenation of the first and third string, and then, on a new line, the concatenation of the second and fourth string. BUT: your function isn’t allowed to use a print statement! You can, however, use your catstr function.

Execution Flow

  • The Python interpreter executes one statement at a time

  • To make sense of programs, we need to know which instruction gets executed when.

  • In a program, the statements get executed in the order in which they appear in the program, top to bottom of the file.
    • Later, we’ll learn how to jump around.
  • What happens when a function gets called? Let’s trace through this program:

    def dostuff(a,b):
            c = b*2
            d = (a+4)*2
            c = d + c
            return c
    x = 2
    y = 3
    print dostuff(x,y)
    print "where am I?"
  • So what happens is:
    • The interperter makes a note of where the function is being called from.
    • The flow of execution passes to the function
    • The interpreter executes each statement in the function, in order.
    • at the end of the function, control returns to the point from which the function was called.

Function values

  • Notice how dostuff ended with a return statement.

  • The return statement tells Python: “return this value to whoever called this function”

  • With return, functions evaluate into values.

  • Consider:
    >>> print dostuff(2,2)
    >>> print dostuff(4,4)
    >>> print dostuff(2,2) + dostuff(4,4)
  • When the interpreter hits a dostuff, it goes and does stuff (executes the function).

  • Because that function ends in a return, when execution flow comes back to the calling program, the call to dostuff gets replaced with whatever value got return ed.

Quick Activity

  • Write a function nostuff(a,b) which is identical to dostuff(a,b) except it does not contain a return statement.

  • What happens when you try this?
    >>> print nostuff(2,2)
  • What happens when you try this?
    >>> print dostuff(2,2)


Write a function compmag(r,m) to compute, and return, the magnitude of a complex number. It should take the real component of the number as parameter r and the imaginary component as m.

Remember that | r + mi | = sqrt(r^2 + m^2). Say, does Python have a square root function? How would you find it?


  • Python functions can be composed just like mathematical functions.

  • We’ve already seen print composed with dostuff

  • We can nest functions, too:
    >>> dostuff(dostuff(2,2),dostuff(2,2))
  • If you get confused tracing nested functions, just remember:
    • Functions get evaluted and turned into values
    • Find a function you can evaluate
    • Evaluate it
    • Cross out the function and replace it with the value it returns
    • Keep doing this until you’re down to one value.


Figure out the value of dostuff(dostuff(2,2), (dostuff(2,2) + dostuff(4,4)) ) using only pen and paper. No computers!


Figure out the value of nostuff(nostuff(2,2), (nostuff(2,2) + nostuff(4,4)) ) using only pen and paper. No computers!

Variable scope (not the mouthwash)

  • If you set a variable inside a function, it is local to that function.

  • No other function can see a function’s local variables. They are local. Consider this code:

    def domore(a,b):
            c = 2*a + b
            return c
  • What happens if I do this:
    >>> print domore(4,4)
    >>> print c
    NameError: name 'c' is not defined
  • Error! But c is defined in domore! Why did we get an error?

  • Moral of the story: variables have scope. This can actually be a suprisingly delicate concept and we’ll come back to it later.

Optional parameters for functions

  • Sometimes you want a function to have an optional parameter, with a pre-specified default value.

  • This is done very easily:

    def my_function(a,b,c=3):
  • When you call my_function(5,12), a will have value 5, b value 12 and c value 3.

  • Because we specified a default value for c, we don’t have to provide one when we call the function.

  • If we want to override the default though, we can: my_function(4,3,2).

  • A reasonable example:

    def time_to_fall(d, a = 9.807):
       return math.sqrt(2*d/a)


  • Another practical matter: sometimes you want to make a big library of functions. Maybe related to analysis data from your research.

  • You’d like to access some of those functions from another program that you’re writing.

  • If you put your functions in a file called ‘myfuncs.py’, you can import them into another program like this:
    >>> from myfuncs import *
  • (The * here means everything)

  • You could also use:
    >>> import myfuncs
  • BUT, this adds a namespace. To access a function called dostuff in the file myfunc after this style of import, you’d have to type:

    >>> myfuncs.dostuff(...)

Import — MORE

  • Can also import other people’s functions

  • >>> import math
  • >>> import numpy

For next class