{"id":1738,"date":"2019-05-03T12:18:17","date_gmt":"2019-05-03T20:18:17","guid":{"rendered":"http:\/\/wonghoi.humgar.com\/blog\/?p=1738"},"modified":"2025-12-31T01:33:33","modified_gmt":"2025-12-31T09:33:33","slug":"matlab-and-python-paths","status":"publish","type":"post","link":"https:\/\/wonghoi.humgar.com\/blog\/2019\/05\/03\/matlab-and-python-paths\/","title":{"rendered":"MATLAB and Python paths"},"content":{"rendered":"\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p><strong>EDIT<\/strong> (2025-12-31): I finally bit the bullet and wrote an OS system call version which is faster. Please see the link to the <a href=\"https:\/\/github.com\/wonghoi\/pysyspath\" data-type=\"link\" data-id=\"https:\/\/github.com\/wonghoi\/pysyspath\">Github page<\/a>. It&#8217;s no longer called <code>qpath<\/code> but I left the code listing in the blog page to illustrate the thought process.<\/p>\n<\/blockquote>\n\n\n\n<p>MATLAB&#8217;s <code data-enlighter-language=\"matlab\" class=\"EnlighterJSRAW\">path()<\/code> is the analog to Python&#8217;s <code data-enlighter-language=\"python\" class=\"EnlighterJSRAW\">sys.path<\/code>. <\/p>\n\n\n\n<p>However, MATLAB&#8217;s path is stored as strings while Python&#8217;s <code data-enlighter-language=\"python\" class=\"EnlighterJSRAW\">sys.path<\/code> is stored as lists (MATLAB&#8217;s analog of cells). The tradeoffs between strings vs list\/cells (hetereogenous wrappers)<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<p>To add paths in MATLAB, use the obviously named function <code class=\"EnlighterJSRAW\" data-enlighter-language=\"matlab\">addpath()<\/code>. Supply the optional&nbsp;<code class=\"EnlighterJSRAW\" data-enlighter-language=\"matlab\">-end<\/code>&nbsp;argument if you don&#8217;t want any potential shadowing (i.e. the folder to import has lower priority if there&#8217;s an existing function with the same name).<\/p>\n\n\n\n<p>I generally avoid <code class=\"EnlighterJSRAW\" data-enlighter-language=\"matlab\">userpath()<\/code> or the graphical tools because the results are sticky (persists between sessions). The best way is to exclusively manage your paths with startup.m so you always know what you are getting. If you want full certainty, you can start with <code class=\"EnlighterJSRAW\" data-enlighter-language=\"matlab\">restoredefaultpath()<\/code> in MATLAB.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<p>Python&#8217;s suggested <a href=\"https:\/\/stackoverflow.com\/questions\/10095037\/why-use-sys-path-appendpath-instead-of-sys-path-insert1-path\">these<\/a> as equivalents of MATLAB&#8217;s <code class=\"EnlighterJSRAW\" data-enlighter-language=\"matlab\">addpath()<\/code>:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">sys.path.insert(0, folder_to_add_to_path)  # Add to the front\nsys.path.append(folder_to_add_to_path)     # Add to the end<\/pre>\n\n\n\n<p>but just like MATLAB&#8217;s <code data-enlighter-language=\"matlab\" class=\"EnlighterJSRAW\">addpath()<\/code> which works with strings only (not <code data-enlighter-language=\"matlab\" class=\"EnlighterJSRAW\">cellstr<\/code>), these Python options do not work correctly with Python <code data-enlighter-language=\"python\" class=\"EnlighterJSRAW\">lists<\/code> as inputs (typically needed for multiple paths) because lists&#8217; methods in <code data-enlighter-language=\"python\" class=\"EnlighterJSRAW\">sys.path<\/code> are as primitive as doing <code data-enlighter-language=\"python\" class=\"EnlighterJSRAW\">[sys.path, new_stuff]<\/code>:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>This means you&#8217;ll end up with list of lists if you supplied Python lists as inputs to the above<br>(MATLAB will throw an exception if you try to feed it with <code data-enlighter-language=\"matlab\" class=\"EnlighterJSRAW\">cellstr<\/code> instead of polluting your path space with garbage)<\/li>\n\n\n\n<li>This also means it doesn&#8217;t check for duplicates! It&#8217;ll keep stacking entries!<\/li>\n<\/ol>\n\n\n\n<p>To address the first problem, we use <code data-enlighter-language=\"python\" class=\"EnlighterJSRAW\">sys.path.extend()<\/code> instead. It&#8217;s like doing <code data-enlighter-language=\"matlab\" class=\"EnlighterJSRAW\">addpath(..., '-end')<\/code> in MATLAB. If you want it to be inserted at the front (higher priority, shadows existing), you&#8217;ll need <code data-enlighter-language=\"python\" class=\"EnlighterJSRAW\">sys.path = list_of_new_paths + sys.path<\/code>. <\/p>\n\n\n\n<p>MATLAB is the other way round: if you have a cell (python&#8217;s version of list) of paths to enter, you can make a path string by using <code data-enlighter-language=\"matlab\" class=\"EnlighterJSRAW\">pathsep<\/code>:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"matlab\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">addpath(strjoin(cellstr_of_paths, pathsep)))<\/pre>\n\n\n\n<p>Note that\u00a0 <code data-enlighter-language=\"python\" class=\"EnlighterJSRAW\">sys.path.extend()<\/code> expect list, sets or tuples so if you feed it a string, which Python will consider it a list of characters, you will get a bunch of one character paths inserted!<\/p>\n\n\n\n<p>On the other hand, DO NOT TRY to get multipe path entries to work with <code>insert<\/code>\/<code>append <\/code>by flattening the list of path into one string becaust <code>sys.path<\/code> is not a string and it&#8217;s not smart enough to auto switch depending on data types! In other words, <code data-enlighter-language=\"python\" class=\"EnlighterJSRAW\">sys.path.append( ';'.join(path_list))<\/code> is INCORRECT as it&#8217;ll add an invalid path when more than 1 path is in the <code>path_list<\/code>. Python recognize <code data-enlighter-language=\"python\" class=\"EnlighterJSRAW\">sys.path<\/code> as a list, NOT a one long string like MATLAB\/Windows <code data-enlighter-language=\"generic\" class=\"EnlighterJSRAW\">path<\/code>, despite <code data-enlighter-language=\"python\" class=\"EnlighterJSRAW\">insert()<\/code> and <code data-enlighter-language=\"python\" class=\"EnlighterJSRAW\">append()<\/code> accepts only strings!<\/p>\n\n\n\n<p>MATLAB&#8217;s uses string, which is a much more efficient path representation, so many duplicate entries won&#8217;t slow down the system much. Path order do affect shadowing (which functions with the same name gets exposed) though.<\/p>\n\n\n\n<p>The second problem (which does NOT happen in MATLAB) is slightly more work. You&#8217;ll need to subtract out the existing paths before you add to it so that you won&#8217;t drag your system down by casually adding paths as you see fit. One way to do it:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">def keep_only_new_set_of_paths(p):\n    return set(p)-set(sys.path)\n<\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<p>You should organize your programs and libraries in a directory tree structure and use code to crawl the right branch into a path list! <strong>Don&#8217;t let the lack of built-in support to tempt you to organize files in a mess<\/strong>. Keep the visuals clean as mental gymnastics\/overheads can seriously distract you from the real work such as thinking through the requirements and coming up with the right architecture and data structures. If you constantly need to jump a few hoops to do something, do it only once or twice using the proper way (aka, NOT copying-and-pasting boilerplate code), and reuse the infrastructure.<\/p>\n\n\n\n<p>At my previous workplaces, they had dozens and dozens of MATLAB files including all laying flat in one folder. The first thing I did when I join a new team is showing everybody this idiom that recursively adds everything under the folder into MATLAB paths:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"matlab\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">addpath(genpath())<\/pre>\n\n\n\n<p>Actually the built-in support for recursive directory search sucks for both MATLAB and Python.\u00a0 Most often what we need is just a list of full paths for a path pattern that we search recursively, basically <code data-enlighter-language=\"msdos\" class=\"EnlighterJSRAW\">dir\/w\/s *.<\/code>\u00a0None of them has this right out of the box. They both make you go through the comprehensive data structure returned (let it be tuples from <code data-enlighter-language=\"python\" class=\"EnlighterJSRAW\">os.walk()<\/code>\u00a0in Python or <code data-enlighter-language=\"matlab\" class=\"EnlighterJSRAW\">dir()<\/code> in MATLAB) and massage the output to a simple path list.<\/p>\n\n\n\n<p><code data-enlighter-language=\"matlab\" class=\"EnlighterJSRAW\">genpath()<\/code> itself is slow and ugly. It&#8217;s basically a recursive wrapper around <code data-enlighter-language=\"matlab\" class=\"EnlighterJSRAW\">dir()<\/code> that cleans up garbage like <code data-enlighter-language=\"generic\" class=\"EnlighterJSRAW\">'.'<\/code> and <code data-enlighter-language=\"generic\" class=\"EnlighterJSRAW\">'..'<\/code>.\u00a0 Instead of getting a newline character, a different row (as a <code data-enlighter-language=\"matlab\" class=\"EnlighterJSRAW\">char<\/code> array) or a different cell (as <code data-enlighter-language=\"matlab\" class=\"EnlighterJSRAW\">cellstr<\/code>), you get semi-colons (<code data-enlighter-language=\"matlab\" class=\"EnlighterJSRAW\">;<\/code>) as <code data-enlighter-language=\"matlab\" class=\"EnlighterJSRAW\">pathsep<\/code> in between. Nonetheless, I still use <code>genpath()<\/code> because despite I have written faster recursive path tools in my own libraries, I&#8217;ll need to load the library first in my startup file, which requires a recursive path tool like <code data-enlighter-language=\"matlab\" class=\"EnlighterJSRAW\">genpath()<\/code>. This bootstraps me out of a chicken-and-egg problem without too much ugly syntax.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<p>Most people will tell you to do a <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">os.walk()<\/code> and use listcomp to get it in the typical full path form, but I&#8217;m not settling for distracting syntax like this. People in the community suggested using <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">glob<\/code> for a relatively simple alternative to <code class=\"EnlighterJSRAW\" data-enlighter-language=\"matlab\">genpath()<\/code><\/p>\n\n\n\n<p>Here&#8217;s a cleaner way:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">def list_subfolders_recursively(p):\n    p = p + '\/**\/' \n    return glob.glob(p, recursive=True);<\/pre>\n\n\n\n<p>It&#8217;s also worth noting that Python follows Linux&#8217;s file search pattern where directory terminates with a filesep (<code class=\"EnlighterJSRAW\" data-enlighter-language=\"matlab\">\/<\/code>) while MATLAB&#8217;s <code class=\"EnlighterJSRAW\" data-enlighter-language=\"matlab\">dir()<\/code> command follows the OS, which in Windows, it&#8217;s <code class=\"EnlighterJSRAW\" data-enlighter-language=\"matlab\">*.<\/code>.<\/p>\n\n\n\n<p>Both MATLAB and Python uses <code data-enlighter-language=\"generic\" class=\"EnlighterJSRAW\">**<\/code> to mean regardless of levels, but you&#8217;ll have to turn on the <code data-enlighter-language=\"oracledb\" class=\"EnlighterJSRAW\">recursive=True<\/code> in <code data-enlighter-language=\"python\" class=\"EnlighterJSRAW\">glob<\/code> manually. <code data-enlighter-language=\"generic\" class=\"EnlighterJSRAW\">**<\/code>\u00a0is already implied to be recursive in MATLAB&#8217;s <code data-enlighter-language=\"matlab\" class=\"EnlighterJSRAW\">dir()<\/code> command.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<p>Considering there&#8217;s quite a bit of plumbing associated with weak set of <code class=\"EnlighterJSRAW\" data-enlighter-language=\"python\">sys.path<\/code> methods provided in Python,&nbsp;I created a <code class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\">qpath.py<\/code> next to my <code class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\">startup.py<\/code>:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">''' This is the quick and dirty version to bootstrap startup.py\nShould use files.py that issue direct OS calls for speed'''\n\nimport sys\nimport glob\n\ndef list_subfolders_recursively(p):\n    p = p + '\/**\/' \n    return glob.glob(p, recursive=True);\n\ndef keep_only_new_set_of_paths(p):\n    return set(p)-set(sys.path)\n\ndef set_of_new_subfolders_recursively(p):\n    return keep_only_new_set_of_paths( list_subfolders_recursively(p) )\n\ndef add_paths_recursively_bottom(p):\n    sys.path.extend(set_of_new_subfolders_recursively(p));\n\ndef add_paths_recursively_top(p):\n    # operator+() does not take sets\n    sys.path = list(set_of_new_subfolders_recursively(p)) + sys.path;<\/pre>\n\n\n\n<p>In order to be able to import my <code class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\">qpath<\/code> module at <code class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\">startup.py<\/code> before it adds the path, I&#8217;ll have put <code class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\">qpath.py<\/code> in the same folder as <code class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\">startup.py<\/code>, and request <code class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\">startup.py<\/code> to add the folder where it lives to the system path (because your current Python working folder might be different from <a href=\"https:\/\/wonghoi.humgar.com\/blog\/2019\/05\/03\/python-startup-management\/\"><code class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\">PYTHONSTARTUP<\/code><\/a>) so it recognizes&nbsp;<code class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\">qpath.py<\/code>.<\/p>\n\n\n\n<p>This is the same technique I came up with for managing <strong><em>localized<\/em><\/strong> dependencies in MATLAB: I put the dependencies under the calling function&#8217;s folder, and use the path of the&nbsp;<code class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\">.m<\/code>&nbsp;file for the function as the anchor-path to add paths inside the function. In MATLAB, it&#8217;s done this way:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"matlab\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">function varargout = f(varargin)\n  anchor_path = fileparts( mfilename('fullpath') );\n  addpath( genpath(fullfile(anchor_path, 'dependencies')) );\n  % Body code goes here<\/pre>\n\n\n\n<p>Analogously,<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Python has <code data-enlighter-language=\"generic\" class=\"EnlighterJSRAW\">__file__<\/code> variable (like the good old preprocessor days in C) in place of <code data-enlighter-language=\"matlab\" class=\"EnlighterJSRAW\">mfilename()<\/code>.<\/li>\n\n\n\n<li>MATLAB&#8217;s&nbsp; <code data-enlighter-language=\"matlab\" class=\"EnlighterJSRAW\">mfilename('fullpath')<\/code>&nbsp;always gives the absolute path, but Python&#8217;s&nbsp;&nbsp;<code data-enlighter-language=\"generic\" class=\"EnlighterJSRAW\">__file__<\/code> is absolute if it&#8217;s is not in <code data-enlighter-language=\"generic\" class=\"EnlighterJSRAW\">sys.path<\/code> yet, and relative if it&#8217;s already in it.<\/li>\n\n\n\n<li>So to ensure absolute path in Python, apply <code data-enlighter-language=\"python\" class=\"EnlighterJSRAW\">os.path.realpath(__file__)<\/code>. Actually this is a difficult feature to implement in MATLAB. It&#8217;s solved by a MATLAB FEX entry called <a href=\"https:\/\/blogs.mathworks.com\/pick\/2011\/04\/01\/be-absolute-about-your-relative-path-with-getfullpath\/\">GetFullPath<\/a>().<\/li>\n\n\n\n<li>Python\u00a0<code data-enlighter-language=\"python\" class=\"EnlighterJSRAW\">os.path.dirname<\/code> is the direct equivalent of <code data-enlighter-language=\"matlab\" class=\"EnlighterJSRAW\">fileparts()<\/code> if you just take the first output argument. <code>os.path.basename<\/code> is the 2nd output argument.<\/li>\n<\/ul>\n\n\n\n<p>and in my <code data-enlighter-language=\"generic\" class=\"EnlighterJSRAW\">startup.py<\/code> (must be in the same folder as <code data-enlighter-language=\"generic\" class=\"EnlighterJSRAW\"><code data-enlighter-language=\"generic\" class=\"EnlighterJSRAW\">qpath.py<\/code><\/code>):<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">import os\nimport sys\n\nsys.path.append(os.path.dirname(os.path.realpath(__file__)))\n\nimport qpath\n\nuser_library_path = 'D:\/Python\/Libraries';\nqpath.add_paths_recursively_bottom(user_library_path)<\/pre>\n\n\n\n<p>This way I can make sure all the paths are deterministic and none of the depends on where I start Python.<\/p>\n\n\n\n<p><\/p>\n<div class=\"pvc_clear\"><\/div><p id=\"pvc_stats_1738\" class=\"pvc_stats all  \" data-element-id=\"1738\" 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>EDIT (2025-12-31): I finally bit the bullet and wrote an OS system call version which is faster. Please see the link to the Github page. It&#8217;s no longer called qpath but I left the code listing in the blog page &hellip; <a href=\"https:\/\/wonghoi.humgar.com\/blog\/2019\/05\/03\/matlab-and-python-paths\/\">Continue reading <span class=\"meta-nav\">&rarr;<\/span><\/a><\/p>\n<div class=\"pvc_clear\"><\/div>\n<p id=\"pvc_stats_1738\" class=\"pvc_stats all  \" data-element-id=\"1738\" 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":[10,6,34],"tags":[],"class_list":["post-1738","post","type-post","status-publish","format-standard","hentry","category-matlab","category-note-to-self","category-python"],"_links":{"self":[{"href":"https:\/\/wonghoi.humgar.com\/blog\/wp-json\/wp\/v2\/posts\/1738","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=1738"}],"version-history":[{"count":38,"href":"https:\/\/wonghoi.humgar.com\/blog\/wp-json\/wp\/v2\/posts\/1738\/revisions"}],"predecessor-version":[{"id":6925,"href":"https:\/\/wonghoi.humgar.com\/blog\/wp-json\/wp\/v2\/posts\/1738\/revisions\/6925"}],"wp:attachment":[{"href":"https:\/\/wonghoi.humgar.com\/blog\/wp-json\/wp\/v2\/media?parent=1738"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/wonghoi.humgar.com\/blog\/wp-json\/wp\/v2\/categories?post=1738"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/wonghoi.humgar.com\/blog\/wp-json\/wp\/v2\/tags?post=1738"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}