Quick note about Jupyter cells

When you are editing a cell in Jupyter notebook, you need to re-run the cell by pressing <Shift> + <Enter>. This will allow changes you made to be available to other cells.

Use <Enter> to make new lines inside a cell you are editing.

Code cells

Re-running will execute any statements you have written. To edit an existing code cell, click on it.

Markdown cells

Re-running will render the markdown text. To edit an existing markdown cell, double-click on it.


Common Jupyter operations

Near the top of the https://try.jupyter.org page, Jupyter provides a row of menu options (File, Edit, View, Insert, ...) and a row of tool bar icons (disk, plus sign, scissors, 2 files, clipboard and file, up arrow, ...).

Inserting and removing cells

  • Use the "plus sign" icon to insert a cell below the currently selected cell
  • Use "Insert" -> "Insert Cell Above" from the menu to insert above

Clear the output of all cells

  • Use "Kernel" -> "Restart" from the menu to restart the kernel
    • click on "clear all outputs & restart" to have all the output cleared

Save your notebook file locally

  • Clear the output of all cells
  • Use "File" -> "Download as" -> "IPython Notebook (.ipynb)" to download a notebook file representing your https://try.jupyter.org session

Load your notebook file in try.jupyter.org

  1. Visit https://try.jupyter.org
  2. Click the "Upload" button near the upper right corner
  3. Navigate your filesystem to find your *.ipynb file and click "open"
  4. Click the new "upload" button that appears next to your file name
  5. Click on your uploaded notebook file

Python is interpreted

  • Python is an interpreted language, in contrast to Java and C which are compiled languages.

  • This means we can type statements into the interpreter and they are executed immediately.


In [1]:
5 + 5


Out[1]:
10

In [2]:
x = 5
y = 'Hello There'
z = 10.5

In [3]:
x + 5


Out[3]:
10

Assignments versus equations

  • In Python when we write x = 5 this means something different from an equation $x=5$.

  • Unlike variables in mathematical models, variables in Python can refer to different things as more statements are interpreted.


In [4]:
x = 1
print ('The value of x is ', x)


The value of x is  1

In [5]:
x = 2.5
print ('Now the value of x is ', x)


Now the value of x is  2.5

In [6]:
x = 'hello there'
print ('Now it is ', x)


Now it is  hello there

Calling Functions

We can call functions in a conventional way using round brackets


In [7]:
print (round(3.14))


3

In [8]:
help(round)


Help on built-in function round in module builtins:

round(...)
    round(number[, ndigits]) -> number
    
    Round a number to a given precision in decimal digits (default 0 digits).
    This returns an int when called with one argument, otherwise the
    same type as the number. ndigits may be negative.


In [9]:
import builtins
dir(builtins)


Out[9]:
['ArithmeticError',
 'AssertionError',
 'AttributeError',
 'BaseException',
 'BlockingIOError',
 'BrokenPipeError',
 'BufferError',
 'BytesWarning',
 'ChildProcessError',
 'ConnectionAbortedError',
 'ConnectionError',
 'ConnectionRefusedError',
 'ConnectionResetError',
 'DeprecationWarning',
 'EOFError',
 'Ellipsis',
 'EnvironmentError',
 'Exception',
 'False',
 'FileExistsError',
 'FileNotFoundError',
 'FloatingPointError',
 'FutureWarning',
 'GeneratorExit',
 'IOError',
 'ImportError',
 'ImportWarning',
 'IndentationError',
 'IndexError',
 'InterruptedError',
 'IsADirectoryError',
 'KeyError',
 'KeyboardInterrupt',
 'LookupError',
 'MemoryError',
 'ModuleNotFoundError',
 'NameError',
 'None',
 'NotADirectoryError',
 'NotImplemented',
 'NotImplementedError',
 'OSError',
 'OverflowError',
 'PendingDeprecationWarning',
 'PermissionError',
 'ProcessLookupError',
 'RecursionError',
 'ReferenceError',
 'ResourceWarning',
 'RuntimeError',
 'RuntimeWarning',
 'StopAsyncIteration',
 'StopIteration',
 'SyntaxError',
 'SyntaxWarning',
 'SystemError',
 'SystemExit',
 'TabError',
 'TimeoutError',
 'True',
 'TypeError',
 'UnboundLocalError',
 'UnicodeDecodeError',
 'UnicodeEncodeError',
 'UnicodeError',
 'UnicodeTranslateError',
 'UnicodeWarning',
 'UserWarning',
 'ValueError',
 'Warning',
 'ZeroDivisionError',
 '__IPYTHON__',
 '__build_class__',
 '__debug__',
 '__doc__',
 '__import__',
 '__loader__',
 '__name__',
 '__package__',
 '__spec__',
 'abs',
 'all',
 'any',
 'ascii',
 'bin',
 'bool',
 'bytearray',
 'bytes',
 'callable',
 'chr',
 'classmethod',
 'compile',
 'complex',
 'copyright',
 'credits',
 'delattr',
 'dict',
 'dir',
 'display',
 'divmod',
 'enumerate',
 'eval',
 'exec',
 'filter',
 'float',
 'format',
 'frozenset',
 'get_ipython',
 'getattr',
 'globals',
 'hasattr',
 'hash',
 'help',
 'hex',
 'id',
 'input',
 'int',
 'isinstance',
 'issubclass',
 'iter',
 'len',
 'license',
 'list',
 'locals',
 'map',
 'max',
 'memoryview',
 'min',
 'next',
 'object',
 'oct',
 'open',
 'ord',
 'pow',
 'print',
 'property',
 'range',
 'repr',
 'reversed',
 'round',
 'set',
 'setattr',
 'slice',
 'sorted',
 'staticmethod',
 'str',
 'sum',
 'super',
 'tuple',
 'type',
 'vars',
 'zip']

Later we will discuss how to built our own function

Types

  • Values in Python have an associated type.

  • If we combine types incorrectly we get an error.


In [10]:
print (y)


Hello There

In [11]:
y + 5


---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-11-c3d4037f4656> in <module>()
----> 1 y + 5

TypeError: must be str, not int

The type function

  • We can query the type of a value using the type function.

In [12]:
type(1)


Out[12]:
int

In [13]:
type('hello')


Out[13]:
str

In [14]:
type(2.5)


Out[14]:
float

In [15]:
type(True)


Out[15]:
bool

In [16]:
type(y)


Out[16]:
str

Null values

  • Sometimes we represent "no data" or "not applicable".

  • In Python we use the special value None.

  • This corresponds to Null in Java or SQL.


In [17]:
result = None
  • When we fetch the value None in the interactive interpreter, no result is printed out.

In [18]:
print(result)


None

In [19]:
type(result)


Out[19]:
NoneType
  • We can check whether there is a result or not using the is operator:

In [20]:
result is None


Out[20]:
True

In [21]:
x = 5
x is None


Out[21]:
False

Converting values between types

  • We can convert values between different types.

  • To convert an integer to a floating-point number use the float() function.

  • To convert a floating-point to an integer use the int() function.

In [22]:
x = 100
print (type(x))
print (x)


<class 'int'>
100

In [23]:
y = float(x)
print (type(y))
print (y)


<class 'float'>
100.0

In [24]:
print (int(y))


100

Converting to and from ASCII values

  • The functions chr() and ord() can be used to convert characters from and to ASCII.

In [25]:
print (ord('a'))


97

In [26]:
print (chr(97))


a

Variables are not typed

  • Variables themselves, on the other hand, do not have a fixed type.
  • It is only the values that they refer to that have a type.
  • This means that the type referred to by a variable can change as more statements are interpreted.

In [27]:
y = 'hello'
print ('The type of the value referred to by y is ', type(y))
y = 5.0
print ('And now the type of the value is ', type(y))


The type of the value referred to by y is  <class 'str'>
And now the type of the value is  <class 'float'>

Polymorphism

  • The meaning of an operator depends on the types we are applying it to.

In [28]:
1//5


Out[28]:
0

In [29]:
1/5


Out[29]:
0.2

In [30]:
1.0 / 5.0


Out[30]:
0.2

In [31]:
1 + 1


Out[31]:
2

In [32]:
'a' + 'b'


Out[32]:
'ab'

In [33]:
'1' + '1'


Out[33]:
'11'

Conditional Statements and Indentation

  • The syntax for control structures in Python use colons and indentation.

  • Beware that white-space affects the semantics of Python code.


In [34]:
x = 5
if x > 0:
    print ('x is strictly positive')
    print (x)
    
print ('finished.')


x is strictly positive
5
finished.

In [35]:
x = 0
if x > 0:
    print ('x is strictly positive')
print (x)
    
print ('finished')


0
finished

Lists

We can use lists to hold an ordered sequence of values.


In [36]:
l = ['first', 'second', 'third']
print (l)


['first', 'second', 'third']

Lists can contain different types of variable, even in the same list.


In [37]:
another_list = ['first', 'second', 'third', 1, 2, 3]
print (another_list)


['first', 'second', 'third', 1, 2, 3]

Mutable Datastructures

Lists are mutable; their contents can change as more statements are interpreted.


In [38]:
l.append('fourth')
print (l)


['first', 'second', 'third', 'fourth']

References

  • Whenever we bind a variable to a value in Python we create a reference.

  • A reference is distinct from the value that it refers to.

  • Variables are names for references.


In [39]:
X = [1, 2, 3]
Y = X
  • The above code creates two different references (named X and Y) to the same value [1, 2, 3]

  • Because lists are mutable, changing them can have side-effects on other variables.

  • If we append something to X what will happen to Y?


In [40]:
X.append(4)
print(X)


[1, 2, 3, 4]

In [41]:
print(Y)


[1, 2, 3, 4]

State and identity

  • The state referred to by a variable is different from its identity.

  • To compare state use the == operator.

  • To compare identity use the is operator.

  • When we compare identity we check equality of references.

  • When we compare state we check equality of values.


In [42]:
X = [1, 2]
Y = [1]
Y.append(2)

In [43]:
X == Y


Out[43]:
True

In [44]:
X is Y


Out[44]:
False

In [45]:
Y.append(3)
X


Out[45]:
[1, 2]

In [46]:
X = Y

In [47]:
X is Y


Out[47]:
True

Iteration

  • We can iterate over each element of a list in turn using a for loop:

In [48]:
for i in l:
    print (i)


first
second
third
fourth
  • To perform a statement a certain number of times, we can iterate over a list of the required size.

In [49]:
for i in [0, 1, 2, 3]:
    print ("Hello!")


Hello!
Hello!
Hello!
Hello!

For loops with the range function

  • To save from having to manually write the numbers out, we can use the function range() to count for us. As in Java and C, we count starting at 0.

In [50]:
for i in range(4):
    print ("Hello!")


Hello!
Hello!
Hello!
Hello!

List Indexing

  • Lists can be indexed using square brackets to retrieve the element stored in a particular position.

In [51]:
print (l[0])


first

In [52]:
print (l[1])


second

List Slicing

  • We can also a specify a range of positions.

  • This is called slicing.

  • The example below indexes from position 0 (inclusive) to 2 (exclusive).


In [53]:
print (l[0:2])


['first', 'second']
  • If we leave out the starting index it implies the beginning of the list:

In [54]:
print (l[:2])


['first', 'second']
  • If we leave out the final index it implies the end of the list:

In [55]:
print (l[2:])


['third', 'fourth']

Negative Indexing

  • Negative indices count from the end of the list:

In [56]:
print (l[-1])


fourth

In [57]:
print (l[:-1])


['first', 'second', 'third']

Collections

  • Lists are an example of a collection.

  • A collection is a type of value that can contain other values.

  • There are other collection types in Python:

    • tuple
    • set
    • dict

Tuples

  • Tuples are another way to combine different values.

  • The combined values can be of different types.

  • Like lists, they have a well-defined ordering and can be indexed.

  • To create a tuple in Python, use round brackets instead of square brackets


In [58]:
tuple1 = (50, 'hello')
print(tuple1)


(50, 'hello')

In [59]:
print(tuple1[0])


50

In [60]:
type(tuple1)


Out[60]:
tuple

Tuples are immutable

  • Unlike lists, tuples are immutable. Once we have created a tuple we cannot add values to it.

In [61]:
tuple1.append(2)


---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-61-a29bee310764> in <module>()
----> 1 tuple1.append(2)

AttributeError: 'tuple' object has no attribute 'append'

In [62]:
tuple1[0] = 5


---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-62-d604d7b0f62b> in <module>()
----> 1 tuple1[0] = 5

TypeError: 'tuple' object does not support item assignment

Sets

  • Lists can contain duplicate values.

  • A set, in contrast, contains no duplicates.

  • Sets can be created from lists using the set() function.


In [63]:
X = set([1, 2, 3, 3, 4])
print(X)


{1, 2, 3, 4}

In [64]:
type(X)


Out[64]:
set
  • Alternatively we can write a set literal using the { and } brackets.

In [65]:
X = {1, 2, 3, 4}
type(X)


Out[65]:
set

Sets are mutable

  • Sets are mutable like lists:

In [66]:
X.add(5)
print(X)


{1, 2, 3, 4, 5}
  • Duplicates are automatically removed

In [67]:
X.add(5)
print(X)


{1, 2, 3, 4, 5}

Sets are unordered

  • Sets do not have an ordering.

  • Therefore we cannot index or slice them:


In [68]:
X[0]


---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-68-1d61f6e5db90> in <module>()
----> 1 X[0]

TypeError: 'set' object does not support indexing

Operations on sets

  • Union: $X \cup Y$

In [69]:
X = {1, 2, 3}
Y = {4, 5, 6}
X.union(Y)


Out[69]:
{1, 2, 3, 4, 5, 6}
  • Intersection: $X \cap Y$:

In [70]:
X = {1, 2, 3, 4}
Y = {3, 4, 5}
X.intersection(Y)


Out[70]:
{3, 4}
  • Difference $X - Y$:

In [71]:
X - Y


Out[71]:
{1, 2}

Dictionary

  • Key Value list

In [72]:
person = {'name':'sofian','gender':'male', 'age':17}
person['new'] = 'new'
print (person.keys())
print (person.values())
print (person['name'])
print (person)


dict_keys(['name', 'gender', 'age', 'new'])
dict_values(['sofian', 'male', 17, 'new'])
sofian
{'name': 'sofian', 'gender': 'male', 'age': 17, 'new': 'new'}

Defining new functions


In [73]:
def squared(x):
    return x ** 2

print (squared(5))


25

Local Variables

  • Variables created inside functions are local to that function.

  • They are not accessable to code outside of that function.


In [74]:
def squared(x):
    result = x ** 2
    return result

print (squared(5))


25

In [75]:
print (result)


None

Functional Programming

  • Functions are first-class citizens in Python.

  • They can be passed around just like any other value.


In [76]:
print(squared)


<function squared at 0x102d80f28>

In [77]:
y = squared
print (y)


<function squared at 0x102d80f28>

In [78]:
print (y(5))


25

Mapping the elements of a collection

  • We can apply a function to each element of a collection using the built-in function map().

  • This will work with any collection: list, set, tuple or string.

  • This will take as an argument another function, and the list we want to apply it to.

  • It will return the results of applying the function, as a list.


In [79]:
list(map(squared, [1, 2, 3, 4]))


Out[79]:
[1, 4, 9, 16]

List Comprehensions

  • Because this is such a common operation, Python has a special syntax to do the same thing, called a list comprehension.

In [80]:
[squared(i) for i in [1, 2, 3, 4]]


Out[80]:
[1, 4, 9, 16]
  • If we want a set instead of a list we can use a set comprehension

In [81]:
{squared(i) for i in [1, 2, 3, 4]}


Out[81]:
{1, 4, 9, 16}

Cartesian product using list comprehensions

The Cartesian product of two collections $X = A \times B$ can be expressed by using multiple for statements in a comprehension.


In [82]:
A = {'x', 'y', 'z'}
B = {1, 2, 3}
{(a,b) for a in A for b in B}


Out[82]:
{('x', 1),
 ('x', 2),
 ('x', 3),
 ('y', 1),
 ('y', 2),
 ('y', 3),
 ('z', 1),
 ('z', 2),
 ('z', 3)}

Cartesian products with other collections

  • The syntax for Cartesian products can be used with any collection type.

In [83]:
first_names = ('Steve', 'John', 'Peter')
surnames = ('Smith', 'Doe')

[(first_name, surname) for first_name in first_names for surname in surnames]


Out[83]:
[('Steve', 'Smith'),
 ('Steve', 'Doe'),
 ('John', 'Smith'),
 ('John', 'Doe'),
 ('Peter', 'Smith'),
 ('Peter', 'Doe')]

Anonymous Function Literals

  • We can also write anonymous functions.
  • These are function literals, and do not necessarily have a name.
  • They are called lambda expressions (after the $\lambda-$calculus).

In [ ]:
list(map(lambda x: x ** 2, [1, 2, 3, 4]))

Filtering data

  • We can filter a list by applying a predicate to each element of the list.

  • A predicate is a function which takes a single argument, and returns a boolean value.

  • filter(p, X) is equivalent to $\{ x : p(x) \; \forall x \in X \}$ in set-builder notation.


In [ ]:
list(filter(lambda x: x > 0, [-5, 2, 3, -10, 0, 1]))

We can use both filter() and map() on other collections such as strings or sets.


In [ ]:
list(map(ord, 'hello world'))

In [ ]:
''.join(list(filter(lambda x: x != 'l' , 'hello world')))

Filtering using a list comprehension

  • Again, because this is such a common operation, we can use simpler syntax to say the same thing.

  • We can express a filter using a list-comprehension by using the keyword if:


In [ ]:
data = [-5, 2, 3, -10, 0, 1]
[x for x in data if x > 0]

Thanks

  • linkedin, facebook, instagram, twitter = sofianhw

  • wa, telegram = 085273839851

  • email = me@sofianhw.com