Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Download

Jupyter notebook tutorials/Basics/Basics.ipynb

10 views
Kernel: Anaconda (Python 3)

This is a quick review of some basic Python 3 features that you might use in your assignments. Of course, this is far from being exhaustive.

This tutorial follows a example-exercise format. Grey boxes are cells containing code - just place the cursor in any code cell you want to execute, then click on 'Cell/Run' or click on the "play" button.

Python is indentation-sensitive by design – so, please follow the left-indentation that I have used. Otherwise, you will receive an 'IndentationError'. It doesn’t have to be the exact same amount of indentation, however. For example, if you see a tab, it means that there needs to be some left-indentation there; it could be just a single space (tabs are preferred only for clarity). But if you don’t see any indentation, then there shouldn’t be any.

Any line that starts with a '#' is a comment, that is Python does not execute it.

Official Python documentation - https://docs.python.org/3/

A popular book “A byte of python" - http://www.swaroopch.com/notes/Python

BASICS

EXAMPLE 1

Variables & printing

Assigning values to variables, and printing them.

x = 0 print(x) y = 'code' print(y) print(x, y)
0 code 0 code

In Jupyter notebook, executing a block with just a variable name or a function output will automatically print the value.

x + 4
4

EXERCISE 1.1

Assign numerical values to two different variables, multiply them, store the result in a different variable and print it.

a = 1 b = 3 c = a*b print(c)
3

"Concatenation"

This will be a running theme for the data types we look at: how to combine two elements of the type. Numberic types (int and float) are immutable in Python.

x, y = 2, 3 z = x + y print(z)
5
x = 2 x = x + 3 print(x)
5
x = 2 x += 3 print(x)
5

By analogy, you can probably figure out what the -= and *= operators do.

Comparison

4 < 5
True
4 > 5
False
# Need to use the double-equals for comparison 4 == 4
True
4 >= 4
True

EXERCISE 1.2

It is true that for a given integer N, N times N (N squared) is greater than or equal to N. Assign an integer to a variable and show this condition is true.

n = 3 n_sq = n*n n <= n_sq
True

Numerical operations

Division, modulo, exponent

5 / 2 # "Regular" division
2.5
5 // 2 # Integer division
2
5 % 2 # Modulo, returns the remainder
1
5 ** 2 # Exponentiation. This is 5 to the 2nd power.
25

Yes, the all of the shorthand assignment operators work like you would expect

x = 5 x **= 2 print(x)
25

EXERCISE 1.3

Calculate the product of x and its reciprocal (1/x). Verify that the result is equal to 1.0

x = 23 x * (1/x) == 1
True

Functions

Defining functions with def. Note the colon at the end of the arguments list.

def square(x): return x * x square(4)
16

Functions can have optional arguments as well

def pow(x, exponent=2): result = x ** exponent return result print( pow(4) ) print( pow(4,3) )
16 64

EXERCISE 1.4

Define a function called is_even that uses the modulo operator to return whether or not a given integer is even. You'll know your function works if the assertions in the block after run without error.

def is_even(n): return n % 2 == 0
assert is_even(6) assert not is_even(5)

EXAMPLE 2

Conditional logic

Note how the else-if is implemented in python – it is ‘elif’. Also, notice the colon at the end of the conditions which is part of the syntax.

x = 0 if x == 1: print("x is 1") elif x > 1: print("x is > 1") else: print("x is < 1")
x is < 1
# Employing logical AND, OR within conditions x = 1 y = 5 z = 5 if z >= x and z <= y: print("z lies within range [x,y]") if z == x or z == y: print("z lies on the boundary of the range [x,y]")
z lies within range [x,y] z lies on the boundary of the range [x,y]

EXERCISE 2.1

Write a function compare() that accepts two values, and prints if the first value is greater, less than, or equal to the second value.

Example:

>>> compare(4, 5) <<< 4 is less than 5
def compare(x,y): if x > y: print(x, "is greater than ", y) elif x < y: print(x,"is less than", y) else: print(x, "equals", y)
compare(4,5) compare(5,4) compare(7,7)
4 is less than 5 5 is greater than 4 7 equals 7

EXAMPLE 3

Loops

Again note the colon at the end of the loop statements.

i = 0 while i < 10: print(i) i += 1 # equivalent to x = x + 1 print("Final value:", i)
0 1 2 3 4 5 6 7 8 9 Final value: 10
for j in range(0,10): print(j) print("Final value:", j)
0 1 2 3 4 5 6 7 8 9 Final value: 9

Often used in loops, the range() function allows iterating over a sequence of integers.

By itself, range() gives an iterator; we can use list() to iterate through it and return all of the values in a list.

# With one argument, range(x) gives integers in the range [0,x) list(range(10))
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
# With two arguments, range(x, y) gives integers in the range [x, y) list(range(0, 10))
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
# With three arguments, range(x, y, z) gives integers in the range [x, y), counting by z list(range(1, 10, 2))
[1, 3, 5, 7, 9]
# Counting backwards works too list(range(10, 5, -1))
[10, 9, 8, 7, 6]

EXERCISE 3.1

Using a loop, calculate the sum of first 10 positive integers (zero is not positive).

sum = 0 for i in range(1,11): sum += i print(sum)
55

EXERCISE 3.2

Write a loop that goes through numbers 1 through 20, printing only the even numbers

for i in range(1, 21): if i % 2 == 0: print(i)
2 4 6 8 10 12 14 16 18 20

EXERCISE 3.3

A natural number is prime if it has no positive divisors other than 1 and itself. Write a function is_prime() that checks if a number is prime. The assertions afterwards should pass.

def is_prime(n): for i in range(2, n): if n % i == 0: return False return True
assert is_prime(11) assert not is_prime(12)

EXAMPLE 4

List

A list is a sequence of several (possibly unknown quanitity) similar objects. Use square brackets to define a list, and also to access elements by their index. Lists can have elements added to the end with append() or inserted at an arbitrary index with insert(). The del statement deletes an item from a list. The len() function returns the length of a list.

NOTE: All datatypes in Python have their indices starting from 0 as opposed to 1.

names = ['Rob', 'Clara'] ages = [21, 23] print(names, ages) print(len(names), 'Names:', names[0], names[1]) print(len(ages), 'Ages:', ages[0], ages[1])
['Rob', 'Clara'] [21, 23] 2 Names: Rob Clara 2 Ages: 21 23
names = ['Rob', 'Clara'] print(names) del names[0] print(names) names.append('Bob') print(names) names.insert(0, 'Alice') print(names)
['Rob', 'Clara'] ['Clara'] ['Clara', 'Bob'] ['Alice', 'Clara', 'Bob']

The built-in function sorted() will return a sorted copy of a sequence, i.e. it doesn't change the original. Lists also have a .sort() method that sorts in-place.

print(sorted(names)) print(names) names.sort() print(names)
['Alice', 'Bob', 'Clara'] ['Alice', 'Clara', 'Bob'] ['Alice', 'Bob', 'Clara']

Concatenation

Concatenation of lists also works with the + operator:

students = ['Alice', 'Bob', 'Clara'] teachers = ['Fil', 'Clayton'] roster = students + teachers roster
['Alice', 'Bob', 'Clara', 'Fil', 'Clayton']

But since lists are mutable, we can also modify them in place with extend:

roster = ['Alice', 'Bob', 'Clara'] teachers = ['Fil', 'Clayton'] roster.extend(teachers) roster
['Alice', 'Bob', 'Clara', 'Fil', 'Clayton']

Tuple

A tuple can be used to describe the various features of a single object. Suppose a person is to be identified just by their age. Then a simple scalar variable (like the variable x above) would suffice. If a person's name is to be included, then a tuple can be used to store such information. Indexing facility is available, if parts of a tuple must be retrieved. Use circular brackets/parantheses to define a tuple, but as with all sequences, square brackets to access values by their index.

Tuples are "immutable", meaning one can't delete, insert, or append items. As with all sequences, the len() function does return the tuple's length.

person_1 = ('Rob', 21) person_2 = ('Clara', 23) print(person_1, person_2) print('Names:', person_1[0], person_2[0]) print('Ages:', person_1[1], person_2[1])
('Rob', 21) ('Clara', 23) Names: Rob Clara Ages: 21 23
# This will raise an error del person_1[0]
--------------------------------------------------------------------------- TypeError Traceback (most recent call last) <ipython-input-23-cc9e09edf93d> in <module>() 1 # This will raise an error ----> 2 del person_1[0] TypeError: 'tuple' object doesn't support item deletion

Concatenation

Concatenation of tuples works with -- you guessed it -- the + operator.

user_info = ('Alice', 25) account_info = ('[email protected]', 'zyxPasswordHashxyz') record = user_info + account_info record
('Alice', 25, '[email protected]', 'zyxPasswordHashxyz')

Since tuples are immutable, there is no in-place change but the += operator does work.

record += ('6 days ago',) record
('Alice', 25, '[email protected]', 'zyxPasswordHashxyz', '6 days ago')

Notice that a 1-item tuple must have a trailing comma. The following version will fail:

# Raises a TypeError record += ('6 days ago') record
--------------------------------------------------------------------------- TypeError Traceback (most recent call last) <ipython-input-26-cf4bc4c4613a> in <module>() 1 # Raises a TypeError ----> 2 record += ('6 days ago') 3 record TypeError: can only concatenate tuple (not "str") to tuple

Looping over sequences

In other C-like languages, looping over sequences is usually done by iterating over the indices like this:

# DO NOT DO THIS names = ['Alice', 'Bob', 'Carol'] for idx in range(len(names)): print(names[idx])
Alice Bob Carol

We're more civilized here in Python world, so we can use this more readable syntax to loop over a sequence:

names = ['Alice', 'Bob', 'Carol'] for name in names: print(name)
Alice Bob Carol

If you absolutely need the indices, use the enumerate() function

names = ['Alice', 'Bob', 'Carol'] for idx, name in enumerate(names): print(idx, name)
0 Alice 1 Bob 2 Carol

EXERCISE 4.1

Create a list with the following numbers in the same order: 2,7,3,5,9,2,6,15,4. Using a loop, identify the indices in the list with numbers greater than 4. Have the loop insert these indices in a list as and when they are found, and print it at the end of the loop.

indices = [] numbers = [2,7,3,5,9,2,6,15,4] for idx, number in enumerate(numbers): if number > 4: indices.append(idx)
assert indices == [1, 3, 4, 6, 7]

EXERCISE 4.2

Write a function reverse() that takes a sequence and returns a list with the sequence items in reverse order. Use a loop for this, don't use the builtin .reverse() method or the indexing trick in the following assertion.

Bonus: Don't use the range() function either. It's unPythonic 😃

The assertion afterwards should pass.

def reverse(seq): output = [] for item in seq: output.insert(0, item) print(output) return output
names = ['Alice', 'Bob', 'Carol'] # This is judo, you'll learn it later assert reverse(names) == names[::-1]
['Carol', 'Bob', 'Alice']

Aside: Lists vs tuples -- what's the difference?

In addition to mutability, there is a "cultural" difference between tuples and lists. As shown in this example, lists are generally used when you have several items of the same "type", e.g. a list of names. If you're getting a list of names from a database query, for example, you may not know how many names are going to be returned.

Tuples are used when you have multiple different pieces of information about one "thing", e.g. a person's name and age. The cultural difference follows from the immutability of the tuple -- in the above tuple example, the 1th element is always the age, since we know there won't be an extra element inserted between the name and age. Thus a tuple is more like a single database row, with each value corresponding to a column.

When you're the one typing in the values, it usually doesn't matter much which one you use.

For more information on this, check out the excellent article by Ned Batchelder: http://nedbatchelder.com/blog/201608/lists_vs_tuples.html

Slicing

Slicing is used with the colon (😃 inside square brackets. The intution is returning the items at indices from a range() command.

components = ['python', 'javascript', 'html', 'css'] print(components[1:3]) # Returns items at indices from range(1,3), i.e. [1, 2] print(components[:2]) # Omitting an index at the beginning counts from zero print(components[2:]) # Omitting an index at the end counts until the last element
['javascript', 'html'] ['python', 'javascript'] ['html', 'css']

Negative indexes can be used to count from the end of the sequence

print(components[-1]) print(components[-3:-1]) print(components[:-2]) print(components[-2:])
css ['javascript', 'html'] ['python', 'javascript'] ['html', 'css']

Like the range() syntax, we can add a "count-by" argument

print(components[1::2]) print(components[::-1])
['javascript', 'css'] ['css', 'html', 'javascript', 'python']

Sequence membership

'css' in components
True
components.index('css')
3
'java' in components
False
# This will raise an error components.index('java')
--------------------------------------------------------------------------- ValueError Traceback (most recent call last) <ipython-input-40-f64f6f3f3203> in <module>() 1 # This will raise an error ----> 2 components.index('java') ValueError: 'java' is not in list

EXERCISE 4.3

Create an intersect function that takes two sequences and returns a new sequence consisting of the items contained in both sequences.

Example:

>>> intersect( [1,2,3,4], [3,4,5,6] ) <<< [3, 4]
def intersect(list1, list2): output = [] for item in list1: if item in list2: output.append(item) return output
_test = intersect( [1,2,3,4], [3,4,5,6] ) assert sorted(_test) == [3, 4] # Yes, there's a better way to do this with sets...
--------------------------------------------------------------------------- AssertionError Traceback (most recent call last) <ipython-input-42-759d77f0a135> in <module>() 1 _test = intersect( [1,2,3,4], [3,4,5,6] ) ----> 2 assert sorted(_test) == [3, 4] 3 # Yes, there's a better way to do this with sets... AssertionError:

EXAMPLE 5

Strings

One of Python's most beloved qualities is its powerful string manipulation abilities. Strings are immutable sequences like tuples, thus have some of the same methods:

name = "John Jacob Jingleheimer"
'J' in name
True
'z' in name
False
'John' in name
True
name.index('Jacob')
5
name[5:10]
'Jacob'
name[:6]
'John J'
name[::-1]
'remiehelgniJ bocaJ nhoJ'

There are some nice capitalization methods

print(name.lower()) print(name.upper()) print('four score and seven years ago'.title())
john jacob jingleheimer JOHN JACOB JINGLEHEIMER Four Score And Seven Years Ago

Concatenation

The + operator is used for string concatenation.

name = "John Jacob Jingleheimer" name = name + ' Schmidt' name
'John Jacob Jingleheimer Schmidt'

Since strings are immutable there is no in-place change, but the += operator does work:

name += ' Jr' name
'John Jacob Jingleheimer Schmidt Jr'

EXERCISE 5.1

Create a very simple pig-latin translator function, simple_pig_latin(). Put the first letter of the input string on the end and add '-ay'. The assertion afterwards should pass.

def simple_pig_latin(word): word = word[1:] + word[0] +'ay' print(word)
assert simple_pig_latin('clayton') == 'laytoncay'
laytoncay
--------------------------------------------------------------------------- AssertionError Traceback (most recent call last) <ipython-input-111-e981e3fa559b> in <module>() ----> 1 assert simple_pig_latin('clayton') == 'laytoncay' AssertionError:

EXERCISE 5.2

Now make a better pig-latin translator pig_latin(). Take all of the letters up until the first vowel and put them at the end, then add '-ay'.

vowels = ['a','e','i','o','u'] def pig_latin(word): indices = [] for idx, letter in enumerate(word): if letter in vowels: indices.append(idx) min_index = min(indices) return word[min_index:] + word[:min_index] + 'ay'
assert pig_latin('clayton') == 'aytonclay' and pig_latin('filippo') == 'ilippofay'

EXERCISE 5.3

This is a classic. Write a function is_palindrome() that takes a string as input and checks if it is a palindrome.

A palindrome is a word/sentence that reads the same forwards and backwards. Examples include 'racecar' and 'A man, a plan, a canal, panama'.

Your function should ignore spaces and capitalization, but don't worry about punctuation.

Bonus: Do this without using indices

Bonus: Do this without a loop

def is_palindrome(string): if string == string[::-1]: print('True') return string
assert is_palindrome('racecar') and is_palindrome('A man a plan a canal panama')
True

More string awesomeness

String formatting is a whole other subject, but here is the basic usage

print( '{}! His name is my name too!'.format(name) )
person = ('Rob', 21) # Implicit positional arguments print( '{} is {} years old'.format(person[0], person[1]) ) # Explicit positional arguments print( '{1} years ago, {0} was born. Yes {1} years old!'.format(person[0], person[1]) ) # Named arguments print( '{age} years ago, {name} was born. Yes {age} years old!'.format(name=person[0], age=person[1]) )

The .replace() method is useful. For a more flexible version of this, those interested can look up re.sub()

german = 'Floß' swiss = german.replace('ß', 'ss') print('{} -> {}'.format(german, swiss))
sentence = 'I sure do love Python string manipulation' sentence.replace(' ', '')

The .split() method returns a list of items separated by the given string

sentence = 'I sure do love Python string manipulation' words = sentence.split(' ') words

The .join() method is a bit unintuitive at first, but provides the inverse of .split()

' '.join(words)
', '.join(words)

EXAMPLE 6

Dictionary

A dictionary (dict) can be used to store associations or relations between different entities (person and age, name and alias, person and friends' names etc.). Dict items are specified as key:value pairs within curly brackets. Instead of accessing dictionary values by their index, we use the key. As always, values are accessed using square brackets.

age_data = {'Jerry' : 23, 'Martha' : 21} age_data['Jerry'] # extract Jerry's age
'Jerry' in age_data
'Mickey' in age_data
age_data['Mickey']

Since we check for key membership with 'in', it makes sense that list(age_data) gives us all of the keys in age_data

list(age_data)
list(age_data.keys())
list(age_data.values())

This is one way to loop over a dict

for name in age_data: years = age_data[name] msg = '{} is {} years old'.format(name, years) print(msg)

The .items() method gives us another way

list(age_data.items())
for name, years in age.items(): msg = '{} is {} years old'.format(name, years) print(msg)

The keys of a dict must be an immutable object e.g. string, number, or tuple.

The values of a dict can be anything, including lists:

friends = { 'Jerry': [ 'Alice', 'Bob', ], 'Martha': [ 'Bob', 'Carol', ], } print( friends['Martha'] ) print( friends['Martha'][1] )

and mutable values in dicts can be changed.

friends['Martha'].append('Daniel') print( friends['Martha'] )

EXERCISE 6.1

A bored (and inaccurate) spy satellite makes a record of your vacation trail. Below are a few entries in its cloud storage:
Day 1: Paris, Lat = 99, Long = 100
Day 4: Prague, Lat = 99, Long = 90
Day 6: Zurich, Lat = 90, Long = 90
Day 10: Moscow, Lat = 80, Long = 70

Store the above information in a dictionary. Choose an appropriate key. Using a loop, produce a list of just the names of places visited after Day 2 and located above 85 latitude. Name this list matching_places.

assert matching_places == ['Zurich']

File I/O

Unrelated to dicts, but necessary for the exercises

There is a file called nato.txt in this directory. It has one item on each line. The following piece of code does the a few things:

  • Opens the file for reading, assigning the file handle to the variable f

  • Reads the entire contents of the file as a string with f.read()

  • Splits the file content string by newlines, obtaining a the list of items

  • Implicitly closes the file handle once we leave the with block

with open('nato.txt') as f: text = f.read() lines = text.split('\n') lines[:5]

NOTE There are a few methods of parsing a file into a list, one line to an item. The downside of this method is that it can produce an empty item at the end of the list:

lines[-1]

Keep this in mind. One way to deal with it is when consuming the list, add a check like this:

for item in lines: if item: <do stuff>

EXERCISE 6.2

Create a lookup table for the NATO phonetic alphabet. Store it in a dict named nato. The key should be the first letter of each item in the list.

assert nato['Z'] == 'Zulu'

EXERCISE 6.3

Write a function phonetic() that translates a word or short sentence into the NATO phonetic alphabet. Your function should return a space-separated string. Ignore spaces and capitalization, but don't worry about punctuation.

assert phonetic('Hello World') == 'Hotel Echo Lima Lima Oscar Whiskey Oscar Romeo Lima Delta'

"Concatenation"

Unfortunately you can't combine two dicts with the + operator (by default, you could define it). Since dicts are mutable, there is an in-place way to combine two dicts, using the update() method:

exam_scores = { 'Alice': 88, 'Bob': 89, } exam_retake_scores = { 'Bob': 90, 'Carol': 95 } exam_scores.update(exam_retake_scores) exam_scores