# Syntaxe du language

La syntaxe du langage python est connue pour être

> - Simple

> - Concise

> - Efficace

## L’indentation

Une des caractéristiques du langage Python est l’utilisation des indentations comme structuration des programmes.

Exemples:

    >>> word = 'bonjour'
    >>> for letter in word:
    ...     print(letter)
    ...
    b
    o
    n
    j
    o
    u
    r

L’interpréteur détermine le nombre d’espace au début de chaque ligne et en déduit le niveau d’indentation. Une mauvaise indentation peut provoquer une erreur syntaxique.

    >>> for letter in 'Bonjour':
    ... print(letter)
      File "<stdin>", line 2
        print(letter)
            ^
    IndentationError: expected an indented block

Par convention on utilise 4 caractères d’espacement pour une indentation.

Cette caractéristique du langage permet d’obtenir un code forcément bien indenté puisque l’indentation fait partie intégrante du langage.

## Les commentaires

Les commentaires sont préfixés par le caractère dièse “#” et peuvent être placés en fin de ligne ou prendre une ligne complète.

    >>> a = 'Bonjour' #Ceci est une affectation variable
    >>> # Maintenant j'affiche la variable
    ... print(a)
    Bonjour

## Les différents types

### Les types numériques

Il en existe 3 types:

> - Les entiers (simple ou long)

> - Les valeurs à virgule flottante

> - Les nombres complexes

#### Les entiers

- Les entiers sont des nombres qui s’étendent sur une machine 32 bits entre les valeurs -2e31+1 et 2e31-1, soit entre -2147483647 et 2147483647.

- Pour obtenir un nombre négatif on le préfixe du signe “-”

Les différentes bases:

- La forme décimale commence par un chiffre de 1 à 9 suivi de chiffre de 0 à 9.

- La base octale commence par “0” suivi de chiffres de 0 à 7.

- La base hexadécimale commence par “0x” suivi de chiffres 0 à 9 et de lettres de A à F.

Les entiers longs

- Les entiers longs sont eux limités uniquement par la capacité mémoire de la machine.

- Ils sont représentés de la même manière que les entiers mais sont suffixés de la lettre L.

- Depuis la version de 2.4 un nombre entier est automatiqument transtypé si il dépasse la plage autorisée.

    >>> x = 1
    >>> type(x)
    <type 'int'>
    >>> x = 10000000000000000000000000000000000000000
    >>> type(x)
    <type 'long'>

#### Les flottants

- Les flottants permettent de représenter des nombres réels.

- La partie entière est séparée de la partie fractionnelle par un “.”

    >>> x = 1.0
    >>> type(x)
    <type 'float'>
    >>> x = 1.
    >>> x = 0.1
    >>> x = .1
    >>> x = -09.02
    >>> x = -9.02

#### Les nombres complexes

    >>> a = 2 + 1j
    >>> b = 1 + 2j
    >>> a+b
    (3+3j)
    >>> a.imag
    1.0
    >>> a.real
    2.0
    >>> a.conjugate()
    (2-1j)

### Les séquences

Une séquence est une collection ordonnée d’éléments indexés par des nombres positifs. Les index varient de 0 à n-1 pour une séquence de n éléments.

Pour accéder à l’élement qui se trouve en position i on opére de la manière suivante:

sequence[i-1]

el1 | el2 |  | eli |  | eln
--- | --- | --- | --- | --- | ---
0 | 1 |  | i-1 |  | n-1

On peut utiliser des nombres négatifs en les faisant varier de -1 à -n :

sequence[-2]

el1 | el2 |  | eln-1 | eln
--- | --- | --- | --- | ---
-n | -n+1 |  | -2 | -1

Les séquences peuvent être coupées en tranches qui forment alors des sous séquences. sequence[i,j] forme la sequence de l’élement i inclus jusqu’à l’élément j exclu :

sequence[1:3]

el1 | el2 | el3 | el4 |  | eln
--- | --- | --- | --- | --- | ---
0 | 1 | 2 | 3 |  |

sequence[0:-1]

el1 | el2 | el3 | el4 |  | eln-1 | eln
--- | --- | --- | --- | --- | --- | ---
0 | 1 | 2 | 3 |  | -2 | -1

- sequence[:-1] <=> sequence[0:-1]

- sequence[1:] <=> sequence[1:n]

Il existe un troisième élement qui permet de faire varier le pas.

sequence[0:-1:2]

el1 | el2 | el3 | el4 |
--- | --- | --- | --- | ---
0 | 1 | 2 | 3 |

sequence[::-1] => inverse l’ordre de la séquence

Quelques primitives qui s’appliquent sur les séquences:

- len() : retourne le nombre d’élements de la séquence

- min(), max() : renvoie respectivement l’élement à valeur minimale et maximale de la séquence

- sum() : renvoie la somme des élements lorsque ceux ci sont additionnables.

En python il existe 4 types de séquences:

- Les strings

- Les unicode

- Les listes

- Les tuples

Il existe 2 grandes familles de séquences, les séquences immuables (string, unicode et tuple) et les séquences modifiables (liste).

#### Les bytes (ex string python 2)

Les bytes sont des valeurs alphanumériques, elles sont entourées de guillemets simples ou doubles ou de 3 guillemets simples ou doubles et préfixées par un b. Elles sont toujours encodées.

    >>> a = b'hello'
    >>> b = b"hello"
    >>> c = b'''hello'''
    >>> d = b"""hello"""

Un byte est une séquence ordonnée d’éléments.

Un byte n’est plus modifiable une fois instanciée.

Les byte sont des séquences de caractères. Par défaut, chaque caractère est codé sur 8 bits ce qui correspond à des valeurs de 0 à 255. Les valeurs de 0 à 127 sont les caractères de la tables ASCII ceux de 128 à 255 les caractères de la table ASCII étendue.

Il n’existe pas en python de type caractère, un caractère est un byte de longueur 1.

En python 2, les strings sont portés par l’ancienne classe: les strings.

Quelques primitives du langage sur les strings

    >>> chr(120)
    'x'
    >>> ord(b'x')
    120

Les bytes sont des séquences, les différents éléments sont accessibles comme décrit plus haut:

    >>> mot = b'hello'
    >>> mot[1]
    '101'

Les bytes sont des séquences immuables, leurs valeurs ne sont donc pas modifiables.

    >>> mot[1] = b'a'
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    TypeError: 'bytes' object does not support item assignment

##### Méthodes sur les bytes

    >>> mot = b'Hello'
    >>> dir(mot)
    ['__add__', '__class__', '__contains__', '__delattr__', '__doc__', '__eq__',
    '__format__', '__ge__', '__getattribute__', '__getitem__', '__getnewargs__',
    '__getslice__', '__gt__', '__hash__', '__init__', '__le__', '__len__',
    '__lt__', '__mod__', '__mul__', '__ne__', '__new__', '__reduce__',
    '__reduce_ex__', '__repr__', '__rmod__', '__rmul__','__setattr__',
     '__sizeof__', '__str__', '__subclasshook__', '_formatter_field_name_split',
     'capitalize', 'center', 'count', 'decode', 'encode', 'endswith', 'expandtabs',
     'find', 'format', 'index', 'isalnum', 'isalpha', 'isdigit', 'islower', 'isspace',
     'istitle','isupper', 'join', 'ljust', 'lower', 'lstrip', 'partition',
     'replace', 'rfind', 'rindex', 'rjust', 'rpartition', 'rsplit', 'rstrip',
     'split', 'splitlines', 'startswith', 'strip', 'swapcase', 'title',
     'translate', 'upper', 'zfill']

Exercice:Créer une string et essayer quelques méthodes. Voir en particulier les méthodes split et join.

#### Les strings (ex unicode)

Le type str fonctionne de la même façon mais sur une plage de 32 bits. Ils sont standards en python 3.x. (format encodage utilisé par défaut).

    >>> unichr(16000)
    u'\u3e80'
    >>> print(unichr(16000))
    㺀
    >>> ord('€')
    8364

La conversion entre bytes et strings* est possible grâce aux méthodes encode et décode, puique la relation ne peut être bijective on utilise un codec qui permet d’établir la correspondance entre les deux.

    >>> encode = 'é'.encode('ISO-8859-15')
    >>> print(encode)
    é
    >>> decode='é'.encode('ISO-8859-15').decode('ISO-8859-15')
    >>> print(decode)
    é

Formatage des chaines de caractères (strings ou bytes)

    >>> firstname = 'John'
    >>> lastname = 'Cleese'
    >>> print("Hello {}".format(firstname))
    Hello John
    >>> print("Hello {0} {1}".format(firstname, lastname))
    Hello John Cleese

Il existe certaines expression pour modifier la valeur de l’argument. Exemple, pour les float, la syntaxe est .[P]f où P est un paramètre optionnel qui précise la précion utilisée.

    >>> pi = 3.14141592654
    >>> print ("pi vaut {:.2f}".format(pi))
    >>> print ("pi vaut {0:.2f}".format(pi))

    >>> pi = 3.14141592654
    >>> print ("pi vaut {:.2f}".format(pi))
    >>> print ("pi vaut {0:.2f}".format(pi))

D’autres méthodes de formatage existent, mais elles sont à proscrire:

    >>> firstname = 'John'
    >>> lastname = 'Cleese'
    >>> print("Hello %s" % firstname)
    Hello John
    >>> print("Hello %s %s" % (firstname,lastname))
    Hello John Cleese

TODO: utiliser la méthode format avec quelques exemples

#### Les tuples

Les tuples sont des séquences qui contiennent des éléments qui peuvent être hétérogènes. Un tuple est encadré par des parenthèses et chaque élément est séparé par une virgule. Une fois créé il est impossible de modifier sa valeur.

    >>> actors = ('John Cleese', 'Terry Gilliam', 'Michael Palin')
    >>> heterogene = ('John Cleese', 2, u'Terry Gilliam')
    >>> len(actors)
    3
    >>> actors[1]
    'Terry Gilliam'

    >>> actors[1] = 'Terry Jones'
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    TypeError: 'tuple' object does not support item assignment

#### Les listes

Les listes sont des séquences d’élements qui peuvent être hétérogènes et dont la valeur peut être modifiée après sa création.

Une liste est encadrée par des crochets et chaque élément est séparé par une virgule.

    >>> liste_vide = []
    >>> actors = ['John Cleese']
    >>> len(actors)
    1

Voici quelques méthodes qui s’appliquent aux listes.

    >>> dir(actors)
    ['__add__', '__class__', '__contains__', '__delattr__', '__delitem__', '__delslice__',
     '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__',
     '__getslice__', '__gt__', '__hash__', '__iadd__', '__imul__', '__init__',
     '__iter__', '__le__', '__len__', '__lt__', '__mul__', '__ne__', '__new__',
     '__reduce__', '__reduce_ex__', '__repr__', '__reversed__', '__rmul__',
     '__setattr__', '__setitem__', '__setslice__', '__sizeof__', '__str__','__subclasshook__',
     'append', 'count', 'extend', 'index', 'insert', 'pop', 'remove', 'reverse', 'sort']
    >>> actors.append('Terry Gilliam')
    >>> actors
    ['John Cleese', 'Terry Gilliam']
    >>> new_actors = ['Michael Palin', 'Terry Jones']
    >>> actors.extend(new_actors)
    >>> actors
    ['John Cleese', 'Terry Gilliam', 'Michael Palin', 'Terry Jones']
    >>> actors.insert(2, 'Eric Idle')
    >>> actors
    ['John Cleese', 'Terry Gilliam', 'Eric Idle', 'Michael Palin', 'Terry Jones']
    >>> actors.remove('John Cleese')
    >>> actors
    ['Terry Gilliam', 'Eric Idle', 'Michael Palin', 'Terry Jones']
    >>> actors.pop(2)
    'Michael Palin'
    >>> actors
    ['Terry Gilliam', 'Eric Idle', 'Terry Jones']
    >>> actors.index('Terry Jones')
    2
    >>> actors.append(actors[2])
    >>> actors
    ['Terry Gilliam', 'Eric Idle', 'Terry Jones', 'Terry Jones']
    >>> actors.count('Terry Jones')
    2
    >>> actors.sort()
    >>> actors
    ['Eric Idle', 'Terry Gilliam', 'Terry Jones', 'Terry Jones']
    >>> actors.reverse()
    >>> actors
    ['Terry Jones', 'Terry Jones', 'Terry Gilliam', 'Eric Idle']

Exercice: Créer une liste et essayer certaines méthodes

### Les dictionnaires

Le mapping, appelé dictionnaire, est une collection d’éléments qui sont identifiés par des clefs uniques.

- Un dictionnaire n’est pas ordonné.

- Les clefs doivent être des objets immuables (chaines de caractère, entier, flottant…)

- Les élements peuvent être hétérogènes

Un dictionnaire est encadrée par des accolades, chaque élement est séparé par une virgule, Chaque élement est préfixé par sa clef suivi de “:”.

    >>> dico_vide = {}
    >>> cleese = {'firstname':'John', 'lastname':'Cleese', 'year_of_birth':1939, 'movies':['Monty Python, la vie de Brian', 'Un poisson nommé wanda']}
    >>> len(cleese)
    4
    >>> cleese['firstname']
    'John'

Voici quelques méthodes des dictionnaires

    >>> a = {}
    >>> dir(a)
    >>> ['__class__', '__contains__', '__delattr__', '__delitem__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__',
     '__getattribute__', '__getitem__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__iter__', '__le__',
      '__len__', '__lt__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__setitem__',
      '__sizeof__', '__str__', '__subclasshook__', 'clear', 'copy', 'fromkeys', 'get', 'items', 'keys', 'pop', 'popitem', 'setdefault', 'update', 'values']

Autres exemples de création

    >>> dict(sape=4139, guido=4127, jack=4098)
    {'sape': 4139, 'jack': 4098, 'guido': 4127}
    >>> dict([('sape', 4139), ('guido', 4127), ('jack', 4098)])
    {'sape': 4139, 'jack': 4098, 'guido': 4127}

Exercice: Créer un dictionnaire et essayer certaines méthodes

### Les ensembles (type set)

TODO: un conteneur d’objets uniques avec des méthodes permettant de faire des calculs d’intersection, d’union, etc…

Un ensemble est encadré par des accolades et chaque élément est séparé par une virgule.

    >>> ensemble = {} #dictionnaire
    >>> ensemble = {'John Cleese','Terry Gilliam'} # type set
    >>> len(ensemble)
    2
    >>> ensemble.update({'John Cleese'})
    >>> len(ensemble)
    2

### None

Type à valeur unique qui ne représente une absence de valeur.

### Les booleans

Représentés par “True” ou “False”

Tout les types de valeur peuvent être interprétés de manière booléénne

> type | False | True
> --- | --- | ---
> int | 0 | les autres valeurs
> float | .0 | les autres valeurs
> list | [] | les listes non vides
> tuple | () | les tuples non vides
> dict | {} | les dicts non vides
> noneType | None | X

### Synthèse des types

> type | exemple | sequence | modifiable
> --- | --- | --- | ---
> int | 1 | NON | NON
> float | 1.0 | NON | NON
> complex | 1+1j | NON | NON
> unicode | “Hello” | OUI | NON
> byte | b’Hello” | OUI | NON
> tuple | (“string”,1) | OUI | NON
> list | [“string”,1] | OUI | OUI
> dict | {“key”:”valeur”} | NON | OUI

### Transtypage

> primitive | exemple | description
> --- | --- | ---
> str(el) | str(1) => “1” | retourne l’élement sous forme de string
> int(el) | int(“1”) => 1 | retourne l’élement sous forme d’entier
> float(el) | float(“1”)=>1.0 | retourne l’élement sous forme de float
> list(el) | list(“1”)=>[“1”] | retourne l’élement sous forme de liste
> tuple(el) | tuple([1])=>(1,) | retourne l’élement sous forme de tuple

## Les opérateurs

### Les opérateurs de base

Addition, soustraction, multiplication, division

    >>> 2+3
    5
    >>> 3-5
    -2
    >>> 2*7
    14
    >>> 'hello'*5
    'hellohellohellohellohello'
    >>> 'hello' + ' ' + 'world'
    'hello world'
    >>> 1/6
    0
    >>> 1.0/6
    0.16666666666666666

### Autre opérateurs

#### Modulo

opérateur %

    >>> 10 % 3
    1

#### Négation

opérateur -

    >>> a = 10
    >>> -a
    -10

#### Puissance

opérateur **

    >>> 2 ** 8
    256

#### Appartenance

opérateur in

S’applique à toutes les séquences

    >>> 'y' in 'Terry'
    True
    >>> 1 in [0,1,2,3,4]
    True

#### Opérateurs de comparaison

- < : inférieur strictement

- > : supérieur strictement

- <= : inférieur ou égal

- >= : supérieur ou égal

- == : égal

- != : différent

- is : est

- is not : n’est pas

Une comparaison travaille sur deux objets et renvoie un résultat booléen.

Exemple de comparaison de deux strings égales:: Les strings sont immuables, l’affectation mémoire des deux objets est identique:

    >>> a = 'Hello'
    >>> b = 'Hello'
    >>> id(a)
    39756736
    >>> id(b)
    39756736
    >>> a is b
    True

## Les structures conditionnelles

Les structures conditionnelles sont des regroupements de lignes délimitées par un niveau d’indentation et dont le contenu est exécuté en fonction d’une ou plusieurs conditions.

### L’instruction if

Syntaxe:

if condition:

  bloc de lignes

else:

  bloc de lignes

Exemples:

    >>> actors = ['Terry Jones', 'Terry Gilliam', 'Eric Idle']
    >>> if 'Terry Jones' in actors:
        ... print('Terry Jones est dans les acteurs')
    ... else:
    ...     print('Terry Jones n\'est pas dans les acteurs')
    ...
    Terry Jones est dans les acteurs

    >>> if 'Terry Jones' or 'Eric Idle' in actors:
    ...     print('Il y a Terry Jones ou Eric Idle')
    ...
    Il y a Terry Jones ou Eric Idle

    >>> if 'John Clesse' in actors:
    ...     print('Il y a Terry Jones')
    ... elif 'Terry Gilliam' in actors:
    ...     print('Il y a Terry Gilliam')
    ... else:
    ...     print('Il n\'y a ni Terry Gilliam ni Terry Jones')
    ...
    Il y a Terry Gilliam

### L’instruction for..in

Syntaxe:

for variable in sequence:

  bloc de lignes

else:

  bloc de lignes

Exemples:

    >>> for actor in actors:
    ...     print(actor)
    ...
    Terry Jones
    Terry Gilliam
    Eric Idle

    >>> for letter in actors[0]:
    ...     print(letter)
    ... else:
    ...     print('fini')
    ...
    T
    e
    r
    r
    y

    J
    o
    n
    e
    s
    fini

Deux instructions permettent d’influer sur le déroulement de la boucle:

- continue interrompt l’execution de la boucle pour l’élément en cours et passe à l’élement suivant.

- break interrompt l’execution de la boucle et n’exécute pas l’instruction else.

    >>> for letter in actors[0]:
    ...     if letter=='e':
    ...         continue
    ...     print(letter)
    ...
    T
    r
    r
    y

    J
    o
    n
    s

    >>> for actor in actors:
    ...     print(actor)
    ...     if actor == 'Terry Gilliam':
    ...         break
    ...
    Terry Jones
    Terry Gilliam

### L’instruction while

L’instruction while permet d’exécuter un bloc de lignes tant qu’une expression est vérifiée. Si l’expression n’est plus vraie l’instruction estlse est exécutée ci celle ci existe et la boucle s’arrête.

Remarque : Les instructions continue et break peuvent également s’utiliser avec while

Syntaxe:

while condition:

  bloc de lignes

else:

  bloc de lignes

Exemple:

    >>> i = 1
    >>> while i < 100:
    ...    i += i
    ...    print i
    ...
    2
    4
    8
    16
    32
    64
    128

## Les list comprehensions

Les lists comprehensions donnent un moyen concis d’appliquer une fonction sur chaque élément d’une liste afin d’en produire une nouvelle.

Voir http://docs.python.org/tutorial/datastructures.html#list-comprehensions

Exemples:

    >>> nombres = [1,3,8,9,7,32,89,42]
    >>> # retourner sous forme de liste chacun des nombres au carré
    >>> [nombre*nombre for nombre in nombres]
    [1, 9, 64, 81, 49, 1024, 7921, 1764]

    >>> nombres = [1,3,8,9,7,32,89,42]
    >>> # retourner sous forme de liste les nombres pairs
    >>> [nombre for nombre in nombres if nombre%2==0]
    [8, 32, 42]

## Les dict comprehensions

Les dict comprehensions donnent un moyen concis de créer un dictionnaire à partir d’une itération.

Exemples:

    >>> {x: x**2 for x in (2, 4, 6)}
    {2: 4, 4: 16, 6: 36}

## Exceptions

Une exception est lévée lorsque les conditions d’execution du programme ne sont pas respectées.

L’exception la plus emblématique est la division par zéro

    >>> 1/0
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    ZeroDivisionError: integer division or modulo by zero

### try..except..else

Lorsqu’une exception est levée il est possible de la stopper en interceptant l’erreur.

syntaxe

    try:
        bloc de code surveillé
    except:
        bloc de code exécuté si l'exception est levée

    >>> def division(a,b):
    ...     try:
    ...         return a/b
    ...     except:
    ...         return 'infini'
    ...
    >>> division(4,0)
    'infini'
    >>> division('allo',0)
    'infini'

Il est fortement conseillé de spécifier l’exception afin de ne pas masquer silencieusement tous les types d’erreur qui pourrait intervenir.

    >>> def division(a,b):
    ...     try:
    ...         return a/b
    ...     except ZeroDivisionError:
    ...         return 'infini'
    ...
    >>> division(4,0)
    'infini'
    >>> division('allo',0)
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
      File "<stdin>", line 3, in division
    TypeError: unsupported operand type(s) for /: 'str' and 'int'

Il est possible d’associer plusieurs exceptions à la suite.

    >>> def division(a,b):
    ...     try:
    ...         return a/b
    ...     except ZeroDivisionError:
    ...         return 'infini'
    ...     except TypeError:
    ...         return 'veuillez rentrer des nombres'

Le bloc else est exécuté à la fin uniquement si il n’y a aucune erreur.

### try..finally

syntaxe

    try:
        bloc de code surveillé
    finally:
        code toujours exécuté (y compris si raise ou return dans le bloc surveillé).

### quelques exceptions

- AttributeError

- ImportError

- IndentationError

- IndexError

- KeyError

- MemoryError

- SyntaxError

- TypeError

- ZeroDivisionError

### Exercice

Compléter le script « tests_exercices.py » proposé dans l’archive.