# basic exception hanlding
try:
    # ... do something
    pass
except WhateverException as e:
    # ... do the handling here
    pass

# more "verbose" exception hanlding
try:
    # ... do something
    pass
except WhateverException as e:
    # ... do the handling here
    pass
finally:
    # ... code here is always executed
    pass

"""
Explanation: Tips & Tricks
Quick Tips
Exceptions   (try...except)
Exceptions are useful for doing proper error handling
End of explanation
"""


import sys

try:
    f = open('myfile.txt')
    s = f.readline()
    i = int(s.strip())
except IOError as e:
    print "I/O error({0}): {1}".format(e.errno, e.strerror)
except ValueError:
    print "Could not convert data to an integer."
except:
    print "Unexpected error:", sys.exc_info()[0]
    raise

"""
Explanation: working example (see https://docs.python.org/2/tutorial/errors.html)
End of explanation
"""


try:
    f = open('adasdasd.foo', 'r')
except:
    pass

"""
Explanation: Never use the "diaper" design pattern ...
End of explanation
"""


# doing it with a function
def my_square(x):
    return x**2
print(my_square(16))

# doing it with a lambda
my_square = lambda x: x**2
print(my_square(16))

"""
Explanation: execution continues silently and it would be horribly annoying to find what went wrong because no error message or warning will be displayed!!
lambda (inline functions)
End of explanation
"""


for i in range(80, 90):
    print(chr(i))

for character in map(chr, range(80, 90)):
    print(character)

import subprocess
import time
import sys

for char in map(chr, range(80, 90)):
    print('   ', char, '   ')
    sys.stdout.flush()
    time.sleep(0.05)
    subprocess.Popen("clear").wait()

import subprocess
import time
import sys
from IPython.display import clear_output

for char in map(chr, range(128, 256)):
    print('   ', char, '   ')
    time.sleep(0.1)
    clear_output(wait=0.1)

"""
Explanation: pythonic "loops"
End of explanation
"""


' '.join(map(chr, range(0, 128)))

"""
Explanation: map
End of explanation
"""


x = [1, 2, 10, -1, -8]
x_positive = []
for element in x:
    if element < 0:
        pass
    else:
        x_positive.append(element)
print(x_positive)

"""
Explanation: filter
End of explanation
"""


list(filter(lambda element: element > 0, x))

"""
Explanation: more elegant implementation (with filter)
End of explanation
"""


x = range(1000)
def calculate_sum1(array):
    my_sum = 0
    for element in array:
        my_sum += element
    return my_sum
print(calculate_sum(x))

from functools import reduce
def calculate_sum2(array):
    return reduce(lambda x, y: x + y, array)
print(calculate_sum2(x))

"""
Explanation: reduce
End of explanation
"""


%timeit calculate_sum1(x)
%timeit calculate_sum2(x) 

"""
Explanation: which is faster ?
End of explanation
"""


import numpy
x = numpy.arange(0, 10000)
%timeit x.sum()

# for methods/function calls that take lots of time
%time x.sum()

"""
Explanation: comparison to numpy
End of explanation
"""


c.InteractiveShellApp.extensions = ['autoreload']
c.InteractiveShellApp.exec_lines = ['%autoreload 2']
c.InteractiveShellApp.exec_lines.append('print("")')
c.InteractiveShellApp.exec_lines.append('print("disable autoreload in ipython_config.py to improve performance.")')
c.InteractiveShellApp.exec_lines.append('print("finished loading.")')

"""
Explanation: Configuring IPython
put this snippet at the bottom of:
 ~/.ipython/profile_default/ipython_config.py

and relaunch ipython. You have to do this once.
End of explanation
"""


