>>> 2 + 3
5
Introduction to Software Engineering (CSSE 1001)
A REPL is a read-evaluate-print-loop providing an interactive environment for a programmer to work in.
We use
to denote the cursor and >>>
the prompt; neither are included when writing Python into files.
We call a single line of Python code that executes at >>>
an expression whereas multiple lines of instructions will be called programs. Python can only interpret “legal” expressions that satisfy Python’s language rules. In this lecture we focus on what arithmetic expressions are legal in the Python language.
A programming language (like Python) has
The syntax of a language defines what it means to be a valid program. The semantics of a language defines what the program actually does.
For instance, 2 + 3
has valid syntax and is therefore a valid Python program (albeit a small one). The semantics of the program is to perform the addition of two and three.
>>> 2 + 3
5
However, + 2 3
is invalid syntax and will raise an error if we try to evaluate it.
>>> + 2 3
The expression (+ 2 3)
is written using Polish or Prefix notation and is valid syntax in other programming languages like Lisp.
+ 2 3
^
SyntaxError: invalid syntax
We must follow Python’s language rules precisely in order for our programs to execute.
The following is an enumeration of the arithmetic available to us in Python.
Table 1 lists all the other arithmetic operators on numbers we will work with.
Operator | Name | Syntax | Semantics |
---|---|---|---|
() |
Brackets | (x) |
Evaluate |
+ |
Unary plus | +x |
Affirm x |
- |
Unary minus | -x |
Negate x |
+ |
Plus | x + y |
Add x and y |
- |
Minus | x - y |
Subtract y from x |
* |
Multiplication | x * y |
Multiply x and y |
\\ |
Integer Division | x // y |
Quotient of x ÷ y |
% |
Modulus | x % y |
Remainder of x ÷ y |
** |
Exponentiation | x ** y |
x to the power of y |
x
and y
integers (but more generally are any legal Python expressions that evaluate to integers). To be more accurate we should add “and store in memory” to each item in the semantics column.
>>> (2)
2
>>> +2
2
>>> -2
-2
Note that Python will ignore space
>>> - 2
-2
>>> +-2
-2
>>> 2 + 2
4
>>> 2 - 3
-1
>>> 2 + - 3
-1
>>> 2 - - 3
5
>>> 2 * 3
6
>>> 14 // 3
4
\(14 = 3 \cdot 4 + 2\) so we say three “goes into” 14 four times with two leftover.
>>> 14 % 3
2
>>> 2 ** 3
8
The carrot operator ^
is not used for powering in Python but rather bit-wise exclusive or. Take care to use the appropriate operator because you will still get a number back when using carrot:
>>> 2^3
1
Which of the following have valid syntax?
>>> + + - - (+-+ 8)
>>> -2 * * 3
What do the following expressions evaluate to? (You must utilize the order of operations.)
>>> -2 ** 2
-4
>>> 3 % 2 ** 3
Programming languages must be unambiguous. That is, every expression we write in Python must have exactly one meaning. Thereby an expression like 3 * 2 + 1
is problematic because it evaluates to 6
if multiplication is done first and 9
if addition is done first.
The first solution we have is to use brackets: 3 * (2 + 1)
has only one meaning. However this quickly becomes ugly as statements like
1 * 2 + 3 * 4 + 5 * 6 + 7
now have to be written like
(1 * 2) + ((3 * 4) + ((4 * 5) + 7)).
Another solution is to designate an order of operations. For instance, if we say multiplication should be evaluated before addition then 3 * 2 + 1
unambiguously evaluates to 7. We therefore designate an order of operations (from highest to lowest precedence) in Table 2.
Operator | Description |
---|---|
() |
Parenthesis. |
** |
Exponents |
-x , +x |
Negation, Unary Plus |
* , / , // , % |
Multiplication, Division, Integer Division, Remainder |
+ , - |
Addition, Subtraction |
However this is actually insufficient to eliminate ambiguity: 1 - 2 - 3
can either mean (1 - 2) - 3
or 1 - (2 - 3)
. The subtraction operation is not associative – it matters how it is bracketed:
>>> (1 - 2) - 3
-4
whereas
>>> 1 - (2 - 3)
2
We resolve this final ambiguity by designating all operations of the same order of precedence to be left associative — excepting exponentiation which is right associative. This means that if @
and #
are operations of the same precedence that x @ y # z
is interpreted as (x @ y) # z
; in practice this means we execute the operations from left to right.
Note the difference in the following pairs of examples which differ only in brackets.
>>> 1 - 2 + 3
2
>>> 1 - (2 + 3)
-4
>>> 5 // 3 % 2
1
>>> 5 // (3 % 2)
5
>>>
Again, exponentiation is right-associative.
>>> 2 ** 1 ** 0
2
Recall \(x^0=1\).
>>> (2 ** 1) ** 0
1
>>> 2 ** (1 ** 0)
2
Up until now integers have been sufficient for our needs. Any expression that we could express with operations and integer numbers evaluated to an integer. This is not the case for division/
which is why we have left it for last.
The division of two numbers can result in a fraction whose decimal expansion we can approximate with a floating point number.
>>> 1/2
0.5
Notice this expression evaluated to a number with a decimal point in it — this new type of number called float
. (We cover types in more detail next lecture.)
>>> type(1/2)
float
We say approximate because a computer has finite memory and therefore cannot represent even simple fractions like \(1/3\) which have an infinite number of digits in their base-ten expansion. Notice the evaluation of following expression gets truncated.
>>> 1/3
0.3333333333333333
Because they are inexact, we generally avoid the use of floats and use integers (which are exact) whenever we can.
All our arithmetic can also be done with floats or indeed with a mixture of floats and integers. When a float is present in a computation the resulting evaluation will be a float, even when a float is not strictly necessary to for the answer.
>>> 4/2
2.0
>>> 1.0 * 0
0.0
>>> 3 ** 2.0
9.0
A float can be converted back to an integer by truncating (dropping all digits after the decimal point).
>>> int(4/2)
2
>>> type(int(4/2))
int