Structures de contrôle et fonctions
====================================

- tests

>>> if "a":
...   print "a"
...
a
>>> if "":
...   print "hell"
...
>>> a = 2
>>> if a == 1:
...   print "un"
... else:
...   print "deux"
...
deux
>>>

.. important:: les types de base ont tous une valeur booléenne

- itérations

>>> for i in range(10):
...   print i
...
0
1
2
3
4
5
6
7
8
9
>>>
>>> l = range(10)
>>> l
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

- tant que

>>> i = 10
>>> while i != 0:
...  print i
...  i = i -1
...
10
9
8
7
6
5
4
3
2
1
>>>

fonctions
-----------

>>> def blabla(x):
...   """fonction qui printe"""
...   print x
...
>>>

il n'y a que des fonctions (et pas de procédures):


>>> def ma_func(x):
...   "écrit quelque chose dans un fichier"
...
>>> ma_func("a")
>>> print ma_func("a")
None
>>>

est équivalent à :

>>> def ma_func(x):
...   "écrit quelque chose dans un fichier"
...   return None

- arité d'une fonction:

    - paramètre non nommé
    - paramètre nommé

>>> def ma_fonction(*args, **kwargs):
...   print "arguments : ", str(args)
...   print "argumments nommés", str(kwargs)
...
>>> ma_fonction("toto", "titi", tutu=2, tata=3)
arguments :  ('toto', 'titi')
argumments nommés {'tata': 3, 'tutu': 2}
>>>

- signature d'une fonction : ça peut renvoyer n'importe quoi (tout ce qu'on veut)

:term:`return` ou :term:`yield` ?

.. glossary::

    yield
        permet de renvoyer le résultat d'une fonction en plusieurs étape
        à l'aide d'un générateur

    return
        résultat d'une fonction

>>> def ma_fonction():
...   for i in range(10):
...     yield i
...
>>> for i in ma_fonction():
...   print i
...
0
1
2
3
4
5
6
7
8
9
>>>

- espaces de nommage à l'intérieur d'une fonction :

>>> def toto(x):
...   print vars()
...
>>> toto("sdfsdf")
{'x': 'sdfsdf'}
>>> class A(object):pass
...
>>> a = A()
>>> a.a = "titi"
>>> a.b = "toto"
>>> vars(a)
{'a': 'titi', 'b': 'toto'}

puisque tout est objet en python, ``vars(mon_objet)`` est équivalent à
``mon_objet.__dict__``


- générateurs et compréhension de liste

les compréhensions de listes permettent de générer de nouvelles listes

exemple :

>>> [ 2*i for i in range(10)]
[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]
>>>

>>> [x for x in range(10) if x>5]
[6, 7, 8, 9]
>>>

>>> [(i, j) for i in range(2,5) for j in range (4,8)]
[(2, 4), (2, 5), (2, 6), (2, 7), (3, 4), (3, 5), (3, 6), (3, 7), (4, 4), (4, 5), (4, 6), (4, 7)]
>>>

les expressions générateurs

>>> expr = (2*i for i in range(10))
>>> expr
<generator object <genexpr> at 0x7ff9efa77cd0>
>>> for e in expr:
...   print e
...
0
2
4
6
8
10
12
14
16
18
>>>


le polymorphisme paramétrique
-----------------------------

polymorphisme exemple de contexte :
la fonction print

>>> print 1
1
>>> print "1"
1

.. todo:: `print 1` et `print "1"` renvoient le même résultat. Pourquoi ?

la programmation par exceptions
-------------------------------------

>>> def function_raise(x):
...   if not type(x) == int:
...     raise TypeError("Pas le bon type")
...   else:
...     return x + 1
...
>>> try:
...   e = function_raise("une string")
... except TypeError, e:
...   print e
...
Pas le bon type
>>

.. important:: règle du Samouraï : une fonction doit renvoyer le résultat escompté
    ou bien lever une exception