Functions

Function Definition

What's a function?

A function is a named sequence of statements that performs some useful operation

Method Definition

What's a method (well… in the context of programming, of course!)?

A method is an action that an object/thing can do. It's basically a function that's attached to an object.

Function Call Definition

What's a function call?

A function call is the statement that actually executes a function. It's just the function's name followed by parentheses, with zero or more arguments within those parentheses.

Argument Definition

What's an argument (again, in the context of functions, of course)?

An argument is a value that's passed in to a function when a function is called. It's the input!

None Definition

What's None?

None is a special value in Python that represents the absence of a value.

Function Definition

What's the syntax for defining a function?

def <function_name>(<zero_or_more_parameters>):
	<statement #1>
	<statement #2>
	.
	.
	<etc.>
	# an optional return statement
	return <some value>

Return

What does return do?

Return Continued

What's value is given back if a function doesn't return anything?

None

Parameters Example

Using the following function and function call, what are the values of both greeting and num within the function? What's the output?→

def greet_more_input(greeting, num):
	s = greeting * num
	return s

import math
print(greet_more_input("hello".upper() * 2, int(math.sqrt(4))))

And The Tricky One

Using the following function and function call, what are the values of both greeting and num within the function? What's the output?→

def greet_more_input(greeting, num):
	s = greeting * num
	return s

num = "yo"
greeting = 1
print(greet_more_input(num, greeting))

Another Parameters Example

def join_three_strings(a, b, c):
	return "%s %s %s" % (a, b, c)

What are the values of a, b and c in the above function when called as below:→

join_three_strings('one', 'two', 'three')
join_three_strings('three', 'one', 'two')
a = 'three'
b = 'two'
c = 'one'
join_three_strings(c, b, a)

Parameters Summary

Accessing a Variable Outside of a Function

What do you think will happen here? Will something be printed out? Nothing? Or an error?

s = "hello"

def greet():
	print(s)

greet()

Oddly… it's fine. Something is printed out. It seems like the function has access to s, which was declared outside of the function.

How About Variables Declared Inside a Function?

What do you think will happen here? Will something be printed out? Nothing? Or an error?

def greet():
	s = "hello"
	print(s)

print(s)

Uh-oh. Looks like you can't access variables that are inside a function.

How About Parameters?

What do you think will happen here? Will something be printed out? Nothing? Or an error?

def greet(s):
	s = s + "!"
	print(s)

greet("foo")
print(s)

This is the same as the previous slide. You can't access the parameters (by their name) that you passed in to the function from outside of the function.

And Lastly…

What do you think will happen here? Will something be printed out? Nothing? Or an error?

s = "hello"

def greet():
	s = "something else"
	print(s)


greet()
print(s)

Hmmm… it looks like names created within a function are local to that function. They don't override names in the global space.

What Does This All Mean?

A scope holds the current set of available names (variables) and the values that they point to.

a, b = 25, "something"

def foo():
	c = "bar"
	print(b)
	print(c)
# what will this print out?
foo()

Scope

Variables that are defined in your function (one indentation level in), however, are only available within your function. They are local to that function. The example below won't work.

def foo():
	c = "bar"
	return c

print(c)

Scope Continued

Furthermore, variables that are declared within a function that have the same name as a global variable are totally different variables/values! They don't overwrite the outer, global variable (there's a way to do this, though). What will this print?

c = "on toast"
def foo():
	c = "grape jelly"
	print(c)

foo()
print(c)
grape jelly
on toast

Scope Summary

Return Values

What's the difference between these two definitions of our function? What gets printed out for each code sample? →

Version #1

def join_three_strings(a, b, c):
	return "%s %s %s" % (a, b, c)

print(join_three_strings('one', 'two', 'three'))

Version #2

def join_three_strings(a, b, c):
	print("%s %s %s" % (a, b, c))

print(join_three_strings('one', 'two', 'three'))

Return Values Continued

The first definition (#1) returns a string, while the other (#2) doesn't return a value. Here are the values that are returned from functions #1 and #2:

  1. 'one two three'
  2. None

Note however, that the actual output is:

# version 1
one two three

# version 2 (the print within the function gets executed, followed by the
# print outside of the function)
one two three
None

The Return Statement

Defining a Function vs Calling a Function

What gets printed out for each version of code?

# version 1
def join_three_strings(a, b, c):
	return "%s %s %s" % (a, b, c)
# version 2
def join_three_strings(a, b, c):
	return "%s %s %s" % (a, b, c)

join_three_strings('one', 'two', 'three')
# version 3
def join_three_strings(a, b, c):
	return "%s %s %s" % (a, b, c)

print(join_three_strings('one', 'two', 'three'))

1: nothing, 2: nothing, 3: 'one two three'

Testing Programs / Functions

So far, we've tested our programs by:

Some shortcomings of manual testing

What can we do to make testing less tedious and error prone? →

Assertions for Testing

Let's get the computer to test it for us! Assertions are a way to systematically check the state of our program.

Assert Syntax

assert <some condition>, "a string representing a test"
  1. the keyword assert
  2. followed by any condition - an expression that returns True or False (usually expected == observed)
  3. followed by a comma
  4. followed by a string that represents the test

An Assertion Example

Let's use assertions to test an incorrect implementation of our function - one that doesn't have spaces between the strings. →

def join_three_strings(a, b, c):
	return "%s%s%s" % (a, b, c)

assert "ha ha ha" == join_three_strings("ha", "ha", "ha"), "should have spaces"
# output shows line number and error...
Traceback (most recent call last):
  File "foo.py", line 4, in <module>
    assert "ha ha ha" == join_three_strings("ha", "ha", "ha"), "joined string should have spaces"
AssertionError: joined string should have spaces

An Assertion Example That Actually Passes

Let's fix the program… and see what happens. →

def join_three_strings(a, b, c):
	return "%s %s %s" % (a, b, c)

assert "ha ha ha" == join_three_strings("ha", "ha", "ha"), "should have spaces"

# results in no output

Another Assertion Example

Let's use assertions to test an incorrect implementation of an absolute_value function →

def absolute_value(x):
	if x >= 0:
		return x
	else:
		return -1
assert 1 == absolute_value(-1), "absolute value of negative # is positive"
assert 1 == absolute_value(1), "absolute value of positive # is same #"
assert 0 == absolute_value(0), "absolute value of 0 is 0"

Questions About Assertions

Example Question #1

Example Usage:

>>> print(join_three_strings('one', 'two', 'three'))
one two three
>>> print(join_three_strings('1', '2', '3'))
1 2 3
>>> print(join_three_strings(1, 2, 3))
1 2 3

Example Question #1 Solution

def join_three_strings(a, b, c):
	return "%s %s %s" % (a, b, c)

assert "one two three" == join_three_strings('one', 'two', 'three'), "three strings are joined with a space"
assert "1 2 3" === join_three_strings(1, 2, 3), "joins three ints as strings"

Example Question #2

Using the function definition below, what is the value and type of a? →

def join_three_strings(a, b, c):
	return "%s %s %s" % (a, b, c)

a = join_three_strings(4, 'five', 6)