{"id":970,"date":"2010-10-25T12:21:09","date_gmt":"2010-10-25T10:21:09","guid":{"rendered":"http:\/\/j-mad.com\/blog\/?p=970"},"modified":"2010-10-25T17:07:44","modified_gmt":"2010-10-25T15:07:44","slug":"monkey-patching-une-petite-astuce","status":"publish","type":"post","link":"https:\/\/j-mad.com\/blog\/2010\/10\/25\/monkey-patching-une-petite-astuce\/","title":{"rendered":"Monkey Patching, une petite astuce"},"content":{"rendered":"<p>Vous connaissez surement cette astuce de dev qu&#8217;est le Monkey Patch (voir <a href=\"http:\/\/fr.wikipedia.org\/wiki\/Monkey-Patch\">article wikipedia<\/a>). En r\u00e9sum\u00e9, \u00e7a permet d&#8217;\u00e9tendre ou de modifier le code sans modification du code original. Bien entendu, on ne peut monkey patcher que des langages dynamiques.<\/p>\n<p>En python, pour monkey patcher une classe, par exemple, on fait\u00a0:<\/p>\n<div class=\"codecolorer-container python default\" style=\"overflow:auto;white-space:nowrap;width:435px;\"><div class=\"python codecolorer\"><span class=\"kw1\">class<\/span> NewKlass\u00a0:<br \/>\n\u2026..<br \/>\n\u2026..<br \/>\n<br \/>\n<span class=\"kw1\">from<\/span> project.<span class=\"me1\">apps<\/span> <span class=\"kw1\">import<\/span> modtest<br \/>\nmodtest.<span class=\"me1\">Klass<\/span> <span class=\"sy0\">=<\/span> NewKlass<\/div><\/div>\n<p>Rien de bien difficile. Mais compliquons un peu les choses. Imaginons que notre classe Klass soit une classe qui nous convient en partie, mais pas compl\u00e9tement. Certaines fonctions nous conviennent parfaitement, mais pas d&#8217;autre. Et pour corser le tout, il faudrait qu&#8217;on rajoute des donn\u00e9es membres \u00e0 la classe. On ne peut donc pas &#8216;juste&#8217; monkey patcher les fonctions qui ne nous plaisent pas&#8230;<\/p>\n<p>Comment faire\u00a0?<\/p>\n<p>Il faudrait pouvoir d\u00e9river de la classe qu&#8217;on veut monkey patcher, avant de la monkey patcher.. Mais ce n&#8217;est surement pas possible non\u00a0? Et ben si. Et c&#8217;est tr\u00e8s facile.<\/p>\n<p>On commence par \u00e9crire notre nouvelle classe\u00a0:<\/p>\n<div class=\"codecolorer-container python default\" style=\"overflow:auto;white-space:nowrap;width:435px;\"><div class=\"python codecolorer\"><span class=\"kw1\">from<\/span> project.<span class=\"me1\">apps<\/span>.<span class=\"me1\">modtest<\/span> <span class=\"kw1\">import<\/span> Klass <span class=\"kw1\">as<\/span> OldKlass<br \/>\n<br \/>\n<span class=\"kw1\">class<\/span> NewKlass<span class=\"br0\">&#40;<\/span>OldKlass<span class=\"br0\">&#41;<\/span>\u00a0:<br \/>\n\u2026.<br \/>\n\u2026.<\/div><\/div>\n<p>et ensuite on monkey patch, comme pr\u00e9c\u00e9demment\u00a0:<\/p>\n<div class=\"codecolorer-container python default\" style=\"overflow:auto;white-space:nowrap;width:435px;\"><div class=\"python codecolorer\"><span class=\"kw1\">from<\/span> project.<span class=\"me1\">apps<\/span> <span class=\"kw1\">import<\/span> modtest<br \/>\nmodtest.<span class=\"me1\">Klass<\/span> <span class=\"sy0\">=<\/span> NewKlass<\/div><\/div>\n<p>Une pr\u00e9cision par contre, lorsque vous allez d\u00e9finir les fonctions dans NewKlass, vous alelz peut \u00eatre vouloir appeler la fonction m\u00e8re de la fonction que vous d\u00e9finissez (par exemple dans les models django, on appelle souvent le save de la fonction m\u00e8re, dans le save de notre fonction)<\/p>\n<p>sauf que l\u00e0, il faut faire attention si vous faites\u00a0:<\/p>\n<div class=\"codecolorer-container python default\" style=\"overflow:auto;white-space:nowrap;width:435px;\"><div class=\"python codecolorer\"><span class=\"kw1\">class<\/span> NewKlass<span class=\"br0\">&#40;<\/span>OldKlass<span class=\"br0\">&#41;<\/span>\u00a0:<br \/>\n\u2026.<br \/>\n\u2026.<br \/>\n<br \/>\n&nbsp; &nbsp; <span class=\"kw1\">def<\/span> save<span class=\"br0\">&#40;<\/span><span class=\"kw2\">self<\/span><span class=\"sy0\">,<\/span> *args<span class=\"sy0\">,<\/span> **kwargs<span class=\"br0\">&#41;<\/span>\u00a0:<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; <span class=\"kw2\">super<\/span><span class=\"br0\">&#40;<\/span> NewKlass<span class=\"sy0\">,<\/span><span class=\"kw2\">self<\/span><span class=\"br0\">&#41;<\/span>.<span class=\"me1\">save<\/span><span class=\"br0\">&#40;<\/span>*args<span class=\"sy0\">,<\/span> **kwargs<span class=\"br0\">&#41;<\/span><\/div><\/div>\n<p>vous avez droit \u00e0 une bonne r\u00e9cursion infinie. Parce que oui, la fonction m\u00e8re de NewKlass c&#8217;est OldKlass, qui est en fait NewKlass, vu qu&#8217;on la monkey-patch\u00e9e.<\/p>\n<p>Il faut donc appeler utiliser la classe grand-m\u00e8re de NewKlass, en faisant un<\/p>\n<div class=\"codecolorer-container python default\" style=\"overflow:auto;white-space:nowrap;width:435px;\"><div class=\"python codecolorer\"><span class=\"kw1\">class<\/span> NewKlass<span class=\"br0\">&#40;<\/span>OldKlass<span class=\"br0\">&#41;<\/span>\u00a0:<br \/>\n\u2026.<br \/>\n\u2026.<br \/>\n<br \/>\n&nbsp; &nbsp; <span class=\"kw1\">def<\/span> save<span class=\"br0\">&#40;<\/span><span class=\"kw2\">self<\/span><span class=\"sy0\">,<\/span> *args<span class=\"sy0\">,<\/span> **kwargs<span class=\"br0\">&#41;<\/span>\u00a0:<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; GrandMereNewKlass.<span class=\"me1\">save<\/span><span class=\"br0\">&#40;<\/span><span class=\"kw2\">self<\/span><span class=\"sy0\">,<\/span>*args<span class=\"sy0\">,<\/span> **kwargs<span class=\"br0\">&#41;<\/span><\/div><\/div>\n<p>Petit Rajout : On me demande qui est GrandMereNewKlass. GrandMereNewKlass est en fait MereOldKlass. ou comme le propose <a href=\"https:\/\/twitter.com\/daks_\/status\/28698216265\">@daks_<\/a> OldKlass.__base__ (D&#8217;ailleurs la question de &#8216;est ce que \u00e7a marche avec de l&#8217;h\u00e9ritage multiple qu&#8217;il pose dans le m\u00eame tweet, reste enti\u00e8re \ud83d\ude42 ).<\/p>\n<p>Bon alors, on sait tous que le monkey Patching, c&#8217;est \u00e0 utiliser avec de grande pr\u00e9caution. Et que bidouiller ainsi en d\u00e9rivant de la classe que l&#8217;on veut monkey patch\u00e9e c&#8217;est tout sauf safe. Mais bon, c&#8217;\u00e9tait la question du jeudi de la semaine derni\u00e8re. &#8216;Peut-on monkey patcher une classe par une de ses classes filles ?&#8217;.<\/p>\n<p>La r\u00e9ponses est donc oui.<\/p>\n<p>Mais ce n&#8217;est pas \u00e0 mettre entre toutes les mains.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Vous connaissez surement cette astuce de dev qu&#8217;est le Monkey Patch (voir article wikipedia). En r\u00e9sum\u00e9, \u00e7a permet d&#8217;\u00e9tendre ou de modifier le code sans modification du code original. Bien entendu, on ne peut monkey patcher que des langages dynamiques. En python, pour monkey patcher une classe, par exemple, on fait\u00a0: class NewKlass\u00a0: \u2026.. \u2026.. &hellip; <a href=\"https:\/\/j-mad.com\/blog\/2010\/10\/25\/monkey-patching-une-petite-astuce\/\" class=\"more-link\">Continue reading <span class=\"screen-reader-text\">Monkey Patching, une petite astuce<\/span><\/a><\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"jetpack_post_was_ever_published":false,"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_memberships_contains_paid_content":false,"footnotes":"","jetpack_publicize_message":"","jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":false,"jetpack_social_options":{"image_generator_settings":{"template":"highway","default_image_id":0,"font":"","enabled":false},"version":2}},"categories":[5],"tags":[98],"class_list":["post-970","post","type-post","status-publish","format-standard","hentry","category-python","tag-python"],"aioseo_notices":[],"jetpack_publicize_connections":[],"jetpack_featured_media_url":"","jetpack_shortlink":"https:\/\/wp.me\/p12cdp-fE","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/j-mad.com\/blog\/wp-json\/wp\/v2\/posts\/970","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/j-mad.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/j-mad.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/j-mad.com\/blog\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/j-mad.com\/blog\/wp-json\/wp\/v2\/comments?post=970"}],"version-history":[{"count":12,"href":"https:\/\/j-mad.com\/blog\/wp-json\/wp\/v2\/posts\/970\/revisions"}],"predecessor-version":[{"id":981,"href":"https:\/\/j-mad.com\/blog\/wp-json\/wp\/v2\/posts\/970\/revisions\/981"}],"wp:attachment":[{"href":"https:\/\/j-mad.com\/blog\/wp-json\/wp\/v2\/media?parent=970"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/j-mad.com\/blog\/wp-json\/wp\/v2\/categories?post=970"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/j-mad.com\/blog\/wp-json\/wp\/v2\/tags?post=970"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}