Constants and Functions

Jump to TL;DR (summary)

Constants

Starting with some code from the introduction that draws a square

import turtle
turtle.forward(150)
turtle.left(90)
turtle.forward(150)
turtle.left(90)
turtle.forward(150)
turtle.left(90)
turtle.forward(150)
turtle.done()

We can see that our code is very repetitive. We call turtle.forward 4 times and turtle.left 3 times. We also give them all the same values to the functions over and over. We can start by getting rid of the same value over and over and over again using constants, much like we have with scheme.

Constants in python are defined with all caps names and our constants would look something like

SIZE = 150
ANGLE = 90

Just like in scheme, we use all caps for our constants to show that they will never change. If we wanted to have a constant with spaces in its name, we would write it like such

THIS_IS_MY_CONSTANT = "hello"

And now that we have these constants defined, we can replace the numbers in our code with these constants

turtle.forward(SIZE)
turtle.left(ANGLE)
turtle.forward(SIZE)
turtle.left(ANGLE)
turtle.forward(SIZE)
turtle.left(ANGLE)
turtle.forward(SIZE)
turtle.done()

Now we just have one spot to change our size and angle if we want to draw a different shape or to draw a different size. The code is still repetitive though. If we wanted to draw 2 squares, we would have 14 lines of code. bruh :(. Instead, we can use

Functions

Functions, which you are hopefully familiar with from scheme, are tools that allow us to group blocks of code together which we can run and even give values to. In python, we follow the same steps as in scheme when defining a function.

  • Signature
  • Purpose/Description
  • Tests
  • Actual Function Body

Ok, we do all of them except tests. I know, it is so sad. They were my favorite part of programming too.

Now that we have that out of the way, we can move our code into a function. We start with the signature. Since this function is drawing a square, I think we should name it draw_triangle. No? Ok... I guess we can call it draw_square. And we want our function to take in the size of the square so that we can use it for small and big squares. Our function's signature would look something like

# draw_square : Number -> Image

Although, yes, this function does not return an image per se, we will still put it down as such, to clarify that the function will create an image on the screen. We will get into what it means for a function to actually return a value later.

And now we can add our very, very complex description or purpose statement whatever you prefer.

# draw_square : Number -> Image
# Draws a square of the given size

Now that we have the documentation out of the way, we can move onto the actual body of the function. The function will basically be a copy and paste of our above code with some parts added to tell python that it is a function.

# draw_square : Number -> Image
# Draws a square of the given size
def draw_square(size):
    turtle.forward(size)
    turtle.left(ANGLE)
    turtle.forward(size)
    turtle.left(ANGLE)
    turtle.forward(size)
    turtle.left(ANGLE)
    turtle.forward(size)

Here is a breakdown of the parts of a python function

def # Tells python about the function definition
draw_square # The name of our function
(size) # Tells python that our function takes in one input, and we name it size
: # Tells python that everything after this is the content of the function

If we were to take in 2 inputs, we would put a comma in between them in the parenthesis

def two_inputs(a, b):

Note that in the function, we have replaced SIZE with size since we are no longer using the constant size that we defined before and instead are allowing us to specify the size of the square when we call the function

Python is what is called a "whitespace language" meaning that what the code means is heavily dependant on the whitespace in the code.

Whitespace is a character that you can not see such as space or tab

because of this, the indentation of your code matters. To specify that the code above is part of the function, it needed to be indented one time. If we now wanted to write more code after, but not inside of the function, we would have to un-indent the code which would look like so

# draw_square : Number -> Image
# Draws a square of the given size
def draw_square(size):
    turtle.forward(size)
    turtle.left(ANGLE)
    turtle.forward(size)
    turtle.left(ANGLE)
    turtle.forward(size)
    turtle.left(ANGLE)
    turtle.forward(size)

turtle.done() # This code is now not inside of the above function

To use our function that we have defined, we can now call it as such

draw_square(150)
draw_square(300)
turtle.done()

Unlike in scheme where you can call/use a function from anywhere, in python, you must define a function before you are able to use it, much like variables or constants. So the above code would have to appear below the function we have made

Designing a square root function

Now that we have designed a simple function, we get to do another one! yay!

This function we will define will find the square root of an inputted value

Lets walk down the steps of function definitions

  • Signature
# square_root : Number -> Number
  • Purpose/Description
# Find the square root of a Number
  • Tests

No tests needed

  • Actual Function Body

To make the function, we need to remember how to take a square root. If you remember from math class, a square root is the same as raising something to the power of 1/2, we can express that in python with the following

def square_root(n):
    n**(1/2)

If you remember back to the introduction, the ** operator raises the number on the left to the exponent on the right, so in this case, we are raising n to the power of 1/2 where n is the inputted value to our function

Unlike scheme, python does not return anything from the function unless you explicitly tell it to return the value. That is because in python, you can run multiple lines of code per function where as scheme had you nest all of the function calls inside each other.

To be able to return the value, we need to add return before the value we want to return. Below is the fully complete function

# square_root : Number -> Number
# Find the square root of a Number
def square_root(n):
    return n**(1/2) # n^.5 = sqrt(n)

Now we can use this function

square_root(100) # = 10.0

And we can assign the result to a variable as well

x = square_root(25) # = 5.0

But how to we show out this information to us? Im not too sure, but I have heard a bit about

Printing Values to the Console

Python provides a function to us called print that will print out all of its arguments. This function is a bit different than the ones that we have wrote so far in that it can take 0 or 100 inputs. Print will print out all of the inputs given to it. We use it like any other function that we have before

print("hello")
print("hello", 7, False, 32)
print(square_root(100))

Watch out! Although python is very google-able, there are 2 different versions of python: Python 2 and Python 3. There are a few syntax and other changes between these versions, one of these is the layout of the print function. In Python 2, you would write print "hello" to print the string hello. In the newer Python 3 that we use, we need to put parenthesis around the inputs to all functions, so we would write print("hello"). If you ever see the old way of printing, look around for a newer source since the source you are using may be outdated, or just replace the old style of prints with the new style.

Returning Nothing

When writing a function, we may want to not return anything from it since we might print to the console or draw using a turtle. In that case, we need to mark the function as returning (void).

We will use a simple function that prints a greeting to the name that is given ie. print_greeting("Zach") would print Hello Zach to the console as an example for this.

The function signature and usage would look as follows

# print_greeting : String -> (void)
# Prints a greeting to the given person
def print_greeting(name):
    print("Hello " + name)

print_greeting("Alice") # prints "Hello Alice"

Make note of the un-indentation of the last line, meaning that it is not inside of the function above it.

The main change we have made is the return type being (void) to tell us that this function returns nothing.

Since this function returns nothing, we cannot get a value from it/assign it to a variable

greeting = print_greeting("Alice")
print(greeting) # prints "None"

If we instead wanted to be able to use and manipulate the greeting before printing it out, we would need to make our function return the greeting instead of printing it. Since it now returns the greeting instead of printing it, we can rename it to get_greeting.

# get_greeting : String -> String
# Get a greeting to the given person
def get_greeting(name):
    return "Hello " + name

get_greeting("Alice") # = "Hello Alice"

Note that the last line does not print anything to the console, and instead just will return the greeting for use to use later in our program. We can use the value like so

greeting = get_greeting("Alice")
print(greeting + "'s dog") # prints "Hello Alice's dog"

Mutation

This is a very common feature that scheme intentionally did not let us use, and that is because it has a lot of quirks, but can also be very useful.

We can define variables like such

x = 7

And in python, we can now change that variable.

x = 7
x = 10
print(x)

So in the console, we will see the number 10, since x was changed from 7 to 10

Sometimes we don't want to change a variable to an exact value, but instead change it relatively such as adding 5 to it. Python lets us to so in 2 ways. The obvious way would be to do something like

x = x + 3

But python gives us the tool to do it a bit more concisely using

x += 3

Python lets us do this with all of the operators gone over in the introduction, here is an example of a few of them.

x -= 3 # subtracts 3 from the value x
x *= 3 # multiplies 3 to the value x
x /= 3 # divides 3 from the value x

Weirdly, we can use these with strings too

s = "Hello"
s += "!" # Adds a "!" at the end of the string s
s *= 3 # Duplicates the string s 3 times
print(s) # prints "Hello!Hello!Hello!"

Mutation Madness

Say we have this function

# change_number : Number -> (void)
# Add 3 to the given Number
def change_number(x):
    x += 3

And this code that deals with the function

x = 10
change_number(x)
print(x)

What do you think this would print?

Click for the answer

This would print out 10.

The x in the function and the x in the code outside of the function are not the same variable. Yes they have the same name, but the one inside of the function could just as well be named n or jeff.

The variables that are passed to a function cannot be modified by the function. But they can modify the data in the function themselves.

If we wanted to actually give the changed value back to the code that called this function, we would need to return it back out.

# change_number : Number -> Number
# Add 3 to the given Number
def change_number(x):
    x += 3
    return x

And when we use the function, we would need to do the following:

x = 10
x = change_number(x) # Move the changed value of x into x so we can use it
print(x)

Exercise

Given these functions

def change1(x):
    x += 3
    return x

def change2(a):
    a -= 5
    return a

def change3(s):
    s *= 2

And this code that uses the above functions

a = 7
b = "hello"
c = 10

b = change1(a)
change3(b)
c = change2(c)

print(a, b, c)

What would be printed to the console

Solution
# Answer: a: 7, b: 10, c: 5

a = 7
b = "hello"
c = 10

b = change1(a) # = a + 3
change3(b) # NO CHANGE
c = change2(c) # = c - 5

# a: UNCHANGED  = 7
# b: a + 3      = 10
# c: c - 5      = 5
print(a, b, c)

Too Long; Didn't Read

If you want an abridged version, you can look at just the code that I have gone over here

  • Functions are defined with the def keyword and look something like
# square_root : Number -> Number
# Find the square root of a Number
def square_root(n):
    return n**(1/2) # n^.5 = sqrt(n)
  • Similarly to scheme, CONSTANTS_LOOK_LIKE_THIS and variables_look_like_this the only difference is the underscores _ versus the hyphen -
  • Still follow the help-sheet's rules for defining functions, just skip the test step for now
  • Variable's values can be changed
x = 7
x = 10
print(x)
  • Inside of a function, variables do not modify the variables that were passed in (see Mutation Madness)