PCP Teaser

Overview and Learning Objectives

This unit introduces basic concepts used in Python programming while we assume that you are familiar with general programming and have some experience with other programming languages such as MATLAB, C/C++, or Java. We start with basic Python variables and operators and then introduce compound data types such as lists, tuples, sets, and dictionaries. It is important that you understand the conceptual difference between these constructs and the Python syntax to encode them. Using variables and data types in Python can differ significantly from the other programming languages, often resulting in seemingly surprising programming errors. Therefore, we will briefly discuss how to convert data types in Python. Furthermore, we point out the difference between deep and shallow copies, a delicate and important topic that is a common cause of errors, especially when passing variables to functions. As said, we only touch on these topics by giving concrete examples. To get familiar with the Python syntax, we recommend that you do the small exercises on list (Exercise 1) and dictionary manipulations (Exercise 2). For more comprehensive tutorials, we refer to the following sources:

Basic Facts

  • Python is an interpreted (not compiled), open-source, multi-platform programming language.
  • There exist several modules for scientific computing (e.g. numpy, scipy, matplotlib, librosa) in Python.
  • Python uses indentation (and not brackets or end-commands) to separate blocks of code.
  • Comment lines start with the character #.
  • Useful functions for help:
  • Invoke the built-in help system: help().
  • List objects in namespace: dir().
  • Show global and local variables: globals(), locals().

Variables and Basic Operators

Let us start with some basic facts on Python variables:

  • Variables do not need to be declared; neither their type.
  • Variables are created automatically when they are first assigned.
  • A variable name may contain letters (a, b, ..., Y, Z) and the underscore (_).
  • Variable names are case sensitive.
  • All but the first character can also be positive integer numbers.
  • Usually, one uses lower case letters and underscores to separate words.

A string is given in single ticks (') or double ticks ("). If there is no other reason, we recommend single ticks. The following code assigns a string to a variable and prints it using the print-command.

In [1]:
string_variable1 = 'Welcome to the Python Tutorial'
print(string_variable1)
Welcome to the Python Tutorial

This is how you can print some basic math:

In [2]:
n = 3
print('n + 1 =', n + 1)
print('n - 1 =', n - 1)
print('n * 2 =', n * 2)
print('n / 2 =', n / 2)
print('n ^ 2 =', n ** 2)
n + 1 = 4
n - 1 = 2
n * 2 = 6
n / 2 = 1.5
n ^ 2 = 9

Division always results in a floating-point number, even if the number is divisible without remainder. (Note that there are differences between Python 2 and Python 3 in using /). If the result should be an integer (e.g., when using it as an index), one may use the // operator. The % yields the remainder.

In [3]:
n = 8
print('Normal division:', n / 2)
print('Integer division:', n // 2)
print('Normal division:', n / 5)
print('Integer division:', n // 5)
print('Remainder of integer division:', n % 5)
Normal division: 4.0
Integer division: 4
Normal division: 1.6
Integer division: 1
Remainder of integer division: 3

For re-assigning a variable, one may use the following conventions:

In [4]:
n = 7
n += 11
print('Addition:', n)

n *= 2
print('Multiplication:', n)

n /= 18
print('Division:', n)

n **= 0.5
print('Exponentiation:', n)
Addition: 18
Multiplication: 36
Division: 2.0
Exponentiation: 1.4142135623730951

If you want more control over the formatting of your printed output, we recommend using the f-String format convention.

In [5]:
print(f'This is how to print a number: {17}.')
string_variable2 = 'ABCD'
print(f'This is how to print a string: {string_variable2}.')
print(f'This is how to print a number (enforcing six digits): {1234:6d}.')
print(f'This is how to print a floating point number with three decimals: {3.14159265359:6.3f}.')
This is how to print a number: 17.
This is how to print a string: ABCD.
This is how to print a number (enforcing six digits):   1234.
This is how to print a floating point number with three decimals:  3.142.

Lists and Tuples

The basic compound data types in Python are lists and tuples. A list is enclosed in square brackets and a tuple is enclosed in round brackets. Both are indexed with square brackets (with indexing starting with $0$). The len function gives the length of a tuple or a list.

In [6]:
var_lis = ['I', 'am', 'a', 'list']
var_tup = ('I', 'am', 'a', 'tuple')
print(var_lis)
print(var_tup)
print(var_lis[0], var_tup[1], 'generated from', 
      var_tup[2], var_tup[3], 'and', var_lis[2], var_lis[3])
print(len(var_tup))
print(len(var_lis))

print(type(var_lis))
print(type(var_tup))
['I', 'am', 'a', 'list']
('I', 'am', 'a', 'tuple')
I am generated from a tuple and a list
4
4
<class 'list'>
<class 'tuple'>

What is the difference between a list and a tuple? Tuples are immutable objects (i.e., their state cannot be modified after they are created) and a bit more efficient. Lists are more flexible. Here are some examples for list operations:

In [7]:
var_list = [1, 2, 3]
print('Print list:', var_list)
var_list[0] = -1
print('Alternate item:', var_list)
var_list.append(10)
print('Append item:', var_list)
var_list = var_list + ['a', '12', [13, 14]]
print('Concatenate two lists:', var_list)
print('Last element of list: ', var_list[-1])

print('Remove an item by index and get its value:', var_list.pop(2))
var_list.remove([13, 14])
print('Remove an item by value:', var_list)
del(var_list[2:4])
print('Remove items by slice of indices:', var_list)
Print list: [1, 2, 3]
Alternate item: [-1, 2, 3]
Append item: [-1, 2, 3, 10]
Concatenate two lists: [-1, 2, 3, 10, 'a', '12', [13, 14]]
Last element of list:  [13, 14]
Remove an item by index and get its value: 3
Remove an item by value: [-1, 2, 10, 'a', '12']
Remove items by slice of indices: [-1, 2, '12']

One can index a list with start, stop, and step values ([start:end:step]). Note that, in Python, the last index value is end-1. Negative indices are possible with -1 referring to the last index. When not specified, start refers to the first item, end to the last item, and step is set to $1$.

In [8]:
var_list = [11, 12, 13, 14, 15]
print('var_list =', var_list)
print('var_list[0:3] =', var_list[0:3])
print('var_list[1:3] =', var_list[1:3])
print('var_list[-1] =', var_list[-1])
print('var_list[0:4:2] =', var_list[0:4:2])
print('var_list[0::2] =', var_list[0::2])
print('var_list[::-1] =', var_list[::-1])
var_list = [11, 12, 13, 14, 15]
var_list[0:3] = [11, 12, 13]
var_list[1:3] = [12, 13]
var_list[-1] = 15
var_list[0:4:2] = [11, 13]
var_list[0::2] = [11, 13, 15]
var_list[::-1] = [15, 14, 13, 12, 11]

The following examples shows how the elements of a list or tuple can be assigned to variables (called unpacking):

In [9]:
var_list = [1, 2]
[a, b] = var_list
print(a, b)

var_tup = (3, 4)
[c, d] = var_tup
print(c, d)
1 2
3 4

Leaving out brackets, tuples are generated.

In [10]:
t = 1, 2
a, b = t
print(t)
print(a, b)
(1, 2)
1 2

The range-function can be used to specify a tuple or list of integers (without actually generating these numbers). A range can then be converted into a tuple or list.

In [11]:
print(range(9))
print(range(1, 9, 2))
print(list(range(9)))
print(tuple(range(1, 9, 2)))
print(list(range(9, 1, -1)))
range(0, 9)
range(1, 9, 2)
[0, 1, 2, 3, 4, 5, 6, 7, 8]
(1, 3, 5, 7)
[9, 8, 7, 6, 5, 4, 3, 2]

Boolean Values

Boolean values in Python are True and False. Here are some examples for basic comparisons:

In [12]:
a = 1
b = 2
print(a < b)
print(a <= b)
print(a == b)
print(a != b)
True
True
False
True

The bool function converts an arbitrary value into a boolean value. Here, are some examples:

In [13]:
print(bool('a'))
print(bool(''))
print(bool(1))
print(bool(0))
print(bool(0.0))
print(bool([]))
print(bool([4, 'hello', 1]))
True
False
True
False
False
False
True

Sets

There are also other data types in Python, which we want to mention here. In the following, we introduce sets, which are unordered collections of unique elements. Furthermore, we apply some basic set operations.

In [14]:
s = {4, 2, 1, 2, 5, 2}
print('Print the set s:', s)
print('Union of sets:', {1, 2, 3} | {2, 3, 4})
print('Intersection of sets:', {1, 2, 3} & {2, 3, 4})
s.add(7)
print('Adding an element:', s)
s.remove(2)
print('Removing an element:', s)
Print the set s: {1, 2, 4, 5}
Union of sets: {1, 2, 3, 4}
Intersection of sets: {2, 3}
Adding an element: {1, 2, 4, 5, 7}
Removing an element: {1, 4, 5, 7}

Dictionaries

Another convenient data type are dictionaries, which are indexed by keys (rather than by a range of numbers as is the case for lists or arrays). The following code cell gives an example and introduces some basic operations.

In [15]:
dic = {'a': 1 , 'b': 2, 3: 'hello'}
print('Print the dictionary dic:', dic)
print('Print the keys of dic:', list(dic.keys()))
print('Access the dictionary via a key:', dic['b'])
print('Print the values of the dictionary:', list(dic.values()))
Print the dictionary dic: {'a': 1, 'b': 2, 3: 'hello'}
Print the keys of dic: ['a', 'b', 3]
Access the dictionary via a key: 2
Print the values of the dictionary: [1, 2, 'hello']

Python Type Conversion

Python offers many different numerical types and methods for type conversion. The standard numeric types in Python are int, float, and complex. The function type()can be used to identify the type of a variable. One can use the methods int(), float(), and complex() to convert from one type to another. This is demonstrated in the following code cell.

Note:
  • Type conversions from float to int may result in some rounding.
  • One cannot directly convert from the type complex to int or float.
In [16]:
a = 1
print('a =', a, type(a))
b = float(a)
print('b =', b, type(b))
c = 2.2
print('c =' , c, type(c))
d = int(c)
print('d =', d, type(d))
e = complex(d)
print('e =', e, type(e))
f = complex(a, c)
print('f =', f, type(f))
a = 1 <class 'int'>
b = 1.0 <class 'float'>
c = 2.2 <class 'float'>
d = 2 <class 'int'>
e = (2+0j) <class 'complex'>
f = (1+2.2j) <class 'complex'>

Shallow and Deep Copy Operations

Dealing with objects such as lists, tuples, dictionaries, or sets can be trickier as one may think. In particular, using the assignment operator = may only create a pointer from variable to an object. As a result, two variables may point to the same object, which may lead to unexpected modifications. This effect is illustrated by the following example.

In [17]:
a = [1, 2, 3]
print('a = ', a, ', id(a) = ', id(a))
b = a
b[0] = 0
b.append(4)
print('b = ', b, ', id(b) = ', id(b))
print('a = ', a, ', id(a) = ', id(a))
a =  [1, 2, 3] , id(a) =  140657031920064
b =  [0, 2, 3, 4] , id(b) =  140657031920064
a =  [0, 2, 3, 4] , id(a) =  140657031920064

This example shows that the assignment b = a does not create a copy of the list. The variables a and b point to the same object, whose identifier (unique integer) is revealed by the function id(). To create copies of an object, one can use the python module copy, which is imported by the statement import copy. There are different types of copies called shallow copy and deep copy, which become important when dealing with compound objects (objects whose elements are again objects such as lists of lists).

  • A shallow copy creates a new compound object. However, if the entries are again compound objects, only links are created.
  • A deep copy creates a new compound object, where new objects are created for all entries (and recursively for their entries).

The two cases are illustrated by the subsequent example.

In [18]:
import copy

a = [[1, 2, 3], [4, 5, 6]]
b = copy.copy(a)
b[0] = 0
print('a = ', a)
print('b = ', b)
b[1][0] = 0
print('a = ', a)
print('b = ', b)

a = [[1, 2, 3], [4, 5, 6]]
c = copy.deepcopy(a)
c[1][0] = 0
print('a = ', a)
print('c = ', c)
a =  [[1, 2, 3], [4, 5, 6]]
b =  [0, [4, 5, 6]]
a =  [[1, 2, 3], [0, 5, 6]]
b =  [0, [0, 5, 6]]
a =  [[1, 2, 3], [4, 5, 6]]
c =  [[1, 2, 3], [0, 5, 6]]

Exercises and Results

In [19]:
import libpcp.python
show_result = True

Exercise 1: Basic List Manipulations
  • In the following, we assume that a student is specified by a list containing a student ID (integer), last name (string), and first name (string). Create a list of students, which contains the following entries: [123, 'Meier', 'Sebastian'], [456, 'Smith', 'Walter']. (Note that this becomes a list of lists.)
  • Add to this list the student [789, 'Wang', 'Ming'].
  • Print out the student list in reversed order (descending student IDs).
  • Print out only the first name of the second student in this list.
  • Print the length of the list (using the len function).
  • Make a deep copy of the list and then remove the first and second student from the copied list (using the del statement). Furthermore, change the student ID of the remaining student from 789 to 777. Check if the original list has been modified.
In [20]:
#<solution>
# Your Solution
#</solution>
In [21]:
libpcp.python.exercise_list(show_result=show_result)
[[123, 'Meier', 'Sebastian'], [456, 'Smith', 'Walter']]
[[123, 'Meier', 'Sebastian'], [456, 'Smith', 'Walter'], [789, 'Wang', 'Ming']]
[[789, 'Wang', 'Ming'], [456, 'Smith', 'Walter'], [123, 'Meier', 'Sebastian']]
Walter
3
[[777, 'Wang', 'Ming']]
[[123, 'Meier', 'Sebastian'], [456, 'Smith', 'Walter'], [789, 'Wang', 'Ming']]

Exercise 2: Basic Dictionary Manipulations
  • Again, we assume that a student is specified by student ID (integer), last name (string), and first name (string). Create a dictionary, where a key corresponds to the student ID and a value to a list of the last name and first name. Start with a dictionary with key 123 and value ['Meier', 'Sebastian'] as well as key 456 and value ['Smith', 'Walter'].
  • Add the student with key 789 and value ['Wang', 'Ming'].
  • Print out a list of all keys of the dictionary.
  • Print out a list of all values of the dictionary.
  • Print out the last name of the student with key 456.
  • Remove the student with key 456 (using the del statement).
  • Print the length of the dictionary (using the len function).
In [22]:
#<solution>
# Your Solution
#</solution>
In [23]:
libpcp.python.exercise_dict(show_result=show_result)
{123: ['Meier', 'Sebastian'], 456: ['Smith', 'Walter']}
{123: ['Meier', 'Sebastian'], 456: ['Smith', 'Walter'], 789: ['Wang', 'Ming']}
[123, 456, 789]
[['Meier', 'Sebastian'], ['Smith', 'Walter'], ['Wang', 'Ming']]
Smith
{123: ['Meier', 'Sebastian'], 789: ['Wang', 'Ming']}
2
PCP License