{"id":1286,"date":"2012-09-19T14:55:44","date_gmt":"2012-09-19T12:55:44","guid":{"rendered":"http:\/\/linux.leunen.com\/?p=1286"},"modified":"2012-09-19T14:55:44","modified_gmt":"2012-09-19T12:55:44","slug":"python-les-iterateurs-et-generateurs","status":"publish","type":"post","link":"https:\/\/www.leunen.com\/linux\/2012\/09\/python-les-iterateurs-et-generateurs\/","title":{"rendered":"Python: les it\u00e9rateurs et g\u00e9n\u00e9rateurs"},"content":{"rendered":"<p>Pour une petite application que j&rsquo;avais d\u00e9cid\u00e9 d&rsquo;\u00e9crire en Python, j&rsquo;avais besoin de parcourir des cha\u00eenes de caract\u00e8res en prenant les caract\u00e8res 3 par 3. En tant qu&rsquo;amateur de C++, ma premi\u00e8re id\u00e9e \u00e9tait forc\u00e9ment de cr\u00e9er une boucle <em>for<\/em> du genre de celle qu&rsquo;on \u00e9crit en C++:<\/p>\n<pre class=\"codesource\">\r\nstd::string my_string(<span class=\"quotedstring\">\"azertyuiop\"<\/span>);\r\n<span class=\"reservedname\">for<\/span>(<span class=\"reservedname\">unsigned<\/span> <span class=\"reservedname\">int<\/span> i  = 0; i &lt; my_string.length(); i+=3)\r\n{\r\n  std::cout &lt;&lt; my_string.substr(i, 3) &lt;&lt; std::endl;\r\n}\r\n<\/pre>\n<p>Cette fa\u00e7on d&rsquo;\u00e9crire une boucle n&rsquo;est pas tr\u00e8s compliqu\u00e9e \u00e0 transcrire en Python. On peut utiliser la fonction <em>range()<\/em> \u00e0 laquelle on peut passer des param\u00e8tres dont le d\u00e9but, la fin et le &lsquo;pas&rsquo; (step) de la boucle. Cela donne quelque chose comme:<\/p>\n<pre class=\"codesource\">\r\nmy_string = <span class=\"quotedstring\">\"azertyuiop\"<\/span>\r\n<span class=\"reservedname\">for<\/span> i <span class=\"reservedname\">in<\/span> range(0, len(my_string), 3):\r\n  <span class=\"reservedname\">print<\/span> i, t[i:i + 3]\r\n<\/pre>\n<p>C&rsquo;est consid\u00e9r\u00e9 comme une m\u00e9thode pas tr\u00e8s pythonique. On pr\u00e9f\u00e9rera utiliser un <em>it\u00e9rateur<\/em> ou un <em>g\u00e9n\u00e9rateur<\/em>.<br \/>\nVoyons bri\u00e8vement et basiquement de quoi il s&rsquo;agit.<\/p>\n<h3>Les it\u00e9rateurs<\/h3>\n<p>Les <em>it\u00e9rateurs<\/em> sont des objets permettant de parcourir facilement des containers que ce soient des strings &#8211; qui ne sont que des containers de caract\u00e8res &#8211; des listes ou des containers d&rsquo;objets. Par exemple, pour r\u00e9cup\u00e9rer tous les caract\u00e8res constituant une cha\u00eene de caract\u00e8res, on proc\u00e8de comme ceci:<\/p>\n<pre class=\"codesource\">\r\nfor c in my_string:\r\n  print c\r\n<\/pre>\n<p>C&rsquo;est tr\u00e8s simple \u00e0 utiliser.<\/p>\n<p>Il est tr\u00e8s facile aussi de cr\u00e9er soi-m\u00eame un <em>it\u00e9rateur<\/em> de fa\u00e7on \u00e0 ce qu&rsquo;il se plie \u00e0 ce que l&rsquo;on veut faire. Pour cela, il suffit de cr\u00e9er un objet qui contient deux m\u00e9thodes: __iter__() et next().<br \/>\n<em>__iter__()<\/em> est appel\u00e9 implicitement au d\u00e9but de la boucle et doit renvoyer un objet it\u00e9rateur.<br \/>\n<em>next()<\/em> doit lever une exception <em>StopIteration<\/em> quand on est \u00e0 la fin de la boucle et qu&rsquo;il n&rsquo;y a plus d&rsquo;\u00e9l\u00e9ments \u00e0 traiter. <\/p>\n<p>Dans le cas d&rsquo;un <em>it\u00e9rateur<\/em> qui renvoie les caract\u00e8res trois par trois, <em>l&rsquo;it\u00e9rateur<\/em> ressemblerait \u00e0 ceci:<\/p>\n<pre class=\"codesource\">\r\n<span class=\"reservedname\">class<\/span> triplet:\r\n  <span class=\"reservedname\">def<\/span> __init__(self, data):\r\n    self.data = data\r\n    self.count = 0\r\n    self.length = len(data)\r\n  \r\n  <span class=\"reservedname\">def<\/span> __iter__(self):\r\n    <span class=\"reservedname\">return<\/span> self\r\n    \r\n  <span class=\"reservedname\">def<\/span> next(self):\r\n    <span class=\"reservedname\">if<\/span> self.count &gt; len(self.length):\r\n      <span class=\"reservedname\">raise<\/span> StopIteration\r\n    <span class=\"reservedname\">else<\/span>:\r\n      result = self.data[self.count:self.count+3]\r\n      self.count += 3\r\n      <span class=\"reservedname\">return<\/span> result\r\n<\/pre>\n<p>Et on peut l&rsquo;utiliser dans une boucle de cette fa\u00e7on:<\/p>\n<pre class=\"codesource\">\r\ntest = triplet(<span class=\"quotedstring\">\"azertyuiop\"<\/span>)\r\n     <span class=\"reservedname\">for<\/span> item <span class=\"reservedname\">in<\/span> test:\r\n       <span class=\"reservedname\">print<\/span> item\r\n<\/pre>\n<h3>Les g\u00e9n\u00e9rateurs<\/h3>\n<p>Cette m\u00e9thode utilisant un objet <em>it\u00e9rateur<\/em> me pla\u00eet bien. C&rsquo;est clair, efficace et tr\u00e8s OOP.<br \/>\nIl existe une autre m\u00e9thode qui est tr\u00e8s simple et qui utilise les principes de l&rsquo;<em>it\u00e9rateur<\/em> m\u00eame si c&rsquo;est bien cach\u00e9. Il s&rsquo;agit des fonctions <em>g\u00e9n\u00e9rateurs<\/em>. Il s&rsquo;agit en fait d&rsquo;une fonction qui retourne un <em>it\u00e9rateur<\/em> qui, lui-m\u00eame, peut \u00eatre utilis\u00e9 pour contr\u00f4ler une boucle par exemple.<br \/>\nLe simple fait d&rsquo;utiliser le mot-clef <em>yield<\/em> fait de la fonction une fonction <em>g\u00e9n\u00e9rateur<\/em>. Lorsque ce mot clef est rencontr\u00e9, il retourne la valeur en argument et l&rsquo;execution est suspendue jusqu&rsquo;\u00e0 la prochaine it\u00e9ration. <\/p>\n<p>Dans notre cas, cela donne ceci:<\/p>\n<pre class=\"codesource\">\r\n<span class=\"reservedname\">def<\/span> triplet(str):\r\n  current=0\r\n  <span class=\"reservedname\">while<\/span> current &lt;= len(str):\r\n    yield str[current:current+3]\r\n    current += 3\r\n<\/pre>\n<p>Et on l&rsquo;utilise dans une boucle comme d&rsquo;habitude:<\/p>\n<pre class=\"codesource\">\r\n<span class=\"reservedname\">for<\/span> t <span class=\"reservedname\">in<\/span> triplet(<span class=\"quotedstring\">\"azertyuiop\"<\/span>):\r\n  <span class=\"reservedname\">print<\/span> t\r\n<\/pre>\n<p>Comme on l&rsquo;a vu bri\u00e8vement ici, les <em>it\u00e9rateurs<\/em> et les <em>g\u00e9n\u00e9rateurs<\/em> sont tr\u00e8s simple d&#8217;emploi et on les retrouve tr\u00e8s fr\u00e9quemment dans les codes \u00e9crits en Python. Je vous renvoie \u00e0 la documentation si vous voulez en savoir plus.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Pour une petite application que j&rsquo;avais d\u00e9cid\u00e9 d&rsquo;\u00e9crire en Python, j&rsquo;avais besoin de parcourir des cha\u00eenes de caract\u00e8res en prenant les caract\u00e8res 3 par 3. En tant qu&rsquo;amateur de C++, ma premi\u00e8re id\u00e9e \u00e9tait forc\u00e9ment de cr\u00e9er une boucle for du genre de celle qu&rsquo;on \u00e9crit en C++: std::string my_string(\u00ab\u00a0azertyuiop\u00a0\u00bb); for(unsigned int i = 0; [&hellip;]<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":[],"categories":[15],"tags":[],"aioseo_notices":[],"_links":{"self":[{"href":"https:\/\/www.leunen.com\/linux\/wp-json\/wp\/v2\/posts\/1286"}],"collection":[{"href":"https:\/\/www.leunen.com\/linux\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.leunen.com\/linux\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.leunen.com\/linux\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/www.leunen.com\/linux\/wp-json\/wp\/v2\/comments?post=1286"}],"version-history":[{"count":7,"href":"https:\/\/www.leunen.com\/linux\/wp-json\/wp\/v2\/posts\/1286\/revisions"}],"predecessor-version":[{"id":1293,"href":"https:\/\/www.leunen.com\/linux\/wp-json\/wp\/v2\/posts\/1286\/revisions\/1293"}],"wp:attachment":[{"href":"https:\/\/www.leunen.com\/linux\/wp-json\/wp\/v2\/media?parent=1286"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.leunen.com\/linux\/wp-json\/wp\/v2\/categories?post=1286"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.leunen.com\/linux\/wp-json\/wp\/v2\/tags?post=1286"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}