Lecture 2

Objectives for today

  1. Given a function definition, identify the name, function parameters and body
  2. Distinguish between global variables, function parameters and local variables
  3. Determine the return value of a function
  4. Implement simple functions
  5. Use docstrings (enclosed in """) to document functions for users of our code
  6. Use comments (beginning with #) to document code for ourselves and others reading our code
  7. Describe aspects of good coding style

Thonny mechanics

We use the text editor panel in Thonny to create programs that can be saved.

# An example script
x = 7
x

The line beginning with a ‘#’ is a comment and is not executed. It is a note for the reader (which will often be you!).

We run the program with the green arrow. The first time you save a new program, Python will require you to save it. Create a folder where you will store all your work for this course, and pay attention to where you saved it.

When the code runs, the result is similar to if we had typed the same code into the interpreter, except we don’t get line-by-line feedback. That is, we are not executing each line in the context of REPL (read-eval-print-loop) but just read and eval.

Functions

Creating and using your own functions is a critical part of programming. Functions help us organize our code, and encapsulate computations for reuse with different inputs (think about our discussion in Lecture 1 about doing tasks many, many times).

Functions on Python (and other languages) are similar to their mathematical counterparts (which associates values of one set, the inputs, to another set, the outputs). For example you likely have seen this notation for functions:

Let’s recreate that functionality (and function) in Python. First we figure out how to “square” a number:

>>> 3**2
9
>>> 4**2
16

then I can implement the “squaring” with a function defined in the text editor panel.

def f(x):
    return x**2

I have now defined a function named f. Note that nothing was executed. A function definition is just that, the definition of the computation a function should perform. It does not actually perform the computation. To do so we will need to invoke/call the function, either from the shell or elsewhere in the program itself.

Lets break down the function definition first:

Now let’s invoke our newly defined function.

>>> f(3)
9
>>> f(4)
16
>>> f(4,5)
Traceback (most recent call last):
  File "<pyshell>", line 1, in <module>
TypeError: f() takes 1 positional argument but 2 were given

Why did we get an error above? We tried to call f with more parameters than specified in the function definition.

What is the current value of x?

>>> x
7

Anyone surprised?

Note that parameters and any variables defined inside the function scope don’t change variables in the enclosing scope. That is, the function definition creates a new scope. A variable’s scope is the area of a program in which its value can be accessed. A function invocation creates a new frame (which the PP book calls a “namespace”) that contains instances of the function parameters and any local variables defined within the function.

This a good spot for us to pause and formalize our mental model of how memory works in Python for assignment, re-assignment and functions. Let’s use a combination of recent examples. Check out the “squaring” function in Python Tutor.

Comments

We use comments to explain our code. Comments are ignored by Python, but are useful to ourselves and others reading or using our code.

We will generally encounter two kinds of comments:

  1. Block and inline comments
  2. Docstrings

The former are comments starting with a #, included in the code itself. Everything after the # on the line will not be executed by Python. The second kind of comment, docstrings, are indicated by pairs of triple single or double quotes, can be used as “multi-line” comments, e.g.

"""
A multi-line comment that describes what a function does
and its input and output
"""

This provides a structured mechanism for documenting functions, classes, and other entities in your Python code. For example, if you have defined a docstring for a function called welcome, then typing help(welcome) at the shell will print out the docstring for that function.

Function writing: A recipe

  1. Create a specification of the function along with some examples of the function call, e.g.,

    “Return the string composed of first parameter, a space, and the second parameter”

    >>> glue_strings('John', 'Smith')
    'John Smith'
    
  2. Define the type contract, i.e., the types of parameters the function accepts and the type it returns, e.g.,

    (str, str) -> str

  3. Write the header

    def glue_strings(first, second):
    
  4. Implement the body

    def glue_strings(first, second):
        return first + " " + second
    

Here is a more complex example for compound interest. You may have seen the formula for computing the value of saving yearly interest as

where P is the principal, r is the rate and t is the time in years. How could we implement this in Python? (Show code)

def compound_interest(principal, rate, years):
    """
    Calculates investment value with interest compounded yearly,
    given parameters principal, rate, years
    """
    amount = principal * ((1 + rate) ** years)
    return amount

Note the function body can be multiple lines. The comment (enclosed in pairs of triple quotes) is used to document (for myself and other users of my code) what the function does. Here we also define a “local variable” amount. Much like parameters, local variables exist only within the scope of the function body.

Practice with functions

Here are more simple function examples.

The Peer instruction questions today give you practice with function definition vs function call, parameters, local variables, and return values.

Peer instruction questions (credit) (Section A, Section B)

Good coding practice

A little about style

We should always aim for well-chosen, that is self-documenting, names for functions, variables, etc., and informative (but pithy) comments. The quality of your coding style will also be a factor in your programming assignment grades. Code should be written to be read not just executed! And the most likely person to read your code is “future you”, so think of good style as being kind to future you.

There is no one definition of “good style”, but in general it is combination of efficiency, readability and maintainability (that is, can your code be readily updated/enhanced by you or others).

We will discuss style more as we go, but it is never too early to develop good habits. In general we will follow the PEP 8 style guide.

Being DRY

You will often hear the acronym ‘DRY’ for Don’t Repeat Yourself, often as used as a verb. For example, “DRY it up”. The goal is eliminate duplicated code. Why? Duplicated code makes it harder to read and to update your code (to correct a mistake you might have to change a computation in many places!). Often the way to DRY it up is encapsulate duplicated code as a function!

Comments

In every lab we will ask you to comment your code. The purpose of the comments is to describe what your code is doing, not to describe the code itself; you can assume the reader knows Python. But the reader doesn’t necessarily know what you are trying to do.

Good comments are important for readability and maintainability of your code.

Summary

  1. Functions
  2. Comments
  3. Good coding practice