#!/usr/bin/env python
# coding: utf-8
 
# !!Tip!!: To execute a specific block of code from pycharm:
# a) open a python console (Tools -> python console),
# b) highlight code
# c) alt + shift + e to execute  (You can bind this key combo to a single key in settings -> keymap)
 
### Python refresher. ###
 
# Numbers
a = 3 # These will be python integers
b = 2. # The dot makes it a float
c = float(5) # Explicit definition
 
# Check the types (everything is a class btw)
print(a, type(a))
print(b, type(b))
print(c, type(c))
 
# Potentially nasty bugs if this is done wrong. Float usually has type priority.
print(2/a, 2//a, 2./a, 2/c)
 
# Formating string for printing
print("a: {}, b: {}, c: {}, a * b + c: {}".format(a, b, c, a * b + c))
 
# Also a shorter format version:
foo = 3
print(f'foo = {foo}')
 
# Note: mostly the " and ' are interchangeable for strings.
 
# Lists: Universal container which can contain anything. Can be iterated.
my_list = [1,2,33,["foo"], 4]
my_list.append(print) # Add stuff to list (yes, I added a function to the list. It can be identified and called later)
del my_list[0] # Delete stuff
 
# Indexation is modular
print(my_list[-1], my_list[-2])
 
# Tuples: Immutable containers, also useful. Using round brackets and adding a comma makes it a tuple
not_a_tuple = (1)
a_tuple = (1,)
another_tuple = (1,3,3,7)
 
print(type(not_a_tuple), type(a_tuple), type(another_tuple))
 
# Broadcasting:
print(my_list[1:3])
my_list_2 = my_list[2:]
 
# The traditional way of iterating through a list, not very pythonic, avoid this.
for i in range(len(my_list)):
    print(my_list[i])
 
# Proper way of iterating through list:
for l in my_list:
    if callable(l): # If it's that print function from earlier, then call it with an argument
        l("Hello")
    else:
        print(l)
 
# Enumerate while iterating
for i, t in enumerate(another_tuple):
    print(i, t)
 
# Iterate over n iterables at the same time.
for e1, e2 in zip(my_list, another_tuple):
    assert e1 != e2 # This is like an 'if' statement, but will crash the program if not satisfied. Can be used in a try-catch
 
# Shortcut iterating
my_shortcut_list = [i**2 if i % 2 == 0 else "banana" for i in range(10)]
 
# None type. This is analogous to the null or void types in other languages
q = None
print(q is None) # Dont check None using == sign!
 
# Functions
def myfun_1(arg):
    arg = 3
 
a = 1
myfun_1(a)
print(a) # Unchanged
 
def myfun_2(arg):
    arg[1] = "err"
 
mod_list = list(range(10)) # Note than the function range gives a generator (which is iterable). It has to be converted to a list if you want to work with it
myfun_2(mod_list) # This will modify the list
print(mod_list)
 
# Tip: If you highlight the called function and press CTRL + b in pycharm, the IDE will show you the declaration of the function. 
# This is extremely useful in navigating over the larger repository of code.
 
fun_list = (lambda x : x + i for i in range(10)) # Generator of anonymous functions
print(list(fun(1) for fun in fun_list))
 
# Dictionaries: Very useful container. They key and value can literally be anything that's hashable
my_d = {"key_1" : "value_1", 24 : "value_2", (3,2,"33") : [111]}
my_d_2 = dict(var1=4, var2="3") # Can also be defined like this, instead of the {}. I never use this
 
# Add to dict
my_d["key_x"] = 9
 
print(my_d, '\n', my_d_2)
 
# Check if key is in dict
print((3,2,"33") in my_d)
 
# Iterate dict
for k in my_d_2.keys():
    print(k)
for v in my_d_2.values():
    print(v)
for k,v in my_d_2.items(): # Both keys and values at the same time
    print(k,v)
 
# !!! IMPORTANT NOTES AND TIPS !!!:
# For loops are very slow and should be avoided if possible. Broadcasting should be done instead
# You should absolutely master indexing and broadcasting.
# Everything is a class, be carefull when passing references!
# If you need to do something with an array/list you can be almost sure that a function already exists that helps you do that.
# Try out the debugger! Put a breakpoint somewhere and step the computation using F8. Observe the available variables and their values/attributes.
