Thursday, January 18, 2018

Python 3 fundamentals

#!/usr/bin/env python3

""" GENERAL """

#Python is case-sensitive !!!
#indentations instead of curly braces
#use 4 empty spaces OR tab
#do not mix tabs and spaces

#run python files from CommandPrompt or PowerShell:
#C:\>python filename.py arguments

type(a)           #returns the type of a
sys.argv[0]       #returns the name of the file
sys.argv[1]       #returns the command line argument

print('things inside braces ',
      'can be spread over multiple '+
      'lines')


""" DOCUMENTING """

def myFunc(a):
    """This is a demo function
    Arguments:
        a: string entered by the user
    Returns:
        The same string appended by "bee"
    """
    print(a+' + bee is')
    return a + 'bee'



help(myFunc)      #returns the description of the function


""" SCALAR TYPES """

#integer or float
6/3               #results float 2.0
6//3              #results integer 3

#convert to integer
int(3.5)          #3
int(-3.5)         #-3 (rounds toward zero)
int("300")        #300

#None type
a = None

#Bool
bool(0)           #false; only 0 is false, any other number is true
bool(0.0)         #false
bool(1)           #true
bool(-1.25)       #true

bool([])          #false; any empty collection (string, list, etc..) is false
bool("")          #false
bool([])          #true; any non-empty collection is true
bool([1,6])       #true
bool("0")         #true
bool("False")     #true


""" CONTITIONAL STATEMENTS """

if a < 5:
   print("is smaller than five")
elif a < 3:
   print("is smaller than 3")
else:
   print("is equal or bigger than 5")

  
""" WHILE LOOP """

#will print 5,4,3,2,1
c = 5
while c != 0:
      print (c)
      c -= 1            #decrement by one


#will break the loop if
c = 5
while c != 0:
      print (c)
      if c == 3:
            print("let's break here, no need to go down to 0")
            break
      c -= 1            #decrement by one
     
     
#will wait for the correct response :)
while True:
      response = input("How many musketeers? ")
      if response == "3" or response == "three":
            print("That is correct !")
            break
      print("Wrong answer.")
     

""" STRING """

#using escape characters
path = "c:\\\\Users\d\'Artagnan"
print(path)

#using raw string instead
path = r"c:\\Users\d'Artagnan"
print(path)

#encode and decode strings with special characters
francais = "réponse"
data = francais.encode("utf-8")
print(data)
french = data.decode("utf-8")
print(french)

#string and parts of a string
a = 'montreal'
print(a[0])             #m
print(a[1])             #o
print(a[-1])            #l
print(a[0:5])           #montr
print(a[2:5])           #ntr
print(a[2:-2])          #ntre
print(a[:])             #montreal

#join and split
colors = '-'.join(['red','green','blue'])
print(colors)                             #red-green-blue
print(colors.split('-'))                  #['red', 'green', 'blue']

#concatenate with join or with +
print(''.join(['uga','buga']))            #ugabuga
print('uga'+'buga')                       #ugabuga

#partition
left, middle, right = 'Jack and Jill'.partition(' and ')
print(left)                               #Jack
print(middle)                             # and
print(right)                              #Jill


#more methods
print('hello'.capitalize())               #Hello
print('hello'.replace('e','a'))           #Hallo
print('hello'.isalpha())                  #True
print('hello'.isdigit())                  #False
print(len('green'))                       #5

#format method
a = "Jack"
b = "Jill"
print('Hello, {0} and {1} !'.format(a,'Jimmy'))       #Hello, Jack and Jimmy !
print(f'Hello, {a} and {b} !')                        #Hello, Jack and Jill !

#another format exemple
a=3.14159
print(f'pi = {a} ')           #3.14159
print(f'pi = {a:.2f} ')       #3.14


""" RANGE """

print(list(range(10)))        #[0, 1, 2, 3, 4, 5, 6, 7, 8, 9
print(list(range(5,10)))      #[5, 6, 7, 8, 9]
print(list(range(0,10,2)))    #[0, 2, 4, 6, 8]

for i in range (10):          # will output 0,1,2,3,4,5,6,7,8,9
      print(i)

for i in range (5,10):        # will output 5,6,7,8,9
      print(i)
     
for i in range (0,10,2):      # will output 0,2,4,6,8
      print(i)
     
     
""" LIST """

a = []                        #declare empry list
a.append('X')                 #add values in a list
print(a)                      #['X']

b = [1,33,66]                 #declare list with values
b[0] = 'F'                    #modify value
print(b)                      #['F', 33, 66]


mylist = "m o n t r e a l".split()

print(mylist)                 #['m', 'o', 'n', 't', 'r', 'e', 'a', 'l']
print(mylist[0])              #m
print(mylist[1])              #o
print(mylist[-1])             #l
print(mylist[0:5])            #['m', 'o', 'n', 't', 'r']
print(mylist[2:5])            #['n', 't', 'r']
print(mylist[2:-2])           #['n', 't', 'r', 'e']
print(mylist[:])              #['m', 'o', 'n', 't', 'r', 'e', 'a', 'l']


#copying lists - creates shallow copies
seq = [1,2,3]
seq2 = seq[:]                 #creates shallow copy
seq3 = seq.copy()             #creates shallow copy
seq4 = list(seq)              #creates shallow copy
seq5 = seq                    #creates a reference !!!

seq[0] = 100                  #modifying original list
print(seq)                    #[100, 2, 3]
print(seq2)                   #[1, 2, 3]
print(seq3)                   #[1, 2, 3]
print(seq4)                   #[1, 2, 3]
print(seq5)                   #[100, 2, 3]


#find value in a list, count value instances
mylist = ['heads','shoulders','knees','and','toes','knees','and','toes']
i = mylist.index('heads')
print(i)                                  #0
print(mylist[i])                          #heads
print('knees' in mylist)                  #True; 'in' and 'not in' are supported
print(mylist.count('knees'))              #2

#remove elements by index or by value
mylist = ['heads','shoulders','knees','and','toes']
del mylist[0]
print(mylist)                             #['shoulders', 'knees', 'and', 'toes']
mylist.remove('shoulders')
print(mylist)                             #['knees', 'and', 'toes']
mylist.insert(0,'head')
print(mylist)                             #['head', 'knees', 'and', 'toes']

#convert string to a list
mylist = ['heads','shoulders','knees','and','toes']
str = ' '.join(mylist)
print(str)                                #heads shoulders knees and toes

#growing lists
m = [1,2,3]
k = m + [4]
k += [5]
k.extend([6,7])
print(k)                                  #[1, 2, 3, 4, 5, 6, 7]

#sort and inverse
m = [4,2,1,3]
m.sort()
print(m)                                  #[1, 2, 3, 4]
m.reverse()                               #same as m.sort(reverse=True)
print(m)                                  #[4, 3, 2, 1]

#sort by length
m = ['Melbourn','Oz','Paris']
m.sort(key=len)
print(m)                                  #['Oz', 'Paris', 'Melbourn']

m = ['Melbourn','Oz','Paris']
m.sort(key=len,reverse=True)
print(m)                                  #['Melbourn', 'Paris', 'Oz']


""" DICTIONNARY """

#can be multi-line, thanks to the {}
d = { 'Yahoo':    'http://yahoo.com',
            'Google':   'http://google.com'}
           
d['Google'] = 'www.google.com'                  #modify the value of 'Google'
d['Redmine'] = 'www.redmine.com'                #add new values
print(d)                                        #{'Yahoo': 'http://yahoo.com', 'Google': 'www.google.com', 'Redmine': 'www.redmine.com'}

#keys must be immutable
#values can be mutable
#order is arbitrary !!!!


#copy dictionnaries
e = d.copy()
f = dict(d)

#update from another dictionnary
f = {'red':'rouge', 'green':'verde'}
g = {'yellow':'jaune', 'green':'vert'}
f.update(g)                                     #add and update (f to g)
print(f)                                        #{'red': 'rouge', 'green': 'vert', 'yellow': 'jaune'}

#list keys and values
colors = {'red': '#ff0000', 'blue': '#0000ff'}
for key,value in colors.items():
    print(key + ' is ' +  value)
     
#verify membership and remove items
colors = {'red': '#ff0000', 'blue': '#0000ff'}
print('red' in colors                           #True; 'in' and 'not in' are supported
del colors['red']
print('red' in colors)                          #True


""" SET """

e = set()                                       #creates an empty set
s = {1,2,'a','b'}                               #creates set with values
print(s)                                        #{1, 2, 'b', 'a'}

#use set() to remove duplocates from a list

t = [1,2,2,3,3,3,4,4,5,5,5,5]
s = set(t)
print(s)                                        #{1, 2, 3, 4, 5}

#in and not in are supported
#add(element) to add elements; existing elements are ignored
#remove(element) to remove elements (error if the element )
#discard(element) to remove elements
#update() to update elements

#copy sets
d = {1,2,'a','b'}
e = d.copy()
f = set(d)

#used to compute unions, intersections, differences, subsets


""" FOR LOOP """

#with range
for i in range (10):                      # will output 0,1,2,3,4,5,6,7,8,9
      print(i)
     
#with list
cities = ['London', 'Paris', 'Oslo']
for a in cities:
    print(a)

#with dictionnary
colors = {'red': '#ff0000', 'blue': '#0000ff'}

for a in colors:
      print(a)                            #will return the key

for a in colors:
      print(colors[a])                    #will return the value

#break and continue
for i in range (5):                       # will stop and only output 0,1,2
    if i == 3:
        break
    print(i)

for i in range (5):                       # will skip 3 and output 0,1,2,4
    if i == 3:
        continue
    print(i)           

     
""" FUNCTION """

def myFunc(a):                           
    print(a+' + bee is')
    return a + 'bee'

a = input('Type something : ')
print(myFunc(a))                          #using the result returned by the function


""" FUNCTION ARGUMENTS """

def f(a,b="bananas"):                     #b argument has a default value
    print(+ ' and ' + b)

f('apples')                               #apples and bananas
f('apples', 'strawberries')               #apples and strawberries
f(b="dogs", a="cats")                     #cats and dogs

#passing additional arguments
def function (argument, *args)            # *args is a list
def function (argument, **kwargs)         # **kwargs is a dictionnary


""" ARGUMENT PASSING """

#k and m are the same thing
m = [1,2]
def modify(k):
    k.append(3)
    print('k value is ' + str(k))         #k value is [1, 2, 3]
    print('k id is ' + str(id(k)))        #k id is 58376960

modify(m)
print('m value is '+str(m))               #m value is [1, 2, 3]
print('m id is ' + str(id(m)))            #m id is 58376960

#k and m are no loger the same thing
m = [1,2]
def modify(k):
    k = [1,2]                             #here is when k gets redirected to another object in the memory
    k.append(3)
    print('k value is ' + str(k))         #k value is [1, 2, 3]
    print('k id is ' + str(id(k))         #k id is 49463088

modify(m)
print('m value is '+str(m))               #m value is [1, 2]
print('m id is ' + str(id(m)))            #m id is 49595136


""" VARIABLE SCOPING """

#access variable within a function
count = 6
laps  = 6

def increase(a,b):
    count = a
    count += 1                #count becomes local variable

    global laps               #gives access to the global variable
    laps = b
    laps += 1                 #modifies the global variable

increase(count,laps)

print(count)                  #6
print(laps)                   #7


""" EXCEPTION HANDLING """

try:
    x = int('sdf')
    print('Conversion succeeded! x = ', x)
except ValueError:                              #error message for a specific error
    print('Value error')
except IndentationError:                        #error message for another error
    pass                                        #do nothing
except (TypeError, NameError):                  #regroup errors
    print('Conversion failed')
except Exception:                               #will catch any error
    print('Unknown error')

#get error details
try:
    x = int('sdf')
except Exception as error:
    print('Unknown error - ', error )
     
     
""" CLASS """

class Student():

    shcool = 'Ahuntsic'                         #class attribute - the same for every instance

    def __init__(self, name):                   #'self' refers to the instance;
        print (name + ' created')
        self._name = name                       #creating instance attribute

    def __str__(self):
        return 'Student ' + self._name          #will be returned when the instance is printed

    def name_format(self):                      #functions inside classes = methods
        return self._name.capitalize()          #calitalizes the instance attribute 'name'


mark = Student('mark')                          #'mark' is an instance of the class 'Student'
john = Student('john')                          #another instance of the class 'Student'
print(mark.name_format())                       #Mark
print(john.name_format())                       #John
print(mark)                                     #Student mark
print(john)                                     #Student john
print(Student.shcool)                           #Ahuntsic
print(mark.shcool)                              #Ahuntsic
print(john.shcool)                              #Ahuntsic

class HighSchoolStudent(Student):               #HighSchoolStudent class is derived from Student class

    shcool = 'ETS'                              #overwrites the parent class attribute

    def name_format(self):                      #overwrites the parent method
        a = super().name_format()               #the parent method is still accessible via super()
        return ' '.join(a)                      #adds spaces


maria = HighSchoolStudent('maria')              #maria created
print(maria.shcool)                             #ETS
print(maria.name_format())                      #M a r i a


""" IMPORT """

# --- tools.py ----                             #a simple exemple of python module
def format_names(a):
    return a.capitalize()
#------------------

# --- main.py ----
import tools                                    #import tools.py; note that .py is omitted
print(            format_names('maria'))        #rises an error
print(tools.format_names('maria'))              #works fine
#------------------

# --- main.py ----
from tools import *                             #import all from tools.py
print(            format_names('maria'))        #works fine
print(tools.format_names('maria'))              #rises an error
#------------------

# --- main.py ----
from tools import format_names                  #import specific function from tools.py
print(            format_names('maria'))        #works fine
print(tools.format_names('maria'))              #rises an error
#------------------

# --- main.py ----
from tools import format_names as fnam          #import with an alias
print(            format_names('maria'))        #rises an error
print(fnam('maria'))                            #works fine
#------------------


#Module code is executed once, on first import

#this helps tell if the file is imported or executed directly
if __name__ == '__main__':
    print('the file is executed alone')
else:
    print('the file is imported')
     
#Some definitions :
#Python module - convenient import
#Python script - convenient execution from command line
#Python program - perhaps composed of many modules


""" WORKING WITH FILES """

#write
f = open('test.txt', mode='at', encoding='utf-8')   #'at' is append text
f.write('just testing ')
f.write('new things\n')                             #both 'write' and 'writelines' require \n to end a line
f.writelines(['more tests',
              ' comming soon\n',   
              'farewell ',
              'my friend'])
f.close()

#readline
g = open('test.txt', mode='rt', encoding='utf-8')   #'rt' is read text
print(g.readline())                                 #will read the first line
print(g.readline())                                 #will read the next line
g.close()

g = open('test.txt', mode='rt', encoding='utf-8')
list = g.readlines()                                #will read all lines into a list
g.close()
print(list)

#filename passed as argument and reads all the lines from a file
import sys
def main(filename):
    f = open(filename, mode='rt', encoding='utf-8')
    for line in f:
        print(line)
    f.close()

main(sys.argv[1])


""" EXECUTABLE FILE """

#to install the exe generator, type in PowerShell :
#>pip install pyinstaller

#to create an exe from somefile.py :
#>pyinstaller --onefile somefile.py