Table of Contents

  • 1  Variables

  • 2  Naming Variables

  • 3  Data Types

  • 4  Strings

  • 5  Comparison & Logic

  • 6  Conditionals

  • 7  Data Types - Recap

  • 8  Exercise 1.1

      • 8.0.1  Bonus exercises

  • 9  Functions

  • 10  Built-In Functions

  • 11  Defining Functions

  • 12  Exercise 1.2

      • 12.0.1  Bonus exercises

  • 13  Methods

  • 14  Lists

    • 14.1  Slices

    • 14.2  Modifying a List

  • 15  Loops

  • 16  Exercise 1.3

      • 16.0.1  Bonus exercises

  • 17  Dictionaries

  • 18  Exercise 1.4

      • 18.0.1  Bonus exercises

Variables

Use = to assign a value to a variable.

[ ]:
x = 5

There are two options for displaying the value of x:

[ ]:
x

Or use the print function:

[ ]:
print(x)

A cell can contain multiple lines of code:

[ ]:
length = 10
width = 2.5

Text prefaced with # is a comment, which is a note to people reading the code and is ignored by Python.

[ ]:
# Calculate area of rectangle
area = length * width

# Display the area
area

What if we change the value of a variable? - Currently, length is 10 and width is 2.5

[ ]:
width = 3.5
[ ]:
area
[ ]:
length * width

If we go up to the previous cell that calculated the rectangle area, what is the output?

  • The calculated area corresponds to the new value of width (3.5) even though the cell immediately above shows width being assigned a value of 2.5

  • Jupyter notebooks are nonlinear—a collection of cells that can be run out of order

    • Need to be cautious when running cells out of order!

    • It’s good practice to periodically restart the kernel and run all cells in order

Naming Variables

  • Can only use letters, numbers and underscore _

  • Can’t start with a number

    • day1 is ok, but 1day will cause an error

  • Use descriptive names and separate words with an underscore (e.g. first_name, land_area)

[ ]:
avogadro = 6.02e23

Try typing av and then press Tab and see what happens

[ ]:

Auto-complete! One of the very handy features of IPython.

Data Types

So far we’ve worked two types of numbers in Python: - int (integer) - float (decimal numbers)

[ ]:
n = 42
pi = 3.14159

The type of a variable is determined by the value assigned to it - We can use the function type to find out the type

[ ]:
type(pi)
[ ]:
type(n)

Strings

  • Text data is of the type str, which stands for string: a sequence of characters enclosed in single or double quotation marks

  • The characters in a string can be letters, numbers, punctuation, symbols, etc.

[ ]:
today = "Tuesday"
tomorrow = "Wednesday"
[ ]:
type(today)

Strings can be concatenated with the + operator:

[ ]:
statement = "Today is " + today
statement
[ ]:

Comparison & Logic

We can also use comparison and logic operators (<, >, ==, !=, <=, >=, and, or, not), which will return either True or False.

[ ]:
year = 2018
year
[ ]:
year > 2000
[ ]:
year == 1990

Note the double equals sign above - Single equals sign = is for assigning a value to a variable - Double equals sign == is for checking if values are equal to each other

not reverses the outcome from a comparison.

[ ]:
not year == 1990

and checks if both comparisons are True.

[ ]:
3 < 4 and 5 > 10

or checks if at least one of the comparisons are True. - Below we’re also taking the output of the comparison and assigning it to a variable result

[ ]:
result = 3 < 4 or 5 > 10
result

The resulting True or False value is of type bool, short for Boolean.

[ ]:
type(result)

As we’ll see later, Boolean comparisons like these are important when extracting specific values from a larger set of values.

Conditionals

Another common use of Boolean comparison is with a conditional statement, where the code after the comparison only is executed if the comparison is True.

[ ]:
a = 12

if a < 10:
    print("a is less than 10")
else:
    print("a is greater than or equal to 10")
  • Note the indented line after each colon :

  • Indentation is very important in Python!

  • In this example, indentation indicates which code is executed if the conditional statement is True and which code is executed if the conditional is not True

Data Types - Recap

  • Integer (int — whole numbers)

  • Float (float — numbers containing decimals)

  • String (str — words, sentences, other text)

  • Boolean (boolTrue or False)

  • and many others!

Exercise 1.1

a) Create a variable y with a value of 7

The cell below is your first look at an autograded Jupyter notebook cell. The ideas is that, until you replace the contents of that cell with your answer python will raise an error called “NotImplemented”. Go ahead and remove replace that statement with your own answer.

[ ]:
# YOUR CODE HERE
raise NotImplementedError()

You statement should pass the following test:

[ ]:
from numpy.testing import assert_almost_equal

assert_almost_equal(y, 7, decimal=1)

b) Create a variable two_y with a value of 2 * y

[ ]:
# YOUR CODE HERE
raise NotImplementedError()

c) Change the value of y to 9. What is the value of two_y now, 14 or 18?

[ ]:
# YOUR CODE HERE
raise NotImplementedError()

Bonus exercises

d) Concatenating strings and integers

Assign a value of 2018 to the variable year. What happens if you use the + operator to add the string 'The year is ' with the variable year, as shown below? (You’ll need to copy the code below into a cell in your notebook in order to run it.)

observation = 'The year is ' + year

You’ll find you get an error message because Python doesn’t know how to add an integer and a string together.

You can use typecasting to change the type of an object in Python. For example, you can convert the integer 2018 to the string '2018' using the function str(). Try running the code below in your notebook and then display the value of the variable observation:

observation = 'The year is ' + str(year)
  • Now create a variable num_bottles with a value of 99 (integer) and a variable song_words with a value of ' bottles of beer on the wall'.

  • Use the + operator and the str() function to concatenate num_bottles with song_words and confirm that it produces the output '99 bottles of beer on the wall'.

e) Other mathematical operators

What are the results of the following operations?

  • 9 % 3

  • 10 % 3

  • 11 % 3

How about the above operations with the % operator replaced with the // operator (e.g. 9 // 3)? Based on these results, what do you think the % and // operators do? (Hint).

f) Conditional statements

Create a conditional statement that prints 'Leap year' if the value of the variable year is divisible by 4, and 'Not a leap year' otherwise. Test your conditional statement by assigning different values to year and re-running the code.

Functions

  • Functions are bundles of code that can be re-used

  • Python has many handy built-in functions, for example:

    • print

    • type

    • round

    • abs

    • sum

  • You can also define your own functions

Built-In Functions

[ ]:
round(3.14159)

Auto-complete works for functions too. Try typing ro followed by Tab.

[ ]:

  • Inputs to a function are called arguments

  • Arguments can be required or optional

  • Functions return an output value or a None output

    • round(3.14159) returns 3

    • print('hello') returns None

Use ? after the function name to see the documentation. (remove the # to try this)

[ ]:
# round?
  • Python documentation is sometimes helpful and sometimes totally confusing, especially when you’re first learning the language

  • Other online resources can be much more helpful and easier to understand, for example:

    • Tutorials

    • Blog posts with examples and code

    • Questions and answers posted on Stack Overflow and other forums

  • Think of Google search as an essential component in your Python workflow!

[ ]:
round(3.14159, 2)

When we call a function, what do we do with its output?

  1. We can simply display the output on the screen to see what it looks like:

[ ]:
round(1 / 3, 5)
  1. We can assign it to a variable so that we can use it elsewhere:

[ ]:
one_third = round(1 / 3, 5)
one_third

Defining Functions

  • We can define functions anywhere in our code using the def keyword

  • The contents of the function are an indented block

  • Use the return keyword to return an output

[ ]:
def fraction(num, denom):
    result = num / denom
    return result

We can call our function by supplying input values in the same order they were defined above (numerator followed by denominator).

  • These inputs are referred to as positional arguments.

[ ]:
fraction(30, 7)

Or, we can use use the labels num and denom to specify input values in any order we want in our function call. - These inputs are referred to as keyword arguments.

Pro Tip: Auto-complete works for keyword arguments too!

[ ]:
fraction(denom=7, num=30)

Some terminology:

  • When we define a function, the inputs defined inside parentheses are called parameters

  • When we call a function, the input values we pass to the function are called arguments

We can also specify default values for inputs - When defining the function, these default input values must be listed after any inputs that don’t have default values

[ ]:
def fraction_rounded(num, denom, ndigits=2):
    result = round(num / denom, ndigits)
    return result
[ ]:
fraction_rounded(denom=7, num=30)
[ ]:
fraction_rounded(denom=7, num=30, ndigits=6)
  • It is helpful to include a description of the function.

  • There is a special syntax for this in Python that makes sure that the message shows up in the help message.

[ ]:
def fraction_rounded(num, denom, ndigits=2):
    """Return the fraction num/denom rounded to ndigits"""
    result = round(num / denom, ndigits)
    return result

The string between the """ is called the docstring and is shown in the help message, so it is important to write a clear description of the function here.

As with built-in functions, ? can be used to get help for user defined functions.

[ ]:
# fraction_rounded?

The entire source code of a function can be displayed with ?? (if it is short enough)

[ ]:
# fraction_rounded??

Related functions can be bundled together in libraries (modules/packages)

Exercise 1.2

a) Write a function which takes an input of temperature in degrees Celsius, and returns an output of temperature in degrees Fahrenheit (multiply the temperature in degrees C by 1.8 and add 32)

[ ]:
# YOUR CODE HERE
raise NotImplementedError()
  1. Use this function to convert 15 C to Fahrenheit, and assign the output to a variable temp_F

[ ]:
# YOUR CODE HERE
raise NotImplementedError()

# You code sould pass the following test:
from numpy.testing import assert_almost_equal

assert_almost_equal(temp_F, 59.0, decimal=1)
print(convert_celsius(30))
[ ]:
# This cell contains a hiddent test that calls your function with another value for temp_celsius

Bonus exercises

c) Create a new function based on your function from part (a), which converts the temperature from Celsius to Fahrenheit and also prints a message about the temperature: - If the input temperature is less than 10 C, it prints 'Chilly!', - Otherwise, it prints 'OK'.

Call the function with different input temperatures to test it.

d) Modify your function from part (c) so that it accepts a second input called temp_chilly, which is used for the “chilly” temperature threshold (instead of always using 10 C). Test the function by calling it with various values for each of the two inputs.

Methods

A method is a function that is “attached” to a Python object - e.g. String methods: upper, lower, capitalize, replace, strip, and many more - We use dot notation to access an object’s methods

Note: Methods are a feature of object oriented programming, which we won’t delve into here, but for more details, check out this tutorial.

[ ]:
message = "Hello world."
message.upper()

Did our variable message change?

[ ]:
message

Auto-complete works on methods too - Try typing message. (including the dot at the end) and then press Tab and see what happens

[ ]:

[ ]:
new_message = message.replace("world", "universe")
new_message

Is the replace method case sensitive?

[ ]:
message.replace("hello", "hi")

Methods can be chained together:

[ ]:
shouting = message.upper().replace(".", "!!!")
shouting

Lists

Lists are a common data structure to hold an ordered sequence of elements.

[ ]:
planets = ["Mercury", "Venus", "Earth", "Mars", "Jupiter"]
planets
[ ]:
prime_numbers = [2, 3, 5, 7, 11, 13]
prime_numbers
[ ]:
different_data_types = [3.14, "Canada", 62 / 7, 1729]
different_data_types

Lists can be added together with the + operator:

[ ]:
planets + prime_numbers

You can use the function len to get the length of a list

[ ]:
len(planets)

Each element in a list can be accessed by an index. - Note that Python indexes start with 0 instead of 1.

image0

[ ]:
planets[0]
[ ]:
planets[3]

How can we access the element 'Earth' from the list planets?

[ ]:

How can we access the last element in the list planets?

[ ]:

What happens if we try to access planets[10]?

[ ]:

Notice that for indexing we use square brackets [] whereas for function calls we use parentheses (). This is one of the (many) features that makes Python code easy to read.

thing1 = something(5)
thing2 = something_else[5]

Without having any other information about the code above, we already know that something is a function, whereas something_else is a data structure (such as a list). > Of course, it’s good practice to give your functions and variables descriptive names, unlike the cryptic code above!

Multiple elements in a list can be selected via slicing.

image0

[ ]:
planets[1:3]

Slicing is inclusive of the start of the range and exclusive of the end, so the slice planets[1:3] includes all elements from index 1 up to—but not including—index 3

Either the start or the end number of the range can be excluded to include all items to the beginning or end of the list, respectively.

[ ]:
planets[:2]
  • Lists are modified “in place”

[ ]:
planets[3] = "MARS!"
planets

We can use methods such as append and remove to add or remove items from a list

  • As with string methods, list methods use dot notation

[ ]:
planets.append("Uranus")
planets

Loops

A loop can be used to access the elements in a list or other Python data structure one at a time.

[ ]:
for planet in planets:
    print(planet)

The variable planet is re-created for every iteration in the loop until the list planets has been exhausted.

Operations can be performed on elements inside loops:

[ ]:
for planet in planets:
    print("I live on " + planet)

Exercise 1.3

a) Create a list called numbers containing the values: 7, -4, 1e8, 8.3, 287, 29, -2.5, 9.8

b) Display a slice of numbers containing the last 3 items

c) Loop over the items in numbers to print out each item multiplied by 10

Bonus exercises

d) Creating a sequence of integers

The built-in function range produces a sequence of integers from a specified start (inclusive) to stop (exclusive). This can be helpful when we want to loop over a long sequence of numbers without manually creating a list of those numbers. Try copying the code below into your notebook and running it to see how it works:

for num in range(7, 16):
    print(num)

Using the code above as a guide, and also using the techniques from previous bonus exercise 1.1(d) for concatenating an integer variable with a string: - Create a for loop that loops over a sequence of years from 1938 up to and including 2012, and for each year in the sequence, prints 'The year is ' plus the current value of the year.

In a later bonus exercise, we’ll use this loop for downloading Environment Canada weather data!

e) Negative indices

  • Display the item at index -1 of your list numbers from part (a).

  • How about index -2, -3, etc?

  • What do you conclude about negative indices?

f) More about slices

  • Display the slices numbers[::2], numbers[1:6:2], and numbers[::-1].

  • What do you conclude about the number after the second colon?

  • How would you slice numbers to display every 3rd item starting from index 1?

g) Indexing with strings

Create a variable city with a value of 'Vancouver'. - Display the values of city[0] and city[3:7]. - What do you conclude about indexing string variables? - What happens if you try to assign city[6] = 'Q'?

Dictionaries

A dictionary is a data structure that holds pairs of objects - keys and values.

[ ]:
fruit_colors = {"banana": "yellow", "strawberry": "red"}
fruit_colors
  • Dictionaries work a lot like lists - except that they are indexed with keys.

  • Think about a key as a unique identifier for a set of values in the dictionary.

  • Keys can only have particular types - they have to be “hashable”.

    • Strings and numeric types are acceptable, but lists aren’t.

[ ]:
fruit_colors["banana"]

To add an item to the dictionary, a value is assigned to a new dictionary key.

[ ]:
fruit_colors["apple"] = "green"
fruit_colors

Using loops with dictionaries iterates over the keys by default.

[ ]:
for fruit in fruit_colors:
    print(fruit + " is " + fruit_colors[fruit])

Trying to use a non-existing key, e.g. from a typo, throws an error message.

[ ]:
fruit_colors["bannana"]

This is an error message, commonly referred to as a “traceback”. This message pinpoints what line in the code cell resulted in an error when it was executed, by pointing at it with an arrow (---->). This is helpful in figuring out what went wrong, especially when many lines of code are executed simultaneously.

Exercise 1.4

  1. In the fruit_colors dictionary:

  • Change the color of apple to 'red'

  • Add an item with key 'plum' and value of 'purple'

  • Display the updated fruit_colors dictionary

  1. Loop through the fruit_colors dictionary and print the fruit name (key) only if its color (value) is 'red'.

Bonus exercises

  1. Create a new empty dictionary groceries with the syntax groceries = {} and then display groceries. Add a key 'eggs' with a value of 12 and display the updated groceries dictionary.

  2. Create a new dictionary squares whose keys are the integers 1, 2, 3, 4, 5, and the value for each key is the key squared (so for key 1, the value is 1; for key 2, the value is 4; etc.).

  • Hint: See if you can find a couple of different ways to accomplish this task. One approach might involve a list along with an empty dictionary and a for loop.