{"id":5061,"date":"2025-02-26T20:38:43","date_gmt":"2025-02-27T04:38:43","guid":{"rendered":"https:\/\/wonghoi.humgar.com\/blog\/?p=5061"},"modified":"2025-12-01T02:12:43","modified_gmt":"2025-12-01T10:12:43","slug":"neat-trick-cleaning-up-after-yourself-in-python-the-non-pythonic-the-semi-raii-way-oncleanup","status":"publish","type":"post","link":"https:\/\/wonghoi.humgar.com\/blog\/2025\/02\/26\/neat-trick-cleaning-up-after-yourself-in-python-the-non-pythonic-the-semi-raii-way-oncleanup\/","title":{"rendered":"We use ContextManager (&#8220;with &#8230; as&#8221; statement) in Python because Python&#8217;s fundamental language design (garbage collecting objects) broke RAII"},"content":{"rendered":"\n<p>[TLDR] <strong>Python doesn&#8217;t have RAII<\/strong>. C++ and MATLAB allows RAII. You can have a proper RAII only if destructor timing is 100% controllable by the programmer.<\/p>\n\n\n\n<p>Python uses <a href=\"https:\/\/peps.python.org\/pep-0343\/\">Context Manager<\/a> (<code data-enlighter-language=\"python\" class=\"EnlighterJSRAW\">with ... as<\/code> idiom) to address the old issue of opening up a resource handler (say a file or network socket) and automatically close (free) it regardless of whether the program quit abruptly or it gracefully terminates after it&#8217;s done with the resource.<\/p>\n\n\n\n<p>Unlike destructors in C++ and MATLAB, which registers what to do (such as closing the resource) when the program quits or right before the resource (object) is gone, Python&#8217;s Context Manager is basically rehasing the old try-block idea by creating a rigid framework around it.<\/p>\n\n\n\n<p>It&#8217;s not that Python doesn&#8217;t know the RAII mechanism (which is much cleaner), but Python&#8217;s fundamental language design choices drove itself to a corner so it&#8217;s stuck micro-optimizing the <code>try-except\/catch-finally<\/code> approach of managing opened resourecs:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Everything is seen as object in Python. Even integers have a ton of methods.<br>MATLAB and C++ treats POD, Plain Old Data, such as integers separately from classes<br><\/li>\n\n\n\n<li>Python&#8217;s garbage collector controls the timing of when the destructor of any object is called (<code>del<\/code> merely decrement the reference count).<\/li>\n\n\n\n<li>MATLAB&#8217;s <a href=\"https:\/\/web.archive.org\/web\/20111108064710\/http:\/\/www.mathworks.com\/company\/newsletters\/digest\/2008\/sept\/matlab-objects.html\">do not garbage-collect objects<\/a> so the destructor timing is guaranteed.<\/li>\n\n\n\n<li>C++ has no garbage collection so the destructor timing is guaranteed and managed by the programmer.<\/li>\n<\/ul>\n\n\n\n<p>Python cannot easily exclude garbage collecting classes (which breaks RAII) because fundamentally everything are classes (dictionaries potentially with callables) in Python.<\/p>\n\n\n\n<p>This is one of the reasons why I have a lot of respects for MATLAB for giving a lot of consideration for corner cases (like what &#8217;empty&#8217; means) in their language design decisions. Python has many excellent ideas but not enough thoughts was given to how these ideas interact to produce unwanted\/surprising side effects.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<p>Pythons documentation says out loud right what it does: <code>with ... as ...<\/code> is effectively a rigidly defined <code data-enlighter-language=\"python\" class=\"EnlighterJSRAW\">try-except-finally<\/code> block:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"824\" height=\"1024\" src=\"https:\/\/wonghoi.humgar.com\/blog\/wp-content\/uploads\/2023\/08\/image-3-824x1024.png\" alt=\"\" class=\"wp-image-5071\" srcset=\"https:\/\/wonghoi.humgar.com\/blog\/wp-content\/uploads\/2023\/08\/image-3-824x1024.png 824w, https:\/\/wonghoi.humgar.com\/blog\/wp-content\/uploads\/2023\/08\/image-3-241x300.png 241w, https:\/\/wonghoi.humgar.com\/blog\/wp-content\/uploads\/2023\/08\/image-3-768x955.png 768w, https:\/\/wonghoi.humgar.com\/blog\/wp-content\/uploads\/2023\/08\/image-3-1235x1536.png 1235w, https:\/\/wonghoi.humgar.com\/blog\/wp-content\/uploads\/2023\/08\/image-3.png 1376w\" sizes=\"auto, (max-width: 824px) 100vw, 824px\" \/><\/figure>\n\n\n\n<p>Context Manager heavily depends on resource opener function (<code>EXPR<\/code>) to return a constructed class instance that implements <code>__exit__<\/code> and <code>__enter__<\/code>, so if you have a C external library imported to Python, like <code>python-ft4222<\/code>, likely you have to write in your context manager in full when you write your wrapper.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<p>Typically the destructor should check if the resource is already closed first, then close it if it wasn&#8217;t already closed. Take <code data-enlighter-language=\"generic\" class=\"EnlighterJSRAW\">io.IOBase<\/code> as an example:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><a href=\"https:\/\/github.com\/python\/cpython\/blob\/main\/Lib\/_pyio.py\"><img loading=\"lazy\" decoding=\"async\" width=\"1004\" height=\"1024\" src=\"https:\/\/wonghoi.humgar.com\/blog\/wp-content\/uploads\/2023\/08\/image-5-1004x1024.png\" alt=\"\" class=\"wp-image-5125\" srcset=\"https:\/\/wonghoi.humgar.com\/blog\/wp-content\/uploads\/2023\/08\/image-5-1004x1024.png 1004w, https:\/\/wonghoi.humgar.com\/blog\/wp-content\/uploads\/2023\/08\/image-5-294x300.png 294w, https:\/\/wonghoi.humgar.com\/blog\/wp-content\/uploads\/2023\/08\/image-5-768x783.png 768w, https:\/\/wonghoi.humgar.com\/blog\/wp-content\/uploads\/2023\/08\/image-5.png 1077w\" sizes=\"auto, (max-width: 1004px) 100vw, 1004px\" \/><\/a><\/figure>\n\n\n\n<p>However, this is only a convenience when you are at the interpreter and can live with the destructor called with a slight delay. <\/p>\n\n\n\n<p>To make sure your code work reliably without timing bugs, you&#8217;ll need to explicitly close it somewhere other than at a destructor or rely on object lifecycle timing. The destructor can acts as a double guard to close it again if it hasn&#8217;t, but it should not be relied on.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<p>The <code>with ... as<\/code> construct is extremely ugly, but it&#8217;s one of the downsides of Python that cannot be worked around easily. It also makes it difficult for users to retry acquiring a resource because one way or another retrying involves injecting the retry logic in <code>__enter__<\/code>. It&#8217;s not that much typographic savings using <code>with ... as<\/code> over <code>try-except-finally<\/code> block if you don&#8217;t plan to recycle th contextmanager and the cleanup code is a one-liner. <\/p>\n<div class=\"pvc_clear\"><\/div><p id=\"pvc_stats_5061\" class=\"pvc_stats all  \" data-element-id=\"5061\" style=\"\"><i class=\"pvc-stats-icon medium\" aria-hidden=\"true\"><svg aria-hidden=\"true\" focusable=\"false\" data-prefix=\"far\" data-icon=\"chart-bar\" role=\"img\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\" viewBox=\"0 0 512 512\" class=\"svg-inline--fa fa-chart-bar fa-w-16 fa-2x\"><path fill=\"currentColor\" d=\"M396.8 352h22.4c6.4 0 12.8-6.4 12.8-12.8V108.8c0-6.4-6.4-12.8-12.8-12.8h-22.4c-6.4 0-12.8 6.4-12.8 12.8v230.4c0 6.4 6.4 12.8 12.8 12.8zm-192 0h22.4c6.4 0 12.8-6.4 12.8-12.8V140.8c0-6.4-6.4-12.8-12.8-12.8h-22.4c-6.4 0-12.8 6.4-12.8 12.8v198.4c0 6.4 6.4 12.8 12.8 12.8zm96 0h22.4c6.4 0 12.8-6.4 12.8-12.8V204.8c0-6.4-6.4-12.8-12.8-12.8h-22.4c-6.4 0-12.8 6.4-12.8 12.8v134.4c0 6.4 6.4 12.8 12.8 12.8zM496 400H48V80c0-8.84-7.16-16-16-16H16C7.16 64 0 71.16 0 80v336c0 17.67 14.33 32 32 32h464c8.84 0 16-7.16 16-16v-16c0-8.84-7.16-16-16-16zm-387.2-48h22.4c6.4 0 12.8-6.4 12.8-12.8v-70.4c0-6.4-6.4-12.8-12.8-12.8h-22.4c-6.4 0-12.8 6.4-12.8 12.8v70.4c0 6.4 6.4 12.8 12.8 12.8z\" class=\"\"><\/path><\/svg><\/i> <img loading=\"lazy\" decoding=\"async\" width=\"16\" height=\"16\" alt=\"Loading\" src=\"https:\/\/wonghoi.humgar.com\/blog\/wp-content\/plugins\/page-views-count\/ajax-loader-2x.gif\" border=0 \/><\/p><div class=\"pvc_clear\"><\/div>","protected":false},"excerpt":{"rendered":"<p>[TLDR] Python doesn&#8217;t have RAII. C++ and MATLAB allows RAII. You can have a proper RAII only if destructor timing is 100% controllable by the programmer. Python uses Context Manager (with &#8230; as idiom) to address the old issue of &hellip; <a href=\"https:\/\/wonghoi.humgar.com\/blog\/2025\/02\/26\/neat-trick-cleaning-up-after-yourself-in-python-the-non-pythonic-the-semi-raii-way-oncleanup\/\">Continue reading <span class=\"meta-nav\">&rarr;<\/span><\/a><\/p>\n<div class=\"pvc_clear\"><\/div>\n<p id=\"pvc_stats_5061\" class=\"pvc_stats all  \" data-element-id=\"5061\" style=\"\"><i class=\"pvc-stats-icon medium\" aria-hidden=\"true\"><svg aria-hidden=\"true\" focusable=\"false\" data-prefix=\"far\" data-icon=\"chart-bar\" role=\"img\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\" viewBox=\"0 0 512 512\" class=\"svg-inline--fa fa-chart-bar fa-w-16 fa-2x\"><path fill=\"currentColor\" d=\"M396.8 352h22.4c6.4 0 12.8-6.4 12.8-12.8V108.8c0-6.4-6.4-12.8-12.8-12.8h-22.4c-6.4 0-12.8 6.4-12.8 12.8v230.4c0 6.4 6.4 12.8 12.8 12.8zm-192 0h22.4c6.4 0 12.8-6.4 12.8-12.8V140.8c0-6.4-6.4-12.8-12.8-12.8h-22.4c-6.4 0-12.8 6.4-12.8 12.8v198.4c0 6.4 6.4 12.8 12.8 12.8zm96 0h22.4c6.4 0 12.8-6.4 12.8-12.8V204.8c0-6.4-6.4-12.8-12.8-12.8h-22.4c-6.4 0-12.8 6.4-12.8 12.8v134.4c0 6.4 6.4 12.8 12.8 12.8zM496 400H48V80c0-8.84-7.16-16-16-16H16C7.16 64 0 71.16 0 80v336c0 17.67 14.33 32 32 32h464c8.84 0 16-7.16 16-16v-16c0-8.84-7.16-16-16-16zm-387.2-48h22.4c6.4 0 12.8-6.4 12.8-12.8v-70.4c0-6.4-6.4-12.8-12.8-12.8h-22.4c-6.4 0-12.8 6.4-12.8 12.8v70.4c0 6.4 6.4 12.8 12.8 12.8z\" class=\"\"><\/path><\/svg><\/i> <img loading=\"lazy\" decoding=\"async\" width=\"16\" height=\"16\" alt=\"Loading\" src=\"https:\/\/wonghoi.humgar.com\/blog\/wp-content\/plugins\/page-views-count\/ajax-loader-2x.gif\" border=0 \/><\/p>\n<div class=\"pvc_clear\"><\/div>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"inline_featured_image":false,"footnotes":""},"categories":[25,10,34],"tags":[],"class_list":["post-5061","post","type-post","status-publish","format-standard","hentry","category-cpp","category-matlab","category-python"],"_links":{"self":[{"href":"https:\/\/wonghoi.humgar.com\/blog\/wp-json\/wp\/v2\/posts\/5061","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/wonghoi.humgar.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/wonghoi.humgar.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/wonghoi.humgar.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/wonghoi.humgar.com\/blog\/wp-json\/wp\/v2\/comments?post=5061"}],"version-history":[{"count":21,"href":"https:\/\/wonghoi.humgar.com\/blog\/wp-json\/wp\/v2\/posts\/5061\/revisions"}],"predecessor-version":[{"id":6712,"href":"https:\/\/wonghoi.humgar.com\/blog\/wp-json\/wp\/v2\/posts\/5061\/revisions\/6712"}],"wp:attachment":[{"href":"https:\/\/wonghoi.humgar.com\/blog\/wp-json\/wp\/v2\/media?parent=5061"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/wonghoi.humgar.com\/blog\/wp-json\/wp\/v2\/categories?post=5061"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/wonghoi.humgar.com\/blog\/wp-json\/wp\/v2\/tags?post=5061"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}