CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In

Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.

| Download
Views: 3376
Image: ubuntu2004
Kernel: Python 3 (system-wide)

Introduction

Camilo Garcia Trillos - 2020

In this notebook

In this notebook we will deal mainly with syntax and semantics in Python

  • we explore some basic expressions in Python

  • we introduce some types

  • we introduce some of the most important control flow statements


Jupyter Notebooks

This is a Jupyter notebook. It is an interface allowing us to combine code (in this case Python) and formatted text in a unified way.

The basic unit in a notebook is a cell. You are right now reading the content of a "Markdown" cell, designed to input formatted text. There are also 'Code' cells, designed to input executable code.

  • You can add a new cell by clicking on the + button on the lower toolbar. Alternatively, press A (for inserting a cell above or B for inserting a cell below).

  • Click on the cell to start editing it. Alternatively, you can move between cells with your arrows (note the blue highlighter on the right), and click Enter to edit one cell.

  • To change the type of cell (from 'code' to 'markdown', use the choice tab on toolbar while on the cell.

  • To run the code on a cell (or to format the text you input) you can either click on the 'play' button on the lower toolbar, or hit Ctrl+Enter

  • You can also cut, paste and move cells. Hover your mouse over the buttons on the lower toolbar to know more.

To familiarise yourself with the interface:

  • On the upper toolbar, click on Help> User Interface tour, and follow the instructions to get an overview of this interface.

  • For more information on the Python notebook in general, click on Help> Notebook help

  • To know more about the keyboard shortcuts, go to Help> Keyboard shortcuts

Short Exercise: Add a cell, write '1+1' and execute this program. Then, select the cell again and change its type to Markdown You can finally delete this cell.


Basic expressions

Having briefly reviewed the interface, let us start our review of Python. We will introduce different commands and explain their use: please run the commands as we learned above, and understand their function.

We start by saying hi....

print ("hello world!!!")
hello world!!!

The above line has a statement that asks Python to execute the method/function print receives a 'string' and then shows the string as an output. Strings in Python can be denoted by either double or single quotations. To verify this, let us ask Python to compare if a string with single quotations is the same as a string with double quotations.

The comparison operator is ==

"hello world!!!"=='hello world!!!'
True

The output of this comparison is True, which means that the two values are the same. Let me remark that in general Python compares 'values' and not 'references'. This is an important distinction from other programming languages.

The operator =, with a single equal, is used to make an assignment:

message = 'hello again!!!' # This is an assignment print(message)
hello again!!!

Above we are creating an object that is initialised to contain a string. There is no need to declare the type of the object message. It will be then treated as a string, which is why the function print works.

Note in passing that we can make comments on a code cell by preceding the comment by #

Python is an object-oriented programming language. Objects of a given type or class have associated a certain number of methods that act on them. Take for example the following code

print(message.upper())
HELLO AGAIN!!!

The method upper is available for string objects and turns every character in their uppercase version. Here is another one

'NOW TO LOWERCASE'.lower()
'now to lowercase'

Note that we did not need to use print above. By default the result of the last command of a cell that returns a value is displayed. Let us finally remark that we can use the triple quotes to write text in several lines.

print("""NOW WITH SEVERAL LINES""".lower()) """NOW WITH SEVERAL LINES""".lower()
now with several lines
'now\nwith\nseveral\nlines'

Compare the versions with and without print.

Main numeric data types

Having introduced the string data type, let us look now at the main numerical types: Integer, complex, float, bool

Let us introduce them while we do some numerical calculations: adding, substracting, division, multiplication, modulo, power

0.1+0.2
0.30000000000000004

The operation is performed using 'floats', that is numerical representation of real numbers. Digital computers have only a finite number of digits to represent a real, and so there are roundup errors. The above code shows that operations that for us are simple to do 'without roundup' must be done only approximately by a computer.

We can also have operations with complex numbers. In Python, the imaginary number −1\sqrt{-1} is denoted by j

a=1+1j b=2 print(2*a+b-1) print(type(a)) print(type(b))
(3+2j) <class 'complex'> <class 'int'>

The above operation is executed in the complex numbers (or more appropriately the float version of the complex numbers). The precedence in executing the operations follows the usual convention: first, parentheiss are solved from inner to outer ones, then, for common mathematical operators de precedence is: power, then division and multiplication, then sum and substraction.

Also, note the function 'type': it returns the type of a given object. While we are at it, let us check the name of other types we have encountered before

print(type('Hello'), type(2), type(0.2), type(True))
<class 'str'> <class 'int'> <class 'float'> <class 'bool'>

Division is a bit special. Observe the result of the following operations:

print(20/7) print(20//7)
2.857142857142857 2

Indeed, the double slash / signals integer division. Note that we can use it also with float numbers

print(2.0/0.7) print(2.0//0.7)
2.857142857142857 2.0

The modulo operator, that allow us to obtain the residual of a division, is also included:

20 % 7
6

Powers are defined using the operator double star '**'

print(2**-1, 2**0, 2**1, 2**2) print(2**-1, 2.**0, 2.**1, 2.**2)
0.5 1 2 4 0.5 1.0 2.0 4.0

Short exercise: Remark the difference between the two instructions above. What can you say?

Main structured types

Python comes pre-loaded with some structured types to keep collections of objects. The main ones are: List, tuple, dictionary, set

It is very important to learn about the features of each one of these structured types, as many errors in programming come from misunderstanding them

Lists

Lists are created using the squared brackets []. They are a mutable collection of objects, i.e., after created we can modify the state of the object.

number_list = [1,2,3,4,5,6] print(number_list)
[1, 2, 3, 4, 5, 6]

Any element on a list can be accessed trough the square bracket operator. The indexation on the vector starts from zero

print("The first element:", number_list[0], "\n the third element:", number_list[2], "\n the last element:", number_list[-1])
The first element: 1 the third element: 3 the last element: 6

As shown above, we can provide negative numbers to the position. It simply access the position as counted from the end. Try with some other positions until you are sure how it works.

It is also possible to get slices of a vector, by using [m,n]: this returns all elements in the vector starting from position m and ending in position n-1

# The slicing operator works like a closed-open interval [ , ) print(number_list[1:4]) print(number_list[4:5])
[2, 3, 4] [5]

In the above, if no number is entered, the corresponding extreme is implicitly understood. Take a look at these examples:

print(number_list[:4]) print(number_list[4:]) print(number_list[:])
[1, 2, 3, 4] [5, 6] [1, 2, 3, 4, 5, 6]

Slices can also be used to run over the elements of a list in inverse order, or following a sequence

print('Odd numbers:',number_list[::2]) print('Multiples of 3:',number_list[2::3]) print('Even numbers between 2 and 5:',number_list[1:5:2]) print('Inverse order:',number_list[::-1])
Odd numbers: [1, 3, 5] Multiples of 3: [3, 6] Even numbers between 2 and 5: [2, 4] Inverse order: [6, 5, 4, 3, 2, 1]

Lists can also be extended.

number_list.extend([7,8,9]) print(number_list)
[1, 2, 3, 4, 5, 6, 7, 8, 9]

List assignments are passed by reference. This means that when we assign them to another variable, we only copy an adress to access them in memory. Here is an example

another_list=number_list print(number_list) print(another_list)
[1, 2, 3, 4, 5, 6, 7, 8, 9] [1, 2, 3, 4, 5, 6, 7, 8, 9]
another_list[0]=-1 # This asigns -1 to the first position
print(number_list) print(another_list)
[1, 2, 3, 4, 5, 6, 7, 8, 9] [-1, 2, 3, 4, 5, 6, 7, 8, 9]

Hence, despite the name, another_list points to the same list. However, lists are compared by value, as seen in this example:

list1=[1,2,3] list2=[-1,2,3] print('Checks if both lists are equal (they are not):',list1==list2) list2[0]=1 print('Checks if both lists are equal (they are):',list1==list2)
Checks if both lists are equal (they are not): False Checks if both lists are equal (they are): True

Lists can be concatenated with the operator +

list1+list2
[1, 2, 3, 1, 2, 3]

Note in particular that this does not add up elements in a list. In particular, we cannot concatenate an integer to a list.

list1+1 # This command generates an error
--------------------------------------------------------------------------- TypeError Traceback (most recent call last) <ipython-input-34-66e780646bd1> in <module> ----> 1 list1+1 # This command generates an error TypeError: can only concatenate list (not "int") to list

But we can do as follows

list1 + [1]
[1, 2, 3, 1]

Also, note that concatenation does not modify the original list but creates a new one: i.e., the original list does not change.

print(list1)
[1, 2, 3]

The * operator repeats a list formation

list3 = 3*[1,2,3] print(list3) print(2*list3)
[1, 2, 3, 1, 2, 3, 1, 2, 3] [1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3]

We can also have lists of lists (but they are not matrices!) and lists with different types and lengths

multilist = [[1,2,3],[4,5,6],[7,8,9]] print(multilist) print(multilist[1][1])
[[1, 2, 3], [4, 5, 6], [7, 8, 9]] 5
mixed_list = [[1,2,3],1,'a',1+2j] print(mixed_list) print(mixed_list[1])
[[1, 2, 3], 1, 'a', (1+2j)] 1

We end up our overview of lists by looking at some of the methods associated with it. The best method to discover them is to write a list and put a dot and the end and click TAB (might not work depending on your browser and OS)

Some of the more relevant are shown below

list_a = [4,3,2,1,4] print("The list:",list_a) list_a.sort() # This sorts the elements of the list print("Ordered list:",list_a) print("Size:", len(list_a)) # Getting the size, i.e. number of elements of the list list_a.append(4) # Adding a new element at the end of the list print("List with additional 4:",list_a) elem = list_a.pop() # Take out the last element of the list print("Element and list after pop:",elem, list_a)
The list: [4, 3, 2, 1, 4] Ordered list: [1, 2, 3, 4, 4] Size: 5 List with additional 4: [1, 2, 3, 4, 4, 4] Element and list after pop: 4 [1, 2, 3, 4, 4]

Strings

We have enconuntered strings before. They can be manipulated in a way closed to lists.

message = 'Hello again!' print(message) print(message[1:5]) print(message[-1::-1]) print(len(message))
Hello again! ello !niaga olleH 12

Some methods are different and specific of strings

print(message) print(message.upper()) #Turn to pper case print(message.title()) #Capitalise initial words print(message.split(' ')) #Suubstrings separated by a space print(message.replace('!','?')) #Replace ! with ? print(message.find('aga')) # returns the first position where the sub-stringg print(message.find('opo')) # returns -1 if no position is found
Hello again! HELLO AGAIN! Hello Again! ['Hello', 'again!'] Hello again? 6 -1

Sets, tuples and dictionaries

Finally, let us look at some of the other structures available on Python.

A set is a structure where elements are unique. They are created with curly braces { }

We can add elements, remove elements, and perform mathematical operations like union, intersection, symmetric difference.

set1 = {1,2,3,3} print(set1) set1.add('aa') print(set1)
{1, 2, 3} {1, 2, 3, 'aa'}
set2 = set([1,2,8,10]) # You can turn lists into sets print("set2:",set2) print("set1 union set2", set1|set2) print("set1 intersection set2", set1&set2) print("set1 minus set2", set1-set2) print("set2 minus set1", set2-set1)
set2: {8, 1, 2, 10} set1 union set2 {1, 2, 3, 8, 'aa', 10} set1 intersection set2 {1, 2} set1 minus set2 {'aa', 3} set2 minus set1 {8, 10}

Note that sets do not have indices, so the following code does not make sense

set1[1] # this generates an error
--------------------------------------------------------------------------- TypeError Traceback (most recent call last) <ipython-input-45-c61df569ed49> in <module> ----> 1 set1[1] # this generates an error TypeError: 'set' object is not subscriptable

A tuple can be created using round braces.

Tuples are similar to a list except that elements cannot be changed once created: they are an example of an immutable structure

tuple1 = (1,2,3) print(tuple1) print(tuple1[1])
(1, 2, 3) 2
tuple1[1]=3 #this generates an error ... tuples are immutable
--------------------------------------------------------------------------- TypeError Traceback (most recent call last) <ipython-input-47-96b0480c90ed> in <module> ----> 1 tuple1[1]=3 #this generates an error ... tuples are immutable TypeError: 'tuple' object does not support item assignment

Finally, a dictionary is a structure to connect keys and outputs. It is defined also using the curly braces { }, but instead of simply listing the elements, we list couples of the form key:value. Keys can be any other immutable object (like numbers and basic strings).

The key element is a generalisation of an index. Here are some examples:

mydict = {50:2,'key2':'aa', 'a':1} print(mydict)
{50: 2, 'key2': 'aa', 'a': 1}
print(mydict['key2']) print(mydict[50]) print(mydict[50]+mydict['a'])
aa 2 3

Here are some examples of methods for dictionaries

mydict[50]=10 #Changing the entry with key 50 to 10 print(mydict) mydict.pop(50) # Taking out the entry with key 50 print(mydict) mydict.update({2:'c'}) # Adding an entry with key the number 1 and value 'c' print(mydict)
{50: 10, 'key2': 'aa', 'a': 1} {'key2': 'aa', 'a': 1} {'key2': 'aa', 'a': 1, 2: 'c'}

Basic control flow statements

To close this notebook, we will look at the essential control flow statements (i.e. commands to determine the flow of a program) in Python. The main ones are:

  • Conditionals: if (condition): --- elif(condition): --- else:---

  • Conditional loops: while (condition): --- else:---

  • Automatic loops: *for (iterative): ---

The structure is determined by indentation

Let us look at some examples: you are encouraged to change the inputs and modify the code until you undertstand how the flow of commands works.

#Determine the bigger between two numbers a=4 b=5 if a > b: print('I am running the first case') print(str(a) +' is larger than ' + str(b)) elif b >a: print('I am running the second case') print(str(b) +' is larger than '+ str(a)) else: print('I am running any other case') print('Both are equal to '+str(a))
I am running the second case 5 is larger than 4

In the above code, the code indented after if is run only when the condition is satisfied. The code indented after elif is run only if a>b. The code indented after elif is run only if a>b is not satsified (i.e. a<=b) and b>a. Finally the code after else is run only if neither of the conditions is satsified (i.e. when a==b)

# Print if the numbers of a list are even or odd m_list =[1,5,6,3,2] for i in m_list: if i%2 == 0: print(i, ' is even' ) else: print(str(i) + ' is odd' )
1 is odd 5 is odd 6 is even 3 is odd 2 is even

We can modify the above example to act on the first n natural numbers. To do this, we introduce the command range

# Print if the numbers of a list are even or odd n=5 for i in range(n): if i%2 == 0: print(str(i) + ' is even' ) else: print(str(i) + ' is odd' )
0 is even 1 is odd 2 is even 3 is odd 4 is even

In general, range can generate a pattern from a given start, to a given end with certain step: range(start,end,step). If step is not given, step=1.

Here is a version of the same code using while and avoiding range.

i = 0 n = 5 while i<n: if i%2 == 0: print(str(i) + ' is even' ) else: print(str(i) + ' is odd' ) i+=1
0 is even 1 is odd 2 is even 3 is odd 4 is even

Here is our last example combining many of the elements we introduced before. It prints the primes less or equal to a given number using a cribe algorithm. Take it as an exercise to find out how (and why) it works. You might need some time to understand it, but don't get discouraged by this

# Print the prime numbers lower or equal to max_num, using a cribe algorithm max_num = 30 aux_cribe = (max_num+1)* [True] for i in range(2,max_num+1): if aux_cribe[i]: print (i) aux_cribe[i] = False j = 0 while i**2 +j*i < max_num+1: aux_cribe[i**2 +j*i] = False j = j+1
2 3 5 7 11 13 17 19 23 29

Exercises

  1. Write your own code to sort a list of numbers. Compare with the method sort() we introduced before (for example on the list [5,5,1,2,1,5,10])

def list_sort(a): i = 0 while i<len(a)-1: j = i+1 while i<j<len(a): if a[i]>a[j]: a[i],a[j] = a[j],a[i] continue else: j+=1 i+=1 return a list_sort([5,5,1,2,1,5,10])
[1, 1, 2, 5, 5, 5, 10]
  1. Write a code that, for a given tuple (a,b,c)(a,b,c), returns the roots (possibly complex) of the equation ax2+bx+c=0 a x^2 + b x + c = 0

Note: if there is a double root, only one number must be returned.

import cmath def root(a,b,c): if a==0: if b != 0: return -c/b else: return "no root or infinite roots" else: delta = b**2-4*a*c if delta>0: return ((-b+sqrt(delta))/(2*a),(-b-sqrt(delta))/(2*a)) elif delta==0: return -b/(2*a) elif delta<0: return ((-b+cmath.sqrt(delta))/(2*a),(-b-cmath.sqrt(-delta))/(2*a)) root(0,0,3)
'no root or infinite roots'
  1. Starting from two integers, a,ba,b, and an initial value integer 0<x[0]<2b0<x[0]<2^b, generate kk values of a sequence

x[i+1]=(x[i]∗a)mod 2bx[i+1]= (x[i]*a)_{mod\ 2^b}

Note: this is one of the earliest forms of pseudo-randon number generators. See for example this wikipedia article

x=5001 k=10 a=78 b=19 for i in range(k): x = x*a %(2**b) print(x)
390078 17380 307064 358032 139232 374336 362368 477440 15872 189440