---
# System prepended metadata

title: Python Introduction
tags: [Python, CMSC23, Notes]

---

# Python Introduction

## starting python from the command line

To start python repl on the command line use `python` command. Make sure you have the path to python saved in your OS's `PATH` environment variable. Some python distributions have to be started with the specific version specified. For these distributions use the command `python2` or `python3`.

```
> python
Python 3.8.5 (tags/v3.8.5:580fbb0, Jul 20 2020, 15:43:08) [MSC v.1926 32 bit (Intel)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>>
```

To run a python script (`*.py`), use the same `python` command followed by the path to the script

```
> python script.py
```

## python syntax

Lets start by discussing simple python syntax. Python code looks like this

```python
x = 5
value = 6
print(x + value)
```

The first thing you notice is that python doesn't use semi-colons to separate statements, python finds the semi-colon redundant since programmers separate statements using newlines. Here python interprets three separate statements, `x = 5` and `value = 6`, and `print(x + value)`. The last line is pythons way of printing to an output stream.

## python atomic types

#### Integers

An `int` in python is of course an implementation of an integer in math. There is no limit to the size of a python integer (except for system memory).

```python
9999999999999999999999999999 + 1
```

To  check the type of a python object, you can use the function `type`.

```python
type(9999999999999999999999999999 + 1)
```

```python
int
```

#### Floating-Point Numbers

A python `float` is an implementation of a floating point number. Numbers written in scientific notation are also floating point numbers:

```python
type(4.2)
```

```python
float
```

```python
type(4.2e5)
```

```python
float
```

#### Complex numbers 

Complex numbers exist in python. The complex number $a + bi$ is represented as `a + bj` where `a` and `b` are integer literals.

```python
type(4 + 3j)
```

```python
complex
```

#### Strings

A python `string` is a sequence of characters. Strings can be enclosed by single-quotes or double quotes (but you must pair single-quotes with single-quotes and double-quotes with double quotes). 

```python
type(`This`)
```

```python
string
```

```python
type("That")
```

```python
string
```

#### Boolean

A `bool` in python is either `True` or `False`.

```python
type(True)
```

```python
bool
```

```python
type(False)
```

```python
bool
```

#### Python typing

Python is a type inferred, mostly strongly typed, dynamically checked language.

There are no explicit declarations in python. The first assignment to a python identifier is its declaration. By assigning a value to that identifier python infers the type based on the value assigned to it. Changing the value assigned to  an identifier changes its type as well.

```python
x = 4
print(type(x))
x = 'this'
print(type(x))
```

```python
<class 'int'>
<class 'str'>
```

Python is mostly strongly typed, which means that most type conversions result in a type error.

> **Exercise (Type Coercions)**
>
> What do you think would happen if you try to add different types in python? Without actually executing anything, predict the expected results of the following type surveys,
>
> 1. `type(3 + 3.0)`
> 2. `type(3 + '3')`
> 3. `type(3 + True)`
> 4. `type(4/2)`
> 5. `type(4/0)`
>
> After writing the expected results, write the actual results and compare them to your expectations.

Python is dynamically checked, this means that it checks for type safety during runtime. This means that code that cannot be reached is not type checked. Which means that the following results in a type error:

```python
print("this"+True)
```

But the following doesn't:

```python
if 1 == 0:
	print("this"+True)
```

If you choose to do so you can annotate the type of an identifier using the following syntax:

```python
x:int = 3
```

## Iterable types

#### List

A python list, is a vector of any mix of python objects:

```python
type([1,2,3,"4",True,[]])
```

```python
list
```

The length of a list can be found using the built-in function `len` which accepts an iterable type and returns an integer which is the length:

```python
len(l)
```

```python
6
```

Lists can be concatenated using the `+` operator:

```python
[1,2,3]+[4,5]
```

```python
[1, 2, 3, 4, 5]
```

You can check if an element exists in an iterable using the `in` operator:

```python
2 in [1,2,3,"4",True,[]]
```

```python
True
```

List elements are accessed similar to c arrays.

```python
l = [1,2,3,"4",True,[]]
l[2]
```

```python
3
```

Negative indices count from the right

```python
l[-1]
```

```python
[]
```

You can extract a copy of a sublist using the following indexing methods

- `list[n:m]` produces a sublist from index n to m (including the nth element but excluding the mth element).
- `list[:m]` equivalent to `list[0:m]`.
- `list[n:]` equivalent to`list[n:-1]`.

```python
l[1:3]
```

```python
[2,3]
```

> **Exercise (advanced list slicing)**
>
> Play around with lists and test the behavior of the list access operator `::`. What is the result of the expression `list[a::b]` where `list` is a `list` and `a` and `b` are `int`s? What about `list[a::]` and `list[::b]`.

A python list is mutable, you can change the value of a specific element or range:

```python
l[1] = 0
l
```

```python
[1, 0, 3, '4', True, []]
```

You can delete an element on a specific index or range:

```python
del l[2]
l
```

```python
[1, 0, '4', True, []]
```

```python
del l[2:4]
l
```

```python
[1, 0, []]
```

#### Tuples

A python `tuple` is an immutable collection of objects. Tuples are written surrounded by parentheses instead of square brackets. 

```python
t = (1,2,True,5,6)
```

Tuple elements and subtuples can be accessed the same way with lists. 

```python
t[2]
```

```python
True
```

```python
t[1:4]
```

```python
(2, True, 5)
```

Tuples can also be concatenated similar to lists.

```python
(1,2,3) + (5,6)
```

```python
(1, 2, 3, 5, 6)
```

Tuples are immutable so you cannot change the elements of a tuple.

```python
t[1] = 2
```

```python
...
TypeError: 'tuple' object does not support item assignment
```

#### Dictionaries

A python dictionary is a collection of key value pairs. It is written surrounded by curly braces. The each pair is written, `<key>:<value>`

```python
d = {"a":1,0:"this","b":True}
```

The elements of a dictionary can be accessed using the keys:

```python
d["a"]
```

```python
1
```

```python
d[0]
```

```python
"this"
```

## Selection expressions

Python's if else statements follow this pattern (the else part can be omitted if the execution does not need an alternative). The colon and whitespace are part of the syntax and are mandatory. Every tabulated line is inside the scope of the `if` part or the `else` part.

```python
if boolean:
    truePart
else:
    alternativePart
```

Nesting if else statements in python has a shortcut. The following code:

```python
if False:
    print("won't print")
else:
    if False:
        print("won't print too")
    else:
        print("will print")
```

Can be summarized to this:

```python
if False:
    print("won't print")
elif False:
    print("won't print too")
else:
    print("will print")
```

Python also understands ternary expressions:

```python
x = "this" if True else "That"
print(x)
```

```python
this
```

The value of `x` will depend on the value of the condition. Since this is true, the whole ternary expression reduces to `"this"`

## Iteration

Python's `while` loop is similar to C's while loop. The block of code inside the while loop will be executed repeatedly until the condition becomes false.

```python
i = 0
while i < 5:
    print(i)
    i+=1
```

```python
0
1
2
3
4
```

Python's `for` loop is different from C. Python's `for` loop a collections loop or Java's `foreach` expression.

```python
for i in ["this",2,True,4]:
    print(i)
```

```
this
2
True
4
```

This for loop can be interpreted in common language as, *For every element $i$ in ["this",2,True,4], print $i$*. Inside the for loop, the value of `i` refers to the elements inside the list. At the first execution of `print(i)`, `i` refers to the first element of the list. At the second execution `i` refers to the 2nd element of the list. and so on until it exhausts the list.

A common pattern for a `for` loop is something like this:

```python
for i in range(0,5):
    print(i)
```

```python
0
1
2
3
4
```

The function `range` produces a list of integers, starting from `0` until the `4`. `range(0,5)` can even be shortened to `range(5)`. 

## Python functions

Python functions are written using the following syntax:

```python
def f(parameters):
	body
```

For example creating the add function:

```python
def add(x,y):
    return x * y
```

## Type Annotations

Type annotations mark the type of identifiers.

```python
x:int = 1
s:str = "Hey"
```

You can also annotations to set the parameter types and the return type of a function

```python
def add(x:int,y:int) -> int:
    return x * y
```

Type annotations are only annotations, you don't have to write them. But writing them will help you make sense of a complicated system

## Python library import

To import python libraries use the keyword math followed by the python library name. As much as possible write imports at the topmost part of your python files

```python
import math
math.ceil(1.5)
```

```
2
```

You can give the library name a shorter alias for convenience

```python
import math as m
m.ceil(1.5)
```

```
2
```

You can also choose not to import the whole library, just specific classes and functions. When you do this you must directly access the class or function without the dot reference.

```python
from math import floor,ceil
floor(1.2) + ceil(1.5)
```

```
3
```

## Python comments

Comments are made using the octothorpe/pound/hash/sharp symbol (`#`)

``` python
1 + 1 #comments are made using the octothorpe/pound/hash/sharp symbol
```

