A = (1,2,3,4,5) # quel est le type de A, comment modifier le deuxième élément par 10

"""
Explanation: Mise en situation 2020 (Python3)
Citer les principaux types en Python
https://docs.python.org/fr/3/library/datatypes.html


numbers.Number : créés par les littéraux numériques et renvoyés en tant que résultats par les opérateurs et les fonctions arithmétiques natives. Python distingue les entiers (int,bool), les nombres à virgule flottante (float) et les nombres complexes (complex).


Python fournit les types natifs suivant : dict, list, set, frozenset, et tuple.

La classe str est utilisée pour stocker des chaînes de caractères Unicode.
Les classes bytes et bytearray des données binaires.
Ensembles (set, frozenset) : Ils représentent les ensembles d'objets, non ordonnés, finis et dont les éléments sont uniques.
Ensembles (set en anglais) : représentent les ensembles muables. Un ensemble est créé par la fonction native constructeur set() et peut être modifié par la suite à l'aide de différentes méthodes, par exemple add().
Ensembles gelés (frozenset en anglais) Ils représentent les ensembles immuables. Ils sont créés par la fonction native constructeur frozenset(). Comme un ensemble gelé est immuable et hachable, il peut être utilisé comme élément d'un autre ensemble ou comme clé de dictionnaire.


None est un type qui ne possède qu'une seule valeur. Il n'existe qu'un seul objet avec cette valeur. Vous accédez à cet objet avec le nom natif None. Il est utilisé pour signifier l'absence de valeur dans de nombreux cas, par exemple pour des fonctions qui ne retournent rien explicitement. Sa valeur booléenne est fausse.

Quels types de données sont immuables (« immutables » en anglais) en Python 3 ?
https://docs.python.org/3/reference/datamodel.html?highlight=immutable


Tous les types numbers.* tels que Number, Integral (bool,int), Real etc.. sont des objets immuables. Une fois  créés, leur valeur ne change pas. 


Ensembles gelés (frozenset en anglais) Ils représentent les ensembles immuables.


Séquences immuables : Un objet de type de séquence immuable ne peut pas être modifié une fois qu'il a été créé. Si l'objet contient des références à d'autres objets, ces autres objets peuvent être muables et peuvent être modifiés ; cependant, les objets directement référencés par un objet immuable ne peuvent pas être modifiés. Les types suivants sont des séquences immuables :

Chaînes de caractères (string en anglais) : est une séquence immuable de valeurs qui représentent des caractères Unicode. 
n-uplets (ou tuples) Les éléments d'un tuple sont n'importe quels objets Python. Les tuples de deux ou plus éléments sont formés par une liste d'expressions dont les éléments sont séparés par des virgules.
Bytes : Les objets bytes sont des tableaux immuables. Les éléments sont des octets (donc composés de 8 bits), représentés par des entiers dans l'intervalle 0 à 255 inclus. Les littéraux bytes (tels que b'abc') et la fonction native constructeur bytes() peuvent être utilisés pour créer des objets bytes. Aussi, un objet bytes peut être décodé vers une chaîne via la méthode decode().
End of explanation
"""


A = (1,2,3,4,5)
print(type(x))
print(2 in x)
x[1]=10       # va générer une erreur : TypeError: 'tuple' object does not support item assignment
print(x)

"""
Explanation: La variable A est de type "tuple", type qui permet de stocker des éléments pouvant être différents dans une structure "immutable"
+ Un des avantages des tuples par rapport aux listes est la vitesse
+ Tuples ont par définition une taille fixe (alors que les Listes sont dynamiques)
+ Tuple est "immutable" alors qu'une structure de type list est "mutable".
On ne peut ni ajouter ni enlever des élements d'un "tuple".
End of explanation
"""


x = [1,2,3,4,5]  # ici on crée une liste
print(type(x))
x[1]=10       # avec une liste c'est possible
print(x)
print(10 in x)

"""
Explanation: Pour pouvoir modifier un des éléments il faut utiliser le type List comme ci-après:
End of explanation
"""


a = [1,2,2,2,4,6,1,8,9]    # ici on crée une liste
ensemble = set(a) # que l'on convertit en set
print(ensemble)

"""
Explanation: A partir d'une Liste on peut extraire les éléments distincts en utilisants un set :
End of explanation
"""


texte = "Les sanglots longs des violons de l'automne, blessent mon coeur d'une langeur monotone. Les longs mois de l'automne me blessent !"
liste = texte.split()         # on convertit la chaine de caracatères en liste (espaces comme séparateurs par défaut)
mots_uniques = set(liste)     # on extrait les mots uniques en utilisant un set
print(mots_uniques)      
[(mot,liste.count(mot)) for mot in mots_uniques]  # on peut maintenant utiliser liste.count(mot) pour gener


s = "Momo le haricot !"
# impossible de faire s[0]='T' car le type string est immutable
print(type(s))
l = list(s)
l[0]='T'
l[2]='t'
print('_'.join(l))

"""
Explanation: Comment obtenir une liste de mots uniques (séparés par des espaces) avec le nombre d’occurrences de chacun de ces mots ?
End of explanation
"""


class Point(object):
    """
    A simple 2D cartesian Point with x and y coordinates
    """
    
    def __init__(self,x=0.0,y=0.0):
        self.x = float(x)
        self.y = float(y)
        
    def __repr__(self):
        return ("Point(x={x}, y={y})".format(x=self.x,y=self.y))
    
    def move(self,newx,newy):
        """
        This method moves the point to newx,newy position
        """
        self.x = float(newx)
        self.y = float(newy)
        
    def moverel(self,dx,dy):
        """
        This method moves the point ny dx,dy relative to its current position
        """
        self.x += float(dx)
        self.y += float(dy)
        
        

P1 = Point(3,5)
print(P1)

"""
Explanation: Python Orienté Objet, implémenter une classe Point ?
End of explanation
"""


class NamedPoint(Point):
    
    def __init__(self,name,x,y):
        Point.__init__(self,x,y)
        self.name = name
        
    def __repr__(self):
        return ("NamedPoint(name={name},{point}) ".format(name=self.name, point= Point.__repr__(self)))

P3 = NamedPoint('P3',3,3)    
print(P3) 

"""
Explanation: Python Orienté Objet, implémenter une classe NamedPoint ?
End of explanation
"""


P3 = NamedPoint('P3',3,3) 
print("Au début voici le contenu de P3 :\n {p}".format(p=P3))
P4 = P3     # P3 et P4 pointe tous les deux sur le même objet P4 est un alias de P3
P4.name = 'P4'
P4.move(4,4)
P3.move(1,1)
print("A la fin toutes les modifications sur P3 ou P4 s'appliquent au même objet P4 est un alias de P3")
print(P3, id(P3))
print(P4, id(P4))


import copy                 # le module copy permet de palier a ce probleme si une methode clone n'existe pas
P3 = NamedPoint('P3',3,3) 
P4 = copy.copy(P3)          # ici on cree une copie de P3 dans la varaible P4 avec un nouvel emplacement mémoire
P4.name = 'P4'          
P4.move(4,4)
P3.move(1,1)
print(P3, id(P3))
print(P4, id(P4))

"""
Explanation: Python Orienté Objet :  référence, valeur et copie
End of explanation
"""


id=54321   # attention danger ! ici on vient de redefinir la fonction builtin id PAS BIEN DU TOUT !
class Account:
    def __init__(self, id):
        self.id = id + 1 # on stocke la valeur du parametre id dans l'attribut de la classe id en y ajoutant 1
        id = 0        # ici id correspond à une variable locale n'existant que dans la fonction (portée locale)
print(id) 
acc = Account(id=12345) # id ici correspond au nom du paramètre
print(acc.id)

"""
Explanation: Qu’est-ce qui est affiché par la dernière ligne ?
End of explanation
"""


confusion = {}              # on initialise un dictionary vide
confusion[1] = 1            # on ajoute une entrée au dict avec la clé d'accès 1
confusion['1'] = 2          # on ajoute une nouvelle entrée au dict avec la clé d'accès '1'
confusion[1.0] = 4          # on modifie l'entrée du dict avec la clé d'accès 1  1.0 ou 1 c'est pareil
 
somme = 0
for k in confusion:
    somme += confusion[k]
 
print(somme)
print(confusion)

"""
Explanation: Qu’est-ce qui est affiché par la dernière ligne ?
End of explanation
"""


x = True
y = False
z = False
 
if x or y and z:
    print("yes")
else:
    print("no")

"""
Explanation: Qu’est-ce qui est affiché par la dernière ligne ?
End of explanation
"""


inventaire = [("pommes", 22),("melons", 4),("poires", 18),("fraises", 76),("prunes", 51)]
#Classer cette liste selon la quantité de chaque fruit par ordre décroissant

inventaire_inverse = [(qtt, nom_fruit) for nom_fruit, qtt in inventaire]
print(inventaire_inverse)
inventaire_inverse.sort(reverse=True)
inventaire = [(nom_fruit, qtt) for qtt , nom_fruit in inventaire_inverse]
print(inventaire)

"""
Explanation: Classer la liste par ordre décroissant de quantité :
End of explanation
"""


import numpy as np
import numpy.random as nprnd
liste = nprnd.randint(1000, size=1000)
print(type(list(liste)))
print(sum(list(liste)))
%timeit sum(list(liste))

import functools
print(functools.reduce(lambda x,y: x+y, liste))
%timeit functools.reduce(lambda x,y: x+y, liste)

print(np.sum(liste))
%timeit np.sum(liste)

"""
Explanation: calculer la somme d’une liste de nombres
End of explanation
"""


liste = [2,1,1,2,4,1,3]

liste_filtree = [i for i in liste if i % 2 ==1]
print(liste_filtree)
sum(liste_filtree)

"""
Explanation: Calculer la somme d’une liste des nombres impairs :
End of explanation
"""


liste = [2,1,1,2,4,1,3]
list(set(liste))

"""
Explanation: Transformez cette liste pour qu'il ne reste que des éléments unique  :
End of explanation
"""


%%writefile test.txt
Ceci est la ligne numéro 1
et la ligne numéro deux
d'un fichier texte qui au final contiendra
quatre lignes !

# 1) Comment ouvrir un fichier en lecture en traitant les exceptions possibles ?
my_file = 'test2.txt'
try:
    f = open(my_file)

    try:
        all = f.readlines()        
        print("FILE : {file} contains {numlines:06d} lines ".format(file=my_file,numlines=len(all)))
    except EXPECTED_EXCEPTION_TYPES as e:
        print("ERROR READING FILE : {file}".format(file=my_file))
        print(e)
    finally:
        f.close()

except (IOError, OSError) as e:
    print("ERROR OPENING FILE : {file}".format(file=my_file))
    print(e)
# https://pyformat.info/

my_file = 'test.txt'
with open(my_file) as f:
    all = f.readlines()
    print(all)
    print("FILE : {file} contains {numlines:06d} lines ".format(file=my_file,numlines=len(all)))
    

"""
Explanation: Meilleur pratique pour ouvrir un fichier:
End of explanation
"""


