2.6. Lecture 5: Python 3

Before this class you should:

  • Read Think Python:

    • Chapter 8: Strings;

    • Chapter 10: Lists;

    • Chapter 11: Dictionaries; and

    • Chapter 12: Tuples

Before next class you should:

  • Read Think Python:

    • Chapter 15: Classes and objects;

    • Chapter 16: Classes and functions;

    • Chapter 17: Classes and methods; and

    • Chapter 18: Inheritance

Note Taker: Justin Cripps

2.6.1. Strings

Starting with Strings

A string is a sequence of characters, and the characters can be accessed one at a time with the bracket operator [index]. They use 0-base indexing, like C, using integers and NOT floats. Variables can also be used for indexing (and expressions).

  • String[x], string[x+1], etc.

The ‘len’ function is built-in, and returns the number of characters in a string. If you are trying to index using this ‘len’ function, make sure to add a ‘-1’ due to zero indexing. You can also use an index of negative numbers to start from the end of the string, as opposed to the start.

String Traversal

Typically involves the use of ‘While’ and ‘For’ Loops. Traversal involves processing every character of a string, one at a time.

  • Start at the beginning of the string, do something to each character, and continue until the end

Simple way to traverse a string is using the ‘for in’ syntax, for example:

for letter in 'fruit':
    print(letter)

The above code would print out every letter in the word ‘fruit’

String Concatenation

Also known as “String Addition”, is used to combine two strings, using a ‘+’ sign. We then went over an example question involving code to print out the names of various ducks, where all duck names were 4 characters long except for two with the names ‘Ouack’ and ‘Quack’. The provided code did not work for these names as it was hard-coded to only work for duck names that were 4 characters long.

  • An if-else statement would need to be implemented to fix the names ‘Ouack’ and ‘Quack’ to have the correct outputs, for example:

prefixes = 'JKLMNOPQ'
suffix = 'ack'

for letter in prefixes:
    if letter == 'O' or letter == 'Q':
        print(letter + 'u' + suffix)
    else:
        print(letter + suffix)

A segment of a string is called a slice, and is accessed similar to selecting a character using a colon, for example:

String[0:4] or String[4:8]

Note that the end index is NOT included, so in the first example, only characters 0,1,2,3 are selected in the slice. A second note is if you omit the first index, the slice starts at the beginning of the string, and if you omit the last index, the slice ends at the end of the string.

Strings are also immutable, and are not meant to be changed after they’ve been defined. You cannot change a single character in a string by referencing it with brackets. For example, the following code would not work:

myString = 'hello'
myString[0] = 'j'

The best you can do is create a new string that is a variation of the original.

Searching

Searching can be defined as traversing a sequence and returning when we find what we are looking for. The following example was given during the lecture:

def find(word, letter):
    index = 0
    while index < len(word):
    if word[index] == letter:
        return index
    index = index + 1
    return -1

This function will iterate through a given string looking for a given character, and return the index of the character in the word. If no match is found, it returns -1.

String Methods

String methods are simple methods for strings that are built into python. Methods are similar to functions but the syntax is slightly different. They are attached to objects (via their class), and are accessed using ‘.’ (dot) syntax.

  • ‘.upper()’ is used to return a string in all uppercase letters:

myFavCourse = 'modelling complex systems'
myFavCourseCapitalized = myFavCourse.upper()
print(myFavCourseCapitalized) # Would yield: 'MODELLING COMPLEX SYSTEMS'

When calling a method, it is called an invocation. There is a built-in method that takes in a string and finds the first occurrence of a character, called the find method. It will return the index of the specified character in the string being searched:

myFavFood = 'banana'
finder = myFavFood.find('a')
print(finder) # This would yield '1'

You can also supply an index to start the ‘find’ at a certain index, instead of the beginning of the string:

myFavFood = 'banana'
finder = myFavFood.find('n', 3)
print(finder) # This would yield '4' since it starts at index 3 (skipping the first 'n' at index 2)

This index is an example of an optional argument. Another optional argument for this method is supplying an index where the ‘find’ will stop at, instead of the end of the string. The ‘in’ operator will take two strings, and return ‘true’ if the first string appears as a substring of the second string.

if 'a' in 'banana':
    print('True') # This print statement would run since 'a' is in 'banana'
String Comparison

Relational operators work on strings, for example to see if two strings are equal using the ‘==’ format. Other relational operators are useful for putting words in alphabetical order (‘<’ or ‘>’). One note for uppercase letters is that they always come before lowercase letters (similar to ASCII codes) when checking alphabetical order.

2.6.2. Lists

Starting with Lists

A list is a sequence of values. In a string, the values are characters. However, in a list, they can be any type. The values in a list are called elements or items. There are several ways to create a new list, however the simplest is to enclose the elements in square brackets. For example:

myList = ['element one', '2']

The elements of a list don’t have to be the same type, and lists can contain other lists as an element. A list within another list is ‘nested’. Lists are also mutable (while strings are immutable), and the ‘in’ operator also works on lists.

Traversing a List

The most common way is using the ‘for element in list’ syntax which works well if you only need to read the elements of the list. However, if you want to write or update the elements then you will need the indices. A common way of tackling this is by combining the built-in functions ‘range’ and ‘len’, an example is visible below:

For i in range(len(numbers)):
    numbers[i] = numbers[i] * 2
  • Note 1: A ‘for’ loop that iterates through an empty list will never run the body of the loop

  • Note 2: Although a list can contain another list, nested lists still count as a single element
    • ex. checking the length of the outer list won’t include the length of the inner list

List Operations

The ‘+’ operator concatenates lists, and the ‘*’ operator repeats a list a given number of times

List Slices

The slice operator also works on lists, ex: list[1:3]. Once again, omitting the first/last index will slice starting at the beginning/end of the list, Omitting both results in a copy of the entire list. A slice operator on the left side of an assignment CAN update multiple elements (potentially unintentionally).

List Methods

Most list methods are void; they modify the list and return ‘none’. The following methods are all accessed with the dot syntax:

  • ‘.append’ adds a new element to the end of a list

  • ‘.extend’ takes a list as an argument and appends all of the elements

  • ‘.sort’ arranges the elements of a list from low to high

Map, Filter and Reduce

Mapping occurs when traversing one list to build another, filtering occurs when sorting through a list to look for certain patters or characteristics, and reducing involves removing elements from a list. All of these paradigms are useful in certain circumstances.

The following example of a shortform operator was given in class:

  • ‘total +=x’ is equivalent to ‘total = total + x’

Python also has a built-in ‘sum’ function, for example:

t = [1,2,3]
# sum(t) would equal 6

This is called ‘reduce’ or ‘aggregating’. Alternatively, you may want to traverse one list while building another, which is known as ‘mapping’, where you are mapping one list to another based on a sequence. Another paradigm is selecting certain elements, and returning a sub-list.

  • Example: only_upper, this sort of operation is called a ‘filter’ because it selects some of the elements and filters out the others

Deleting Elements in a List
  • ‘pop’ is used to delete a certain element at a provided index

  • ‘remove’ can be used if you don’t want to use the index, and instead looks for an element matching the provided one

  • ‘del’ can be used to remove multiple elements at once

Lists and Strings

A string is a sequence of characters and a list is a sequence of values, but a list of characters is not the same as a string. To convert from a string to a list of characters, you can use the ‘list’ function (which is built into python).

S = 'spam'
t = list(s)
print(t) # Would yield: ['s', 'p', 'a', 'm']

You can also use .split to break it into individual words, in the same format as a list. One note is that you can set the delimiter using the split function. The ‘.join’ method is the inverse of ‘.split’, and therefore does the inverse operation of split and joints the elements together.

2.6.3. Dictionaries

Starting with Dictionaries

A dictionary is a mapping and is like a list, but more general. In a list, the indices have to be integers, however in a dictionary they can be (almost) any type. A dictionary contains a collection of indices, which are called ‘keys’, and a collection of values. Each key is associated with a single value, the association of a key and a value is called a ‘key-value pair’ or sometimes an ‘item’. You can say that each key ‘maps to’ a value. Another way to think of it is as a lookup. Can be created using the ‘dict()’ syntax:

myDict = dict()

When this empty dictionary is printed, the output would be:

{}

To add items to a dictionary, you use square brackets:

myDict['keyOne'] = 'valueOne'

Now, when printing the dictionary, the output would look like:

{'keyOne': 'valueOne'}

An initial note is that the order of the key-value pairs might not be the same, depending on your version of python. The ‘len’ method works on dictionaries as well, in addition to the ‘in operator’ also working on dictionaries to check if elements exist.

  • Note: ‘in’ works on the keys, not values. If you want to use values, use the .values() method on a dictionary.

Python dictionaries use a different data structure (known as a hash table) that allows for searching in constant time (which is very useful for lookups).

An implementation is a way of performing a computation; some implementations are better than others.

Dictionaries also have a method called ‘get’ that takes a key and a default value. If the key appears in the dictionary, get returns the corresponding value; otherwise, it returns the default value.

Looping in dictionaries can be done using a ‘for’ loop which will loop through the keys of the dictionary. The keys of a dictionary can also be accessed using the .keys() method:

myCarDict = {"Brand":"Toyota", "Model":"Rav4". "Year":"2022"}
dictKeyList = myCarDict.keys()
print(dictKeyList) # The output of this print statement would be: dict_keys(['Brand','Model','Year'])

2.6.4. Other Topics and Concepts

Objects and Values

Question asked to the class was: if two different variables are set to ‘banana’, are they referring to the same string? You can check this using the ‘is’ operator (to check if they are the same object). This is different for lists, even if they are equivalent to each other, they are not identical (can be checked once again using the ‘is’ operator), since they are two different things in memory. It is more precise to say objects have values, ‘object’ and ‘value’ are not interchangeable.

Aliasing

If a variable ‘a’ refers to an object, and you assign another variable, ‘b’, to that object, ‘b’ is pointing to object ‘a’ (same as in C/C++). The association of a variable with an object is called a reference. If the aliased object is mutable, changes made with one alias WILL affect the other. Although useful, this behaviour is error-prone (safer to avoid aliasing when you are working with mutable objects). For immutable objects (like strings), aliasing is not much of a problem.

List Arguments

When passing a list to a function, the function gets a reference to the list. If the function modifies the list, the caller sees the change. It is important to distinguish between operations that modify lists, and operations that create new lists.

  • Example: ‘append’ method modifies a list, but the ‘+’ operator creates a new list.

This difference is important when you are trying to write functions that are SUPPOSED to modify lists. An alternative is to write a function that creates and returns a new list.