This tutorial introduces some important aspects of the Python programming language. For more comprehensive introductions and a complete reference, the following links may be useful: - [The Python Tutorial](https://docs.python.org/3/tutorial/index.html) introduces the reader informally to the basic concepts and features of the Python language and system. - The [Scipy Lecture Notes](https://www.scipy-lectures.org/) is a tutorial on the scientific Python ecosystem including libraries such as Numpy, Scipy, and Matploblib. - The Python package [LibROSA](https://librosa.github.io/) provides many building blocks to create music information retrieval systems. It also cotains a [gallery](https://librosa.github.io/librosa/advanced.html) of more advanced examples.
numpy
, scipy
, matplotlib
, librosa
) in Python.end
-commands) to separate blocks of code.#
.help()
dir()
globals()
, locals()
Let us start with some basic facts on Python variables:
a
, b
, ..., Y
, Z
) and the underscore (_
).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.
string_variable = 'Welcome to the Python Tutorial'
print(string_variable)
Here some basic string formatting:
print('This is an integer: %d.' % 17)
print('This is string1: %s and this is string2: %6s.' % ('ABCD', '1234'))
print('This is a floating point number: %06.3f.' % 3.14159265359)
Some basic math:
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)
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.
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)
For re-assigning a variable, one may use the following conventions:
n = 7
n += 11
print(n)
n *= 2
print(n)
n /= 18
print(n)
n **= 0.5
print(n)
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 bracekts. Both are indexed with square brackets (with indexing starting with $0$). The len
function gives the length of a tuple or a list.
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))
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:
var_lis = [1, 2, 3]
var_lis[0] = -1
var_lis.append(10)
var_lis = var_lis + ['a', '12', [13, 14]]
print(var_lis)
print(var_lis[-1])
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$.
var_lis = [11, 12, 13, 14, 15]
print('var_lis[0:3] =', var_lis[0:3])
print('var_lis[1:3] =', var_lis[1:3])
print('var_lis[-1] =', var_lis[-1])
print('var_lis[0:3:2] =', var_lis[0:4:2])
print('var_lis[0::2] =', var_lis[0::2])
print('var_lis[::-1] =', var_lis[::-1])
The following examples shows how the elements of a list or tuple can be assigned to variables (called unpacking):
var_lis = [1, 2]
[a, b] = var_lis
print(a, b)
var_tup = (3, 4)
[c, d] = var_tup
print(c, d)
Leaving out brackets, tuples are generated.
a, b = 1, 2
print(a, b)
The range
-function can be used to specify a tuple or list of integers (without actually generating these numbers):
print(range(9))
print(range(1, 9, 2))
A range can then be converted into a tuple or list as follows:
print(list(range(9)))
print(tuple(range(1, 9, 2)))
print(list(range(9, 1, -1)))
Boolean values in Python are True
and False
. Here are some examples for basic comparions:
a = 1
b = 2
print(a < b)
print(a <= b)
print(a == b)
print(a != b)
The bool
function converts an arbitrary value into a boolean value. Here, are some examples:
print(bool('a'))
print(bool(''))
print(bool(1))
print(bool(0))
print(bool(0.0))
print(bool([]))
print(bool([4, 'hello', 1]))
There are also other data types in Python, which we do not cover here. These data types includ sets and dictionaries, which are illustrated by the following examples:
s = {4, 2, 1, 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})
print()
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 dic via a key:', dic['b'])
print('Print the values of dic:', list(dic.values()))
For control structures such as if
, for
or while
one has to use indentations (as part of the syntax). A typical Python convention is to use four spaces. For example, an if
-statement is written as follows:
n = 2
if n == 2:
print('True')
else:
print('False')
The next example shows how to use a for
-loop. Note that an iterable may be specified by a range, a list, a tuple, or even other structures.
for i in range(3):
print(i, end='-')
print()
for i in ['a', 2, 'c', 'def']:
print(i, end='-')
print()
for i in 'abcd':
print(i, end='-')
print()
A while
-loop is written as follows:
a = 0
while a < 5:
print(a, end='-')
a += 1
One defines functions with the def
-keyword. As variable names, function names may contain letters (a
, b
, ..., Y
, Z
) and the underscore (_
). All but the first character can also be positive integer number. Usually one uses lower case letters and underscores to seperate words.
The following function is named add
. It has three arguments a
, b
, and c
(with b
and c
having a default value). The return
keyword is succeded by the return value.
def add(a, b=0, c=0):
print('Addition: ', a, ' + ', b, ' + ', c)
return a + b + c
print(add(5))
print(add(5, 2, 1))
print(add(5, c=4))
There can also be multiple return values (which are returned as a tuple):
def add_and_diff(a, b=0):
return a + b, a - b
x = add_and_diff(3, 5)
print(x)
Python has several useful built-in packages as well as additional external packages. One such package is NumPy, which adds support for multi-dimensional arrays and matrices, along with a number of mathematical functions to operate on these structures, see NumPy Reference Manual for details. In the following, we give some examples.
The NumPy package is imported as follows:
import numpy as np
It is convenient to bind a package to a short name (for example np
as above). This short name appears as prefix when calling a function from the package. This is illustrated by the following array
-funtion provided by numpy:
x = np.array([1, 2, 3, 3])
print(x)
Each array has a shape:
print(x.shape)
In this example, note that x.shape
produces a one-element tuple, which is encoded by (4,)
for disambiguation. (The object (4)
would be an integer of type int
rather than a tuple.)
Multi-dimensional arrays are created like follows:
x = np.array([[1, 2, 3], [4, 5, 6]])
print(x)
print(x.shape)
There are a couple of functions for creating arrays:
print('Array of given shape and type, filled with zeros: %s' % np.zeros(2))
print('Array of given shape and type, filled with ones:\n %s' % np.ones((2, 3)))
print('Evenly spaced values within a given interval: %s' % np.arange(2, 8, 2))
print('Random values in a given shape:\n', np.random.rand(2, 3))
print('Identity matrix:\n%s' % np.eye(3))
Reshaping of an array is possible like follows:
x = np.arange(2 * 3 * 4)
print(x)
print('Shape:', x.shape)
y = x.reshape((3, 8))
print(y)
print('Shape:', y.shape)
y = x.reshape((2, 3, 4))
print(y)
print('Shape:', y.shape)
Applied to arrays, many operations are conducted in an element-wise fashion:
x = np.arange(5)
print('x + 1 =', x + 1)
print('x * 2 =', x * 2)
print('x > 2 =', x > 2)
Similarly, applied to two arrays of the same shape, these operations are conducted in an element-wise fashion. Matrix multiplication is performed by using the function np.dot
:
a = np.arange(0, 4).reshape((2, 2))
b = 2 * np.ones((2, 2))
print(a)
print(b)
print(a + b)
print(a * b)
print(np.dot(a, b))
Note that arrays and lists may behave in a completely different way. For example, addition leads to the following results:
a = np.arange(4)
b = np.arange(4)
print(a + b, type(a + b))
a = list(a)
b = list(b)
print(a + b, type(a + b))
The sum of an array's elements can be computed as follows:
x = np.arange(6).reshape((2, 3))
print(x)
print(x.sum())
print(x.sum(axis=0))
print(x.sum(axis=1))
There are many ways for accessing and manipulating arrays:
x = np.arange(6).reshape((2, 3))
print(x)
print(x[1, 1])
print([x > 1])
print(x[x > 1])
print(x[1, :])
print(x[:, 1])
Specifying the data type for an array can be important in some situations.
x = np.arange(-3, 3)
print(x, type(x), type(x[0]), x.dtype)
x = np.sqrt(x)
print(x)
x = np.arange(-3, 3, dtype='complex')
print(x, type(x), type(x[0]), x.dtype)
x = np.sqrt(x)
print(x)
Acknowledgment: This notebook was created by Frank Zalkow, Stefan Balke, and Meinard Müller.