Goals

• Review self-paced introduction material
• Create and manipulate lists
• Create and manipulate dicts
• Learn what a module is and how to access its functions
• Apply all these concepts to understanding a real program

Review tutorial material

Section goals

• math
• `type()`
• variables
• strings
• booleans
• `if`/`elif`/`else`
• functions

Math!

With Python you can do math in much the same way that you would with a calculator or in an Excel file. Simple operations such as addition, subtraction, multiplication and division can all be done at the Python prompt:

```2+2
5-2
3 * 4
4 / 2
```

So, pretty easy right? As you can see from above, it doesn't even matter if you add spaces or not (though generally it is a best practice to be consistent in your use of spacing). Let's try something a little more difficult.

```2*2 + 10*2
```

This example is a bit more complicated, but more importantly, it shows that the order of operations in Python is the same as the order of operations that you learned in math class

As you may have noticed, none of those numbers involved decimal places. How do you think Python will handle decimal places? Let's give it a try:

```1 / 2
```

Ok, so that isn't quite what we were expecting. Because that fraction involved two integers, the result is also going to be an integer. Any time you want a decimal point to be an option, you have to input at least one number with a decimal point:

```1.0 / 2
```

Much better! Note that you could have also put the decimal in the denominator instead. In Python, numbers with decimals are called floats.

`type()`

So that covers basic math, but it also hints at the fact that Python recognizes multiple types of data. Now we have discused two of the types that Python recognizes: integers and floats. In fact, it also recognizes other types of data was we will see shortly. But, how can we know what type of data Python thinks something is? Luckily, there is a function for that! The function is `type()`. If you use this function and put the information for which you want a type in the parentheses, the result will be the type of data:

```type(1)
type(1.0)
```

This can be a useful way to find out what type of data you are dealing with.

Variables

Already you can do a few things with Python, but what if you want to move on to more complicated mathematical concepts or functions. For that you will need variables to store information, which you saw this morning in the tutorial. To use variables, you will need to first pick a name for the piece of information that you want to represent with the variable. This is called assignment. You can assign a value to a variable with an equals sign.

```x = 4
y = 2.5
x
y
```

Be sure to assign a value to a variable before you use it; if you don't you'll see an error message. Tips for picking a variable:

• Variables cannot have spaces or any special characters
• Ideally, variables should be descriptive to make your code more readable

Strings

Now that we have covered how Python can let you do math, it is time to see what it can do with strings. Strings are the third data type we are learning about after integers and floats. The term string refers to any group of multiple characters. These can be letters, numbers, special characters, punctutation mark or spaces and they can be in any combination. Strings are always surrounded by quotes and anything between the quotes is considered part of the string:

```"Hello"
"Hello, world!"
"Testing 1, 2, 3"
```

Strings can also be stored in variables.

```x = "Hello"
x
```

Booleans

Booleans are another data type. There are only two booleans: `True` and `False`. They allow us to test facts and write programs that make decisions. Be sure to pay attention to the capitalization:

```type(True)
type(true)
type(False)
type(false)
```

These booleans make it easy to test equality and inequality.

```0 == 0
0 == 1
0 != 1
```

You'll notice a couple of things from these examples. First, notice that equality is tested with `==` as opposed to `=`. This is because `=` is used for assignment whereas `==` is used for comparison. You'll also notice that `!=` tests whether the two values are NOT equal to one another.

In addition to equality and inequality, you can also test for greater than or less than. <,<=, >, and >= all mean exactly what you would expect.

```2 > 1
1 < 2
2 >= 5
```

`if`/`elif`/`else`

Now that we understand booleans, we can start creating and use conditional statements:

```if 2 > 1:
print "Two is greater than one."
```

Did that behave the way you expected it to? As you can see, now that we are writing multiple lines of code, indentations become important. You will need to indent the second line 4 spaces and you have to be careful that your indentations are always consistent. As you'll see, as soon as you type the first line, Python detects that your code will have multiple lines and prompts you with `...` instead of `>>>`. When you click enter at the end of the code block, Python will evaluate the conditional statement and respond accordingly.

This allows us to evaluate conditional expressions, but it only allows us to respond to either the True or the False case. What if we want to execute code for more than one condition? To do this, we need to use `else` in addition to `if`.

```x = 5
y = 3
if y > x:
print "y is greater than x."
else:
print: "x is greater than y."
```

Here we have two code blocks and this is reflected in the indentation. But, what if we want to write more complicated conditional statements? We can do that by using `and` and `or`:

```date = 28
if date > 26 and date < 31:
print "Time for ALA Annual."
else:
print "Still time to pack!"
```

You can also check more than situations. If you want to do so, you need to use `elif` (remember it as an abbreviation for else if). You can use `elif` as many times as you would like to evaluate as many conditions as you would like. Let's see an example:

```date = 28
if date > 26 and date < 31:
print "Time for ALA Annual."
elif date == 28:
print "Time for my preconference!"
else:
print "Still time to pack!"
```

Functions

We've already talked briefly about simple functions when we discussed `type()`, but with Python you can write far more complicated functions than this. As with `type()`, many functions have already been written and can be called with their name. But, what if we want to write our own functions?

It makes sense to write functions for activities that we will want to reuse. Functions start with a function signature which defines the function and tells you how it will be called and what argument(s) it takes. The second part of a function is the part that actually does work for you. It is indented just as we did with `if`/`elif`/`else` blocks. You already saw a few of these in the tutorial this morning, but let's take a look at an example of a function:

```def multiply(x, y):
result = x * y
return result
```

Now let's try using our function:

```multiply(2, 3)
```

While this is a very basic function, you can also write more complicated functions, as we will see later today.

Lists

Section goals

• purpose
• initialization
• `len()` review
• accessing elements
• changing elements
• slicing lists
• strings are like lists

Purpose, initialization, and `len()`

So, are you a list-maker? What are some lists you make? Are there times that you need to look at each element on a list and do something to, or about, it?

For example, do you ever make a to-do list? Let's start one.

```todo_list = ['initialize list', 'find its length', 'access elements']
todo_list
len(todo_list)
```

You can initialize lists with any starting set of items you want. They don't have to be strings. You could use numbers:

`number_list = [3, 1, 4, 1, 5]`

Do you see why these are numbers and not strings? How would it be different if you wanted this to be a list of strings?

As you can see, it's okay to have the same element more than once, and you don't have to put things in order. Whatever order you put things in, the program will remember.

Accessing elements

You probably want to access individual elements of your list sometimes. How about the first item on your to-do list?

```todo_list
```

...wait what?

You can add elements to the end of your list with the `append()` function:

```todo_list.append('add an element')
todo_list
```

If you have more than one list, you can add those together too:

```todo_list_two = ['coffee break', 'learn about negative indexing']
todo_list + todo_list_two
```

Note that that returned a new list, but we didn't assign it to anything, so we're not going to be able to use it in the future. We could say something like `new_todo_list = todo_list + todo_list_two`. Or we might want this useful shorthand:

```todo_list += todo_list_two
todo_list
```

What just happened there?

Changing elements

Remember what the first element on our list was?

```todo_list
```

We've totally done that. Let's assign a new value to that space on our list (which will overwrite the existing value).

```todo_list = 'change a list element'
todo_list
```

Slicing lists

Our list has gotten kind of long!

```len(todo_list)
```

What if we only want to tackle part of it? We've seen how to access a single element (e.g. `todo_list`). If we're feeling like we can handle more than one thing on our to-do list right now, though, we can take a slice.

Which elements do you think this will give us? How many?

```todo_list[0:2]
```

Was that what you expected? If not, what's actually going on here?

Here's why this is useful, by the way:

```todo_list[0:2] + todo_list[2:6]
```

You can leave one of the slice parameters out and it still works.

```todo_list[:2]
```

What did this one do?

```todo_list[2:]
```

By the way, this means you can do this too:

```todo_list[:2] + todo_list[2:]
```

So you don't have to know how long the list is for this trick to work.

Let's feel really accomplished by tackling the last item on our to-do list:

```todo_list[-1]
```

Booyah! How cool is that?

You can use negative indexes for slicing, too. How about we look at everything from the third-to-last element to the end?

```todo_list[-3:]
```

Strings are like lists

So check out this wacky Python trick. Let's make a string:

Mmmm, pie.
```string = 'Pie, .py, or pi?'
string
string[0:3]
```

Strings aren't always like lists; you can't use list methods like `append()` on them. But the indexing notation works, treating a string just like a list of characters. The `len()` function can take either a string or a list as an argument. + works for both strings and lists, too. And the `for` and `while` loops we'll see next work the same way for both strings and lists, too.

If you've programmed before in a statically typed language, this will probably either blow your mind with glee, or freak you out. More details.

In general, Python tries not to be too picky about what type an object is unless it absolutely has to be; otherwise it tries to do what you'd expect.

What if you wanted to organize your to-do list -- say, have a section of work items and one of home items? Well, we can't do that...yet. But you'll be able to by the end of the lecture. See if you can spot how.

Loops and more flow control

Section goals

• `for` loops
• `if` statements inside `for` loops
• nested `for` loops
• `range()`
• `while` loops
• infinite loops
• `if` statements inside `while` loops
• `break`
• `raw_input()`

`for` real?

Lists are fine, lists are great, and you will be up to your ears in lists in your coding life. Here's the catch: a list can contain any number of elements, and more often than not you will have to look at each element and determine what to do with it.

Let's take a list:

`pie = ["strawberry", "blueberry", "raspberry"]`

So, how can I find out what's all in `pie`? Well, we could do this:

```print pie
print pie
print pie 
```
More information on RSIs at Wikipedia. In short, they stink. Remember to take frequent breaks while programming!

It'll do the job, but do we really want to write the same code over and over again for each element? Do you like RSIs? I don't.

Enter loops. Loops allow you to work through lists efficiently by only having to write one piece of code for all (or a subset of) the elements of a list. There are different types of loops that you can use for different situations. For our example, we will use the `for` loop.

And here it is! Do you know what's going on in the `for` loop below?

```for fruit in pie:
print fruit
```

The example above has a simple command, but we can do much more in loops. We can introduce other forms of flow control in loops. Do y'all remember `if…else` statements? You can use them in loops if you are looking for a particular condition with one or more of the elements in a list. Here's an example. What will happen?

```for fruit in pie:
if fruit == "strawberry":
print "I like strawberry!"
else:
print fruit
```

Better yet, you can put loops within loops! Loopception, if you will.

A wild list appears!

```approved_berries = ["strawberry", "currants", "elderberry"]
```

If I want to see if there are any elements in `approved_berries` in `pie`, what will it look like? Also, what's up with that `i` there?

```i = 0
for fruit in pie:
for berry in approved_berries:
if fruit == berry:
i += 1
print str(i) + ' approved berries in this pie'
```

Home on the `range()`

The `range()` function will help if you need to go through a series of numbers as well as lists. You can get fancy with `range()` but we'll stick to two ways you can use the function.

The first is your garden variety going through a series of numbers. You can either give an end number as a single parameter, or have both a beginning number and an end number as two parameters.

```range(14)
range(3,14)
```

`range ()` also works on lists. Going back to our `pie` list, what berries will be printed out below?

```for fruit in range(len(pie)):
print pie[fruit]
```

Would this way of printing the list be any different from the first for code snippet above?

`while` you're here...

The other loop function you will find useful is `while`. It is also one of the functions that you will curse the most. Why? Run the following code. Surely nothing will go wrong if you just copy and paste a piece of code and run it, no questions asked…

```i = 0
infinite = True
while infinite:
i += 1
print i
```
You might have other words for it once you delve deeper into loops. Words that are not appropriate in a professional setting.

The loop has nothing in there to tell it to stop on its own. This is called an infinite loop. How do you stop this abomination of system resources?

control + c OR control + break (OR control + backslash on Mac, if the other two don't work)

Remember the above keystrokes. They kill running python processes. They also know where the digital bodies are buried.

How do we make sure that our `while` loop ends? We either need to find a way to make that `infinite` variable `False` or we need to insert a `break` function. `break` allows the script to exit the current loop. However, if we add either of those to the `while` code above, we'd only go through the loop once. What should we do?

Like the `for` loop function, we can use other flow control functions under the `while` loop, such as `if` statements. When will the loop break in the below code?

```i = 0
infinite = True
while infinite:
i += 1
if i == 10:
break
else:
print i
```

You can't handle the `raw_input()`!

Before we finish with this section of the lecture, there's one more function that you'll find useful when dealing with flow control. Sometimes you will need something from the person running the script in order for the script to then act on that information. `raw_input()` prompts the user to enter a value, which you can then store in a variable.

What answer does the user have to answer with to break the `while` loop?

```answer = ""
answer = raw_input("Are you enjoying the workshop so far?")
else:
print "You will enjoy it even if you aren't. Try again."
```

Dictionaries

Section goals

• purpose
• initialization
• accessing elements
• changing elements
• `keys()` and `values()`

Dictionaries, abbreviated to "dicts" in Python-speak, are a set of key-value pairs much like a linguistic dictionary contains terms and their definitions. Dicts can be useful to store structured data or make difficult-to-remember values easier to access. Let's see how you make a new dict.

```words = {
'python': 'an awesome programming language',
'anaconda': 'a terrifying snake'
}
```

Like any other variable, we start by giving the dict a name and then an equals sign. Remember that a single equals assigns a variable's value, while multiple equals is used to compare a variable's value against something else. Dicts look very similar to lists, except they use curly braces to enclose their contents rather than square brackets.

Now that we've got a dict with some items in it, how do we retrieve its values?

```>>> words[ 'python' ]
'an awesome programming language'
>>> words[ 'anaconda' ]
'a terrifying snake'
```

Again, there are similarities with lists. We use bracket notation to specify which item we want to receive from the dict, but instead of using an integer like with lists we use the key whose value we want.

Adding and altering items is easy, too.

```>>> words[ 'boa constrictor' ] = 'an even more terrifying snake'
>>> words[ 'boa constrictor' ]
'an even more terrifying snake'
>>> words[ 'anaconda' ] = 'a slightly less terrifying snake'
>>> words[ 'anaconda' ]
'a slightly less terrifying snake'
```

Dicts come with a couple handy built-in functions: `keys()` returns a list of all the dict's keys, while `values()` returns a list of its—wait for it…—values.

```>>> words.keys()
['python', 'boa constrictor', 'anaconda']
>>> words.values()
['an awesome programming language', 'an even more terrifying snake', 'a slightly less terrifying snake']
```

It's worth noting that there's no guaranteed order to how the keys or values are returned. It also is handy that the `len()` function works on dicts, too; `len( words )` will return the number `3` in our example.

While we've seen only text strings so far, the value of a dict item can be any other Python data structure, including another dict. Here's a much more complicated dict:

```catalog = {
'last_updated': 'Sat Jun 15 17:32:53 EDT 2013',
'num_items': 213,
'items': {
'9780596009250': {
'title': 'Programming Python',
'authors': [ 'Mark Lutz' ],
'pub_year': 2006
},
'9780596002817': {
'title': 'Learning Python',
'authors': [ 'Mark Lutz', 'David Ascher' ],
'pub_year': 2003
}
},
'location_codes': [ 'stacks', 'reserves', 'special collections' ]
}
```

This dict implements a simple library catalog of sorts, using many of the data structures we're already familiar with.

Modules

Section goals

• purpose
• builtins
• imports
• `import random`
• `random.randint`
• `random.choice`
• walk through `state_capitals.py`

Where state_capitals.py from http://mit.edu/jesstess/www/BostonPythonWorkshop7/state_capitals.py is the grand finale and synthesis of lecture material.

Back to Checkoff