Class 2

Objectives for today

  1. Describe the purpose of a variable and perform variable assignment
  2. Evaluate arithmetic expressions over integers and floats
  3. Describe the concept of Type

Syntax vs. Semantics…

For example the Python programming language has undergone substantial syntactic evolution: print 4 became print(4). Those statements are syntactically different but semantically identical.

When implementing an algorithm, it is always tempting to jump right into the implementation, that is jump right to the syntax. I advocate you resist that impulse, and really think through your algorithm and the semantics of its implementation first (i.e., what building blocks will you use to construct your solution).

Python 3

We will be using Python 3, a widely-used, widely-available, general-purpose programming language. Note that I said Python 3 (not 2), Python 3 is not backward compatible. Lets get started!

We will be using the Thonny integrated development environment (IDE).

Today we will be working in the bottom (or right) box, the “Shell”. This gives us access to Python’s REPL (read-eval-print-loop), also called an interpreter.

The >>> is the prompt for us to enter Python code.

Let’s try some basic expressions, hitting enter after each:

>>> 1
1
>>> 5
5

These are “literal” expressions, because the resulting value is the same as the expression itself.

Now some more complex expressions that include “operators”:

>>> 2 + 2
4
>>> 15 * 20
300
>>> 20 / 4
5.0
>>> 10 - 20
-10

When we hit “enter”, Python is evaluating the expression (the “eval” in REPL) and printing the results.

What about errors?

>>> 2 asdf 2
  File "<pyshell>", line 1
    2 asdf 2
         ^
SyntaxError: invalid syntax

We didn’t follow the syntactic rules of the language and so Python reported an error. Python has no idea what “asdf” means, it is not an operator built-in to the language.

Helpfully, Python does tell us where it found the error, but it doesn’t tell us how to fix it (because it can’t read our minds to know what we wanted it to do). Often the error messages will seem a little opaque, one of the skills you will develop over time is the mapping of error messages to the actual error itself.

Python Types

Earlier we saw 5 and 5.0. What is the difference? They have different types. 5.0 is a floating point value or float, 5 is an integer value or int.

A type consists of two things:

  1. A set of values, and
  2. A set of operations that can be applied to those values.

Integers are exactly what they sound like, while floating point numbers are a subset of the Real numbers. Both are necessarily finite subsets of their domains, that is the computer can’t represent all real numbers at infinite precision. Note, we will learn much more about numerical precision at the end of the semester.

Let’s investigate an example of “set of operations” by figuring out the difference between ‘/’ and ‘//’. Both are valid Python operators.

>>> 8 / 3
2.6666666666666665
>>> 8 // 3
2

What do you notice? The latter appears to round to an integer. How does it round? It doesn’t appear to be “normal” rounding rules (since 2.66 ≥ 2.5 it should have rounded to 3), but is it rounding “down” or rounding towards zero? How could we figure out?

>>> -8 // 3
-3

The above shows us it always rounds “down” (towards negative infinity). And in fact this is called the “floor division” operator. What are the type rules for these operators and how are they different? We can already see integer division and integer floor division produce different types. How could we figure out the type rules? Much as we did above we could experiment…

>>> 8 / 3.0
2.6666666666666665
>>> 8 // 3.0
2.0
>>> 8.0 // 3
2.0
...

What we observe is that division of integers produces a float, while floor division of integers produces an integer. For both operators, if either operand is a float, the other is converted to a float (if not already) and the result is a float. Note that ‘//’ applied to floats still rounds “down”.

This experimentation is not at the exclusion of reviewing the documentation. In general, we should start by consulting the relevant Python documentation (which in this case confirms and expands upon our observations). However, we want to develop both skills: 1) using the documentation and 2) infer/verify behavior via experimentation

What about more complex expressions involving multiple operators, e.g., 2 + 3 * 4? In general, Python follows PEMDAS. So this expression would be interpreted as 2 + (3 * 4) and evaluate to 14, i.e.,

>>> 2 + 3 * 4
14

PI questions (don’t use the interpreter, we are trying to build up our intuition)

Non-numeric types

Let’s think about non-numeric types

>>> "hello"
'hello'

We term "hello" a string literal because it is a string (sequence) of characters whose value is the characters themselves, i.e., we are literally and exactly specifying the value. We indicate strings by characters between either double or single quotes (you can’t mix double and single quotes). Thus "hello" and 'hello' are equivalent.

We can apply certain operators to strings, for example

>>> "hello" + "goodbye"
'hellogoodbye'
>>> "hello" * 2
'hellohello'

but what about:

>>> "hello" + 2
Traceback (most recent call last):
  File "<pyshell>", line 1, in <module>
TypeError: must be str, not int

Python is telling us that adding an integer to a string isn’t a supported operation of string types. Concatenation of strings with + is supported (we just saw that) and Python does know how to convert integers to strings. But Python won’t do so implicitly as it can’t be sure that is what you intended (recall what we learned previously about ambiguity). You need to explicitly convert the integer to a string with the str function (As you might expect, there are corresponding int and float functions that convert the input to the corresponding types - including strings. Try int("10") in the shell).

>>> "hello" + str(2)
'hello2'

Assignment

Everything we have done so far was very ephemeral. We gain tremendous power by being able to save and use intermediate results.

Assignment syntax: <variable> = <expression>

Example:

>>> x = 7

Nothing was printed by the REPL. So did anything happen? To figure it out, lets build up our mental model of what is going on in the Python interpreter.

Assignment semantics:

  1. Evaluate the right-hand side (RHS) of the expression (all expressions in Python produce a value, and thus have a type)
  2. Store the resulting value in memory
  3. Set the variable name on the left-hand side (LHS) to point to the memory location with result

Python Tutor Memory Model Picture

Unlike expressions, the assignment statement does not evaluate to value (that is printed to the REPL).

PI questions (don’t use the interpreter, we are trying to build up our intuition) (credit)

What’s in a name?

Python variable names can generally be of any length, contain upper and lower case letters, digits and the underscore character (_). Although variable names can contain digits, a variable name can’t start with a digit. Additionally there are a set of Python keywords (or “reserved” words) such as def that have specific meaning in the Python language and so can’t be used as variable names. As you might imagine variable naming can get very contentious. We will talk a lot more about good “style”, of which variable naming is an important aspect, but in the meantime, aim for concise, descriptive variables names (e.g. “income” instead of “i” of “asdf”) that minimize the cognitive burden anyone reading your code.

Thonny mechanics (saving files, and finding errors)

If we close and restart Thonny and try to use variables we created last time, we will get errors

>>> x
Traceback (most recent call last):
  File "<pyshell>", line 1, in <module>
NameError: name 'x' is not defined

We could repeat everything that we did, e.g.

>>> x = 4
>>> x
4

but that is very inefficient and can’t possibly be the “way”,

If we want to save our code, and use that code to regenerate results, we will need to create additional files. We can do so using the text editor panel in Thonny.

# An example script
x = 7
x

Everything to the right of ‘#’ (and including the ‘#’) is a comment and is not executed. It is an explanatory note for the reader (which will often be you!).

I will create a script with our command, save the file, and then run it (with the green arrow).

The result is the similar to if we had typed the same code into the interpreter, except we don’t get line-by-line feedback. That is we are not executing each line in the context of REPL (with the “print”) but just “read” and “eval”. We will need to use explicit print functions to see the result.

# An example script
x = 7
print(x)

But we can use the variables we defined in the file later in the interpreter:

>>> x
7

Lets introduce an error so we see what Thonny reports, e.g. referencing an undefined variable y:

>>> %Run lecture2.py
Traceback (most recent call last):
  File "lecture2.py", line 3, in <module>
    y
NameError: name 'y' is not defined

PI questions (don’t use the interpreter, we are trying to build up our intuition)