# 1 04-ExpressionsTypes

Previous: 03-IntroPython.html

**Q**: In hexadecimal, in Canada, why is 6 afraid of 7?
**A**: Because 7, 8, 9, A?

## 1.1 Screencasts

• Password for the Vimeo videos is in Zulip chat.
• Lecture 1: https://vimeo.com/508153540
• Lecture 2: https://vimeo.com/508759087
• Tip: If anyone want to speed up the lecture videos a little, inspect the page, go to the browser console, and paste this in:
document.querySelector('video').playbackRate = 1.2

* https://automatetheboringstuff.com/2e/chapter0/
* https://automatetheboringstuff.com/2e/chapter1/
* https://inventwithpython.com/invent4thed/chapter0.html
* https://inventwithpython.com/invent4thed/chapter1.html
* https://inventwithpython.com/invent4thed/chapter2.html
* http://scipy-lectures.org/intro/intro.html
* http://scipy-lectures.org/intro/language/python_language.html
* http://scipy-lectures.org/intro/language/first_steps.html
* http://scipy-lectures.org/intro/language/basic_types.html
* https://books.trinket.io/pfe/01-intro.html
* https://books.trinket.io/pfe/02-variables.html
* https://docs.python.org/3/library/constants.html
* https://docs.python.org/3/library/functions.html (built in python functions, not how to write your own)
* https://docs.python.org/3/library/stdtypes.html
* https://www.python-course.eu/python3_variables.php
* https://www.python-course.eu/python3_operators.php
* https://www.learnpython.org/en/Variables_and_Types
* https://www.learnpython.org/en/Basic_Operators
* https://python.swaroopch.com/basics.html
* https://python.swaroopch.com/op_exp.html
* https://www.tutorialspoint.com/python3/python_basic_syntax.htm
* https://www.tutorialspoint.com/python3/python_variable_types.htm
* https://www.tutorialspoint.com/python3/python_numbers.htm
* https://www.tutorialspoint.com/python3/python_basic_operators.htm

## 1.3 Encoding data

How do we represent data in the computer?

### 1.3.1 Binary (lowest level)

https://en.wikipedia.org/wiki/Binary_number

• Complete this tutorial (it’s good, and I mean you should actually read it!):
https://ryanstutorials.net/binary-tutorial/

### 1.3.2 ASCII and UTF-8 (higher level)

Text encoding (above the numeric/binary layer):

Note: There are some non-numeric characters, like ‘’

Hidden characters exist invisibly at the end of lines, as tabs, etc.
Watch out, because hidden characters differ by operating system, and can break code (do your work in the class VM when we get there)!

## 1.4 Python

Types of built-in “object” in Python3 include:

• Numbers

• Integers
• Floats
• Complex numbers
• Strings

• Containers

• Lists, Tuples, Sets, FrozenSets, and Dictionaries

Note: there can hypothetically be more types, but you would import them as extra modules.

### 1.4.1 Variables, assignment, strings

Variable names are aliases assigned to objects.
04-ExpressionsTypes/00_variables.py

#!/usr/bin/python3
# -*- coding: utf-8 -*-
"""
Variables
"""

# There are no declarations in python3, only assignments.
# Use lower_case_with_underscores for variable names.
some_var = 5
some_var  # => 5

# Accessing a previously unassigned variable names is an error / exception.
# >>> print(some_unknown_var)
# If we ran the line above, it raises a NameError, learn to read errors!!!!
# NameError: name 'some_unknown_var' is not defined

my_string = "This is a string"
my_string = 'This is a string too with an " in it'
my_string2: str = (
"The : str part is optional, and for now is just a helpful note, called a type hint"
)
my_string3: str = "The thing below isn't a number, but a string"
my_string4: str = "4"
my_int: int = 4  # This is a real int
my_string = "We're re-using the above name (my_string) for a new object. Variables (labels) can be re-assigned to new objects"
# They can also be assigned to new objects of different type, but that's usualyl not good style.
my_string = 4

# Variables are just references to objects stored in memory
# These references are like numeric addresses
# The variable name does not have a type, but the object being referenced does.
myint: int = 7
print(type(myint))
print(id(myint))
print(myint)

myfloat: float = 7.0
print(type(myfloat))
print(id(myfloat))
print(myfloat)

myfloat = float(myint)
print(type(myfloat))
print(id(myfloat))
print(myfloat)

mystring = "hello"
print(type(mystring))
print(id(mystring))
print(mystring)
• The left side of assignment using = must be a variable.
• The right side must be some expression that returns a value (hint expression is a pretty broad category).
• Assignment binds a variable name(s) to the object, the thing in memory.
• Objects have:
• A type (their category/class; see above)
• A value (the thing they store; see above)
• A unique identifier (something like their real memory address; see above)
• Some objects are changeable (mutable) and others are not changeable (immutable).

#### 1.4.1.1 Naming rules

https://docs.python.org/3/reference/lexical_analysis.html#identifiers
* Variables can be named uppercase and lowercase letters A through Z, the underscore _ and, except for the first character, the digits 0 through 9.
* Naming is case-sensitive, as it is in Linux.
* Starting with _ has special purpose (more to come later)
* The following identifiers are used as reserved words, or keywords of the language, and cannot be used as ordinary identifiers. They must be spelled exactly as written here:
~~~
False await else import pass
None break except in raise
True class finally is return
and continue for lambda try
as def from nonlocal while
assert del global not with
async elif if or yield
~~~

##### 1.4.1.1.1 Naming conventions

Name your variables using snake case, for example:
slithering_reptile

As a preview (more to come), name your classes using camel case, for example:
DesertAnimal

### 1.4.2 Keyboard input and output

How to get input from the user (during run-time of your program)?
How to print output to the screen for the user during run-time?
04-ExpressionsTypes/01_input.py

#!/usr/bin/python3
# -*- coding: utf-8 -*-

x: str = input()
print(x)

y: str = input("Type something in please:")
print(y)

Notes:
* Command line arguments are another way to get input from users, but they only work before/at run-time, not during the actual run of the program.
* Files are another way.

Example: http://inventwithpython.com/invent4thed/chapter4.html
You can just discard input (or take enter alone) by calling input() without storing the result. Print by itself just prints a newline.
04-ExpressionsTypes/02_jokes.py

#!/usr/bin/python3
# -*- coding: utf-8 -*-

print("What do you get when you cross a snowman with a vampire?")
input()
print("Frostbite!")
print()

print("What do dentists call an astronaut's cavity?")
input()
print("A black hole!")
print()

print("Knock knock.")
input()
print("Who's there?")
input()
print("Interrupting cow.")
input()
print("Interrupting cow wh", end="")
print("-MOO!")

### 1.4.3 Boolean types

https://en.wikipedia.org/wiki/Boolean_data_type

The best thing about a boolean is even if you are wrong, you are only off by a bit...
#!/usr/bin/python3
# -*- coding: utf-8 -*-

# Boolean values are special primitives
# Note: the capitalization
True  # => True
False  # => False

# They're actual objects
var = True
var = False

### 1.4.4 None type (a null type)

#!/usr/bin/python3
# -*- coding: utf-8 -*-

# A special value indicating null
None

# None is an actual object.
var = None

https://realpython.com/null-in-python/

### 1.4.5 Numeric types

https://en.wikipedia.org/wiki/Integer
* An integer (from the Latin integer meaning “whole”) is a number that can be written without a fractional component.
* The set of integers consists of:
* zero (0),
* the positive natural numbers (1, 2, 3, …), also called whole numbers or counting numbers, and
* their additive inverses (the negative integers, i.e., -1, -2, -3, …).
* In python, integers can be arbitrarily big (this is nice, especially compare to other languages like C or C++ where you need to use a library or write it yourself)!

https://en.wikipedia.org/wiki/Floating-point_arithmetic
* Floating-point arithmetic (FP) is arithmetic using formulaic representation of real numbers as an approximation, so as to support a trade-off between range and precision.
* For this reason, floating-point computation is often found in systems which include very small and very large real numbers, which require fast processing times.
* A number is, in general, represented approximately to a fixed number of significant digits (the significand), and scaled using an exponent in some fixed base.
* You may get slightly off answers, so watch out for things like x == 2.0!
* https://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/
* Floating point number example: 3.2
* Floating point literal number example: 4 / 5

#### 1.4.5.1 Expressions and operators

An operator performs an action, e.g., the + in 4 + 5
An operand is operated upon, e.g., the 4 and 5 in 4 + 5
Operands and operators can be combined into expressions, which themselves are evaluated and then “return” objects.

04-ExpressionsTypes/03_numeric.py

#!/usr/bin/python3
# -*- coding: utf-8 -*-

x = 4
print(type(x))
print(id(x))
print(x)

y = 4.0
print(type(y))
print(id(y))
print(y)

# If I type 7, what is the type of z???
z: str = input()
print(type(z))
print(id(z))
print(z)

# Type-hint comment, optional for now, but recommended
# Explicit is better than implicit!!
a: int = 4
b: float = 4.0

# Assignment statements
# An assignment statement assigns a variable with the value of an expression.
# [var] = [arithexpr]
# Left must be a variable, right an arithmetic expression.
# Below,  the third statement assigns a with 2 times a's current value.
# If a was 4, the statement assigns a with 2 * 4, or 8.

print(a)
# variables can be re-assigned to themselves
a = 2 * a

# variables can be re-assigned to other values
b = (4 + 3) / 2
print(b)

Expressions produce (return) values upon evaluation.

### 1.4.6 Numeric operations

04-ExpressionsTypes/04_loan.py

#!/usr/bin/python3
# -*- coding: utf-8 -*-

# Left must be a variable, right an arithmetic expression.
# Arithmetic operators: +, -, *, /, %

# An assignment statement’s right side can be an arithmetic expression:
# Ex: x = (3 * y) + (z / 2).
# This over-simplified program below computes the total cost of a loan,
# given the loan amount and interest.

loan_amount: float = float(input())
interest_rate: float = float(input())
total_cost: float = loan_amount + (loan_amount * interest_rate)
print(total_cost)

#### 1.4.6.1 Integer division

https://en.wikipedia.org/wiki/Division_(mathematics)#Of_integers
https://en.wikipedia.org/wiki/Division_(mathematics)#Of_real_numbers

++++++++++++++++++++
Cahoot-04.1
https://mst.instructure.com/courses/58101/quizzes/55317

#!/usr/bin/python3
# -*- coding: utf-8 -*-

# Integer versus float division
fahr: int = int(input())
# despite calling celc a float, it labels an int.
celc: float = (5 // 9) * (fahr - 32)
print(celc)
• When an int (integer type) is divided with // by another int, the result is an int.
• See program to convert Fahrenheit to Celsius above.
• Regardless of the value of fahr that is used, celc will be assigned 0.
• Both of the literal constants 5 and 9 are stored by the compiler as integers.
• Integer division (//) will give you 0 (9 goes into 5 zero times), and 0 times anything is 0.
• The solution is to use the expected float division (/)
04-ExpressionsTypes/06_convert_good.py
#!/usr/bin/python3
# -*- coding: utf-8 -*-

# Integer versus float division
fahr: float = float(input())
celc: float = (5.0 / 9.0) * (fahr - 32)
print(celc)

Note: Watch out for deprecated python2 code, where this // vs. / pattern is reversed.

#### 1.4.6.2 Type conversion / Casting

Some implicit conversions are automatically performed:
04-ExpressionsTypes/07_conversions.py

#!/usr/bin/python3
# -*- coding: utf-8 -*-

integer1: int = 3
print(type(integer1))

float1: float = 4.0
print(type(float1))

print(type(float1 + integer1))  # gives a float
print(type(float1 - integer1))  # gives a float
print(type(float1 * integer1))  # gives a float
print(type(float1 / integer1))  # gives a float
print(type(integer1 / float1))  # gives a float
print(type(float1 % integer1))  # gives a float
print(type(integer1 % float1))  # gives a float

print(type(float(integer1)))  # gives a float
print(type(int(float1)))  # gives an int, truncatitng decimal points

If conversion is from a floating-point type to an integer type, the value is truncated (the decimal part is removed).

++++++++++++++++++++
Cahoot-04.2
https://mst.instructure.com/courses/58101/quizzes/55318

#### 1.4.6.3 Division by 0

https://en.wikipedia.org/wiki/Division_by_zero
Typically “undefined” and to be avoided!

#!/usr/bin/python3
# -*- coding: utf-8 -*-

print(5 / 0)

# produces an error in python
# >>> print(5/0)
# Traceback (most recent call last):
#   File "<stdin>", line 1, in <module>
# ZeroDivisionError: division by zero

#### 1.4.6.4 Modular arithmetic

++++++++++++++++++++
Cahoot-04.3
https://mst.instructure.com/courses/58101/quizzes/55319

https://en.wikipedia.org/wiki/Modular_arithmetic
In mathematics, modular arithmetic is a system of arithmetic for integers, where numbers “wrap around” upon reaching a certain value, the modulus.

(11 + 4) % 12 = 3

mod The mod operator, %, works this way:

• a mod b produces the remainder after a is divided by b.

• 4 % 7 is 4 (since 4 / 7 is 0 with remainder 4)

• 7 % 3 is 1 (since 7 / 3 is 2 with remainder 1)

• 27 % 3 is 0 (since 27 / 3 is 9 with remainder 0)

• For a positive integer n, two numbers a and b are said to be congruent modulo n, if their difference a - b is an integer multiple of n (that is, if there is an integer k such that a − b = kn). This congruence relation is typically considered when a and b are integers, and is denoted:
$$a\equiv b \pmod {n}$$

• The number n is called the modulus of the congruence.

• The congruence relation may be rewritten as $$a=kn + b$$

• $$a\equiv b \pmod {n}$$
asserts is that a and b have the same remainder when divided by n. That is,

• $$a = p n + r$$
• $$b = q n + r$$
• Example: $$38 \equiv 14 \pmod {12}$$

• because 38 - 14 = 24, which is a multiple of 12, or,
• equivalently, because both 38 and 14 have the same remainder 2 when divided by 12.

Example of mod 9:

mod and division by 10s to choose place

1. Suppose you read in an integer from a user into a variable named x.
2. Assume x is 5 digits long, and let’s represent it as x=abcde.
3. So, e is the “ones” digit, d is the “tens” digit, etc.
4. Thus, we don’t know any of these digits at initial run.
5. But suppose that we need to know, for example, the tens digit, d, during run-time.
6. How can we extract that from the value x, entered by the user at run-time?
7. Well, x % 100 is the integer de.
8. This is because 100 goes into x abc times with a remainder of de.
9. Now, de // 10 is d. That is, 10 goes into de d times.

x = int(input())
tens_digit: int (x % 100) / 10  # assigns tens digit of x to variable tens_digit

## 1.5 Operator precedence

When python evaluates mathematical expressions for their resulting value, the following defines order of precedence, where higher/earlier in the list takes precedence over lower/later:

() Parentheses
** Exponentiation (raise to the power)
~ + - Complement, unary plus, and minus (method names for the last two are +@ and -@)
* / % // Multiply, divide, modulo, and floor division
+ - Addition and subtraction
>> << Right and left bitwise shift
& Bitwise ‘AND’
^ | Bitwise exclusive OR' and regularOR’
<= < > >= Comparison operators
== != Equality operators (<> is like != in python2)
= %= /= //= -= += *= **= Assignment operators
is is not Identity operators
in not in Membership operators
not or and Logical operators

However, you should use () even when you don’t need to.
Explicit is better than implicit!

## 1.6 Built in functions and random numbers

++++++++++++++++++++
Cahoot-04.4
https://mst.instructure.com/courses/58101/quizzes/55320

A function is like a named block of code that can be “called” (a.k.a. executed) by referencing its name as follows:

function(arguments)

These are some pre-written importable functions in Python:

04-ExpressionsTypes/08_random.py

#!/usr/bin/python3
# -*- coding: utf-8 -*-

# using someone else's code:
import random

# Built-in functions generate random numbers.
# Each call specifies a range and returns a new random number within that range.
print(random.randint(0, 5))
print(random.randint(0, 5))
print(random.randint(0, 5))
print()

# To enable reproducible programs,
# a programmer can set a seed,
# as in random.seed(7),
# prior to any calls to random.randint().
# Then, for any run of the program,
# the sequence of "random" numbers is always the same for that seed.
# If no seed is provided, Python3 uses the what as the seed,
# yielding the expected “randomness” for each run of the program?

random.seed(7)
print(random.randint(0, 5))
print(random.randint(0, 5))
print(random.randint(0, 5))
print()

random.seed(7)
print(random.randint(0, 5))
print(random.randint(0, 5))
print(random.randint(0, 5))

# Setting seed first is optional.
# Seed typically only set once, at start of program (if at all)

+++++ Lecture 1 ends here +++++
+++++ Lecture 2 starts here +++++

## 1.7 Example program: Caesar cipher

https://en.wikipedia.org/wiki/Caesar_cipher
The mod operation is important (and actually worth mastering)!

• History of Wheel Cipher (more than 2000 years old)

### 1.7.1 History of Wheel Cipher: Caesar Cipher

• Wheel cipher is also called Caesar cipher
• It was developed more than two thousand years ago
• It is also called “shift cipher” or “rot cipher” (rotation)

### 1.7.2 Algorithm

The Outer Wheel

* English alphabet of 26 letters
* Do we start at 0 or 1?

The Inner Wheel

The inner wheel is used to determine the number of shifts

Spin the Inner Wheel

For an animation, see:
https://inventwithpython.com/cipherwheel/

#### 1.7.2.1 How to Determine an Encryption/Decryption Key

• Each spin gives an encryption/decryption mapping
• A mapping is equivalent to shifting each letter a fixed number of times

#### 1.7.2.2 Encrypt with the cipher wheel

1. Take each letter from the plain-text and match it with the letter on the outer wheel
2. Replace the letter with the corresponding letter on the inner wheel

#### 1.7.2.3 Decrypt with the cipher wheel

1. Take each letter from the cipher-text and match it with the letter on the inner wheel
2. Replace the letter with the corresponding letter on the outer wheel

### 1.7.3 How to formalize and “compute” the Caesar Cipher

This is the Caesar encoding: each letter gets a number, like ASCII, but simpler.

There are two different bugs in the below programs, that only occurs sometimes, related to one of the more confusing operators we’ve covered above!
Can you find them??

#### 1.7.3.1 Key generation code (bug 1: logic error?)

04-ExpressionsTypes/09_keygen.py

#!/usr/bin/python3
# -*- coding: utf-8 -*-

import random

print("Your randomly chosen Caesar cipher key is: ")
print(random.randint(0, 25))
print("\n Share securily with your communication partner, and don't tell anyone else\n")

#### 1.7.3.2 Encryption and decryption code (bug 2: logic error)

Caesar encoding is similar to ASCII encoding, but
~~~
a is 0,
b is 1,
c is 2,

z is 25
~~~

The letters of “hi” in Caesar encoding are what?

Q: “hi” encrypted with a key of 1 is what?
A: “ij”

Q: “hi” encrypted with a key of 1 is what?
A: “jk”

##### 1.7.3.2.1 Encrypt code
• There is an actual logic error, a type of bug, which produces incorrect encryption, but only sometimes!
• Lesson:
• Test with lots of different input types!
• Just because it works on one set of inputs, does not mean your program is correct.

04-ExpressionsTypes/10_encrypt.py

#!/usr/bin/python3
# -*- coding: utf-8 -*-

print("You have to run this program, once for every character of your message")

key: int = int(input("\nEnter your Caesar key in numeric form (1-25): "))

encoded_char: int = int(
input(
"\nEnter Caesar encoded number corresponding to one character of your message:"
)
)

print("\nThe translation of your character is:")

# To encrypt:
print(encoded_char + key)
##### 1.7.3.2.2 Decrypt code

04-ExpressionsTypes/11_decrypt.py

#!/usr/bin/python3
# -*- coding: utf-8 -*-

print("You have to run this program, once for every character of your message")

key: int = int(input("\nEnter your Caesar key in numeric form (1-25): "))

encoded_char: int = int(
input(
"\nEnter Caesar encoded number corresponding to one character of your message:"
)
)

print("\nThe translation of your character is:")

# To decrypt:
print(encoded_char - key)

* Besides the 2 above intentional bugs, what are some missing features with this version of the Caesar cipher?
* Are there any features you’d like for this to be more usable, convenient, easy, or functional?

## 1.8 Language focus

To be stepped through in the python3-spyder IDE and/or python3-pudb debugger:
04-ExpressionsTypes/12_expressions.py
++++++++++++++++++++
Cahoot-04.5
https://mst.instructure.com/courses/58101/quizzes/55384

04-ExpressionsTypes/13_logic.py
++++++++++++++++++++
Cahoot-04.6
https://mst.instructure.com/courses/58101/quizzes/55385

04-ExpressionsTypes/14_string_literals.py
++++++++++++++++++++
Cahoot-04.7
https://mst.instructure.com/courses/58101/quizzes/55386

04-ExpressionsTypes/15_conversions.py
++++++++++++++++++++
Cahoot-04.8
https://mst.instructure.com/courses/58101/quizzes/55387

04-ExpressionsTypes/16_assignment.py
++++++++++++++++++++
Cahoot-04.9
https://mst.instructure.com/courses/58101/quizzes/55388

Next: 05-Branches.html