{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Les fonctions (en Python 3)\n", "___\n", "\n", "Références de cette section :\n", " - [Le cours Moodle sur les fonctions](https://moodle.insa-toulouse.fr/course/view.php?id=1090#section-3)\n", " - [The Python Language Reference -- Function definitions](https://docs.python.org/3/reference/compound_stmts.html#function-definitions)\n", " - [The Python Tutorial -- Functions](https://docs.python.org/3/tutorial/controlflow.html#defining-functions)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Propagande : pourquoi les fonctions sont importantes\n", "\n", "Un point essentiel en informatique (en fait, dans toute discipline scientifique) est la possibilité de raisonner par **abstraction**.\n", "\n", "Dans un langage de programmation, l'abstraction se traduit en premier lieu par la possibilité de définir des **fonctions** qui permettent de répéter plusieurs fois un même algorithme, ou simplement de cacher (on dit \"abstraire\") les détails d'un algorithme.\n", "\n", "Python permet de définir très simplement des fonctions. Mieux : les fonctions sont des objets standards en Python (on dit que ce sont des valeurs de première classe) -- nous en reparlons en cours de programmation fonctionnelle, en 4ème année info.\n", "\n", "D'autres abstractions sont nécessaires pour le développement de logiciels conséquents : la\n", "*généricité*, la *modularité*, l'encapsulation (la possibilité de définir des **types abstraits**), etc. . Pour l'instant (en 2018), Python n'est pas encore à la hauteur sur tous ces points, en particulier l'encapsulation et la définition de types abstraits. Il ne devrait donc pas être utilisé pour du développement logiciel de qualité \"industrielle\". Il est probable toutefois que le langage évolue à l'avenir.\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Définition de fonction, la base\n", "\n", "Un exemple suffit:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%reset -f\n", "\n", "## Carré signé\n", "def carre_signe(x):\n", " \"\"\"\n", " carre_signe(x) renvoie la valeur x^2, mais en préservant le signe de x.\n", " (Je ne garantis pas que cette fonction soit réellement utile.)\n", " \n", " Invariants : | carre_signe(x) | = x^2 \n", " et carre_signe(x) >= 0 <=> x >= 0\n", " \"\"\"\n", " if x >= 0:\n", " return x*x\n", " else:\n", " return -(x*x)\n", " \n", " \n", "print(carre_signe(4))\n", "print(carre_signe(-3))\n", "\n", "## CTRL+ENTER ... ESC+o" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ " - **def** est le mot clef pour définir une fonction\n", " - La chaîne de caractères entre trois guillemets \"\"\" s'appelle une [documentation string](https://docs.python.org/3/tutorial/controlflow.html#documentation-strings). Bien qu'optionnelle, c'est une excellente idée d'introduire la documentation au sein même de la définition de la fonction.\n", " \n", " Pour accéder à la documentation d'une fonction :" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "## À exécuter juste après avoir exécuté le bloc ci-dessus, qui définit carre_signe.\n", "help(carre_signe)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Exercice rapide\n", "\n", " - Facile : obtenez l'aide pour la fonction print() (dans le cadre de code ci-dessous)\n", "\n", " - Moins facile : rappelez-vous de la méthode pop() invoquée sur une liste dans la leçon sur le WHILE. Trouvez comment obtenir l'aide pour cette méthode.\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%reset -f\n", "\n", "## Obtenez l'aide pour la fonction print.\n", "print(\"The cake is a lie.\")\n", "\n", "## Obtenez l'aide pour la méthode pop des listes :\n", "sequence = [1,2,3]\n", "sequence.pop()\n", "\n", "## Si vous obtenez l'aide pour la classe int (les entiers), c'est que vous avez raté quelque chose.\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Fonction qui renvoie None (procédure)\n", "\n", "Une fonction qui ne renvoie rien (comme une procédure en Ada), est en réalité une fonction qui renvoie ```None```.\n", "\n", "Prévoyez ce qu'affiche le code ci-dessous (attention, il faut bien réfléchir). Si vous parvenez à prévoir TOUT ce qui est affiché, vous pouvez vous déclarer Guerrier Dragon du None (et non l'inverse). " ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "## Ces deux fonctions sont équivalentes, à la prononciation près.\n", "\n", "def say_hello():\n", " print(\"Hi.\")\n", " \n", "def say_bonjour():\n", " print(\"Aïl.\")\n", " return None ## Il n'y a en général aucune raison d'écrire return None\n", "\n", "res1 = say_hello()\n", "res2 = say_bonjour()\n", "\n", "print(\"=========\")\n", "\n", "## Ces print devraient vous paraître bizarre.\n", "## Ils servent à afficher la valeur renvoyée par les fonctions.\n", "print(res1)\n", "print(\"--------\")\n", "print(res2)\n", "\n", "print(\"=========\")\n", "\n", "## À votre avis, que renvoie ce test ?\n", "\n", "if say_hello() == say_bonjour():\n", " print(\"Même valeur\")\n", "else:\n", " print(\"Différents.\")\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
À retenir : en Python, une procédure est une simple fonction qui renvoie None (en général implicitement).
\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Petite pause comique\n", "\n", "Il est possible d'annoter les arguments des fonctions avec leur type...\n", "\n", "mais comme le montre l'exemple ci-dessous, cela n'est pas contraignant, donc l'annotation ne vaut pas plus qu'un commentaire." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "def f(x:int,y:int):\n", " print(x,\" et \", y)\n", " \n", "f(10,20)\n", "\n", "## Sérieusement, vous pensez que ça devrait passer ?\n", "f(\"ok\",\"ba\")\n", "\n", "## Pire : l'annotation n'a vraiment aucun sens. Ce n'est même pas nécessairement un type.\n", "def f(x:-12,y:[10,20,30]):\n", " print(x, \" et\", y)\n", " \n", "f(0,1)\n", "\n", "## En réalité, les annotations ont vocation à être exploitées par des outils complémentaires (comme mypy).\n", "## L'interpréteur Python, lui, les ignore royalement.\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Fonctions imbriquées\n", "\n", "Les fonctions sont faciles à définir en Python, donc il ne faut pas se restreindre.\n", "\n", "Elles sont parfois définies à l'intérieur d'une autre fonction lorsqu'elles ne jouent qu'un rôle local.\n", "\n", "Complétez le code ci-dessous avec une fonction interne pour répondre à la spécification indiquée.\n", "\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "def encoder(chaine):\n", " \"\"\"\n", " Transforme la chaine de caractères en remplaçant chaque voyelle a,e,i,o,u par e,i,o,u,a (respectivement).\n", " On ne s'intéresse qu'aux voyelles en minuscules.\n", " Par exemple, encoder(\"banane\") = \"beneni\"\n", " \"\"\"\n", " \n", " ###########################################################################################\n", " ## Définissez ici une fonction interne 'encoder_char' qui transforme un (seul) caractère en un autre.\n", " ## Les voyelles sont transformées comme indiqué ci-dessus, les autres caractères sont inchangés.\n", " ## Vous pouvez écrire la fonction avec des IFs, ou avec un dictionnaire.\n", " ## encoder_char('a') = 'e'\n", " ## encoder_char('u') = 'a'\n", " ## encoder_char('z') = 'z'\n", " ##\n", " ## À vous :\n", " \n", " ## ...\n", " ##########################################################################################\n", " \n", " \n", " ##########################################################################################\n", " ## Corps de la fonction encoder.\n", " ## Pour l'instant, on \"recopie\" juste la chaîne caractère par caractère. À vous de corriger.\n", " resultat = \"\"\n", " \n", " for char in chaine:\n", " resultat += char\n", " \n", " return resultat\n", "\n", "## Tests\n", "print( encoder(\"Gollum fait du Python a' l'insa et il aime bien ça.\") )\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Portée des variables (contextes)\n", "\n", "Reprenez la video Moodle sur les contextes des variables dans les fonctions.\n", "\n", "
En Python, chaque variable **lue** est recherchée dans le contexte le plus local, puis dans chaque contexte englobant (par ordre d'inclusion), jusqu'au contexte le plus grand : le contexte global.
\n", "\n", "Une leçon à venir permettra de mieux comprendre la sémantique (parfois un peu douteuse) des variables en Python.\n", "\n", "### Exercice : contexte des variables\n", "\n", "L'exercice ci-après sert à vous faire réfléchir à la portée des variables.\n", "\n", "Exécutez le code tel quel. Il affiche 4891 (comprenez comment).\n", "\n", "Vous avez le droit de commenter seulement QUATRE lignes (en insérant un # au début de la ligne).\n", "Le programme doit alors aficher 1984.\n", "\n", "Vous ne devez pas modifier le programme autrement qu'en commentant quatre lignes.\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%reset -f\n", "\n", "a = 1000\n", "b = 100\n", "c = 100\n", "d = 1\n", "\n", "def foo(b):\n", " \n", " c = 1\n", " \n", " def moo():\n", " a = 1\n", " c = 10\n", " \n", " def zoo(a):\n", " a = 1\n", " \n", " def bar(c):\n", " print(a*1 + b*9 + c*8 + d*4) ## On veut \"1984\"\n", " \n", " c = 10\n", " a = 1\n", " moo()\n", " zoo(a)\n", " bar(10 * c)\n", "\n", "d = 1000\n", "b = 10\n", "c = 10\n", "foo(c)\n", "\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Exercice : petites fonctions pour le projet perceptron\n", "\n", "Il existe une fonction random.random() qui renvoie un float dans l'intervalle $[0, 1[$.\n", "\n", "### random_within\n", "\n", "En se servant de random.random, écrivez une fonction ```random_within``` telle que, pour tous floats $a$ et $b$ avec $b > a$, ```random_within(a,b)``` renvoie un float dans l'intervalle $[a,b[$.\n", "\n", "\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%reset -f\n", "\n", "## La fonction random.random() se trouve dans numpy.\n", "from numpy import random\n", "\n", "## À vous de définir random_within\n", "\n", "\n", "## Testez." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### random_list\n", "\n", "Écrivez maintenant une fonction ```random_list``` telle que, pour tous entier $n \\geqslant 0$ et floats $a$ et $b$ avec $b > a$, ```random_list(n,a,b)``` renvoie une liste de $n$ nombres aléatoires dans l'intervalle $[a,b[$.\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "## À vous de définir random_list\n", "\n", "## Testez." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### random_matrix\n", "\n", "Et pour finir, écrivez une fonction ```random_matrix``` telle que, pour tous entiers $n \\geqslant 0, m \\geqslant 0$, et floats $a$ et $b$ avec $b > a$, ```random_matrix(n,m,a,b)``` renvoie une matrice (une liste de listes) de dimensions $n \\times m$ contenant des nombres aléatoires dans l'intervalle $[a, b[$.\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "## À vous de définir random_matrix\n", "\n", "## Testez !" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.5.2" } }, "nbformat": 4, "nbformat_minor": 2 }