{"id":6339,"date":"2025-04-21T16:31:27","date_gmt":"2025-04-22T00:31:27","guid":{"rendered":"https:\/\/wonghoi.humgar.com\/blog\/?p=6339"},"modified":"2025-11-30T22:46:53","modified_gmt":"2025-12-01T06:46:53","slug":"matlab-features-conveniences-not-available-in-python-c","status":"publish","type":"post","link":"https:\/\/wonghoi.humgar.com\/blog\/2025\/04\/21\/matlab-features-conveniences-not-available-in-python-c\/","title":{"rendered":"MATLAB features\/conveniences not available in Python\/C++"},"content":{"rendered":"\n<p>A lot of MATLAB&#8217;s conveniences over Python and vice versa stem from their design choices of what are first class citizens and what are afterthoughts. <\/p>\n\n\n\n<p>MATLAB has its roots from scientific computing, so operations (use cases) that are natural to scientists come first. Python is a great general purpose language but ultimately the motivation came from a computer science point of view. It will eventually get clumsy when Python tries to do everything MATLAB does.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Matrix (a stronger form of array) takes the center stage in MATLAB<\/h2>\n\n\n\n<p>In MATLAB, matrix is a first-class citizen. Unlike other languages that starts with singleton and containers are built to extend it to arrays (then to matrices), nearly everything is assumed to be a matrix in MATLAB so a singleton simply seen as a 1&#215;1 matrix. <\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><a href=\"https:\/\/www.mathworks.com\/company\/technical-articles\/inside-matlab-objects-in-r2008a.html\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"255\" src=\"https:\/\/wonghoi.humgar.com\/blog\/wp-content\/uploads\/2025\/04\/image-1024x255.png\" alt=\"\" class=\"wp-image-6340\" srcset=\"https:\/\/wonghoi.humgar.com\/blog\/wp-content\/uploads\/2025\/04\/image-1024x255.png 1024w, https:\/\/wonghoi.humgar.com\/blog\/wp-content\/uploads\/2025\/04\/image-300x75.png 300w, https:\/\/wonghoi.humgar.com\/blog\/wp-content\/uploads\/2025\/04\/image-768x191.png 768w, https:\/\/wonghoi.humgar.com\/blog\/wp-content\/uploads\/2025\/04\/image-500x125.png 500w, https:\/\/wonghoi.humgar.com\/blog\/wp-content\/uploads\/2025\/04\/image.png 1448w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/a><\/figure>\n\n\n\n<p>This is a huge reason why I love MATLAB over Python based on the language design. <\/p>\n\n\n\n<p>Lists in Pythons are cell containers in MATLAB, so a list of numbers in Python is not the same as an array of doubles in MATLAB because the contents of [1, 2, 3.14] must the same type in MATLAB. <\/p>\n\n\n\n<p>Non-uniform arrays like cells\/lists are much slower because algorithms cannot take advantage of uniform data structure packed very locally (the underlying contents right next to each other) and do not need extra logic to make sure different types are handled correctly.<\/p>\n\n\n\n<p><code>np.array()<\/code> is an after thought so the syntax specifying a matrix is clumsy! The syntax is built on lists of lists (composition like arr[r][c] in C\/C++). There used to be a way to use MATLAB&#8217;s syntax of separating rows of a matrix with a semicolon &#8216;;&#8217; with <code>np.matrix('')<\/code> through a string (which clearly is not native and code transparent). <\/p>\n\n\n\n<p>Given that <code>np.matrix<\/code> is deprecated, this option is out of window. One might think <code>np.array<\/code> would take similar syntax, but heck no! If you typed a string of MATLAB style matrix syntax, <code>np.array<\/code> will treat it as if you entered an arbitrary (Unicode) string, which is a scalar (<code>0<\/code> dimension).<\/p>\n\n\n\n<p>For my own use, I extracted the routine <code>numpy.matrix<\/code> used to parse MATLAB style 2D matrix definition strings as a free function. But the effort wasted to get over these drivels are the hidden costs of using Python over MATLAB.<\/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 ast\n\n''' \nmat2list() below is _convert_from_string() copied right off\nhttps:\/\/github.com\/numpy\/numpy\/blob\/main\/numpy\/matrixlib\/defmatrix.py\nbecause Numpy decided to phase out np.matrix yet choose not to\ntransplant this important convenience feature to ndarray\n'''\n\ndef mat2list(data):\n    for char in '[]':\n        data = data.replace(char, '')\n\n    rows = data.split(';')\n    newdata = []\n    for count, row in enumerate(rows):\n        trow = row.split(',')\n        newrow = []\n        for col in trow:\n            temp = col.split()\n            newrow.extend(map(ast.literal_eval, temp))\n        if count == 0:\n            Ncols = len(newrow)\n        elif len(newrow) != Ncols:\n            raise ValueError(\"Rows not the same size.\")\n        newdata.append(newrow)\n    return newdata<\/pre>\n\n\n\n<p>Numpy array is really cornering users to keep track of the dimensions by providing at least 2 pairs of brackets for matrices! No brackets = singleton, 1 pair of brackets <code>[...]<\/code> = array (1D), 2 pairs\/levels of brackets <code>[ [row1], [row2], ... [rowN] ]<\/code> = matrix (2D). Python earns an expletive from me each time when I type in a matrix!<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Slices are not first class citizens in Python<\/h2>\n\n\n\n<p>Slices in Python are roughly equivalent to <code>colon<\/code> operator in MATLAB. <\/p>\n\n\n\n<p>However, in MATLAB, the <code>colon<\/code> operator is native down to the core so you can create a row matrix of equally spaced numbers without surrounding context. <code>end<\/code> keyword, as a shortcut to get the length (which happen to be the last index due to 1-based indexing) of the dimension when indexing, obviously do not make sense (and therefore invalid) for colon in free form.<\/p>\n\n\n\n<p>Python on the other hand, uses slice object for indexing. Slice object can be instantiated anywhere (free form) but buidling it from the colon syntax is exclusively handled inside the square brakcet <code>[]<\/code> acess operator known as the <code>__getitem__<\/code> dunder method. <code>Slice <\/code>objects are simpler than <code>range <\/code>as it&#8217;s not iterable so it&#8217;s not useful to generate a list of numbers like <code>colon<\/code> operator in MATLAB. In other words, Python reserved the colon syntax yet does not have the convenience of generating equally spaced numbers like MATLAB does. Yuck!<\/p>\n\n\n\n<p>Since everything is a matrix (<code>2<\/code> dimensions or more) in MATLAB, there&#8217;s no such thing as <code>0<\/code> dimension (scalar) and <code>1<\/code> dimension (array\/vector) as in Numpy\/Python.<\/p>\n\n\n\n<p>Transposes in Python makes no sense for 1D-arrays so it&#8217;s a nop. A 1D-array is promoted into a row vector when interacting with 2D arrays \/ matrices), while slices makes no sense with singletons.<\/p>\n\n\n\n<p>Because of this, you don&#8217;t get to just say <code>3:6<\/code> in Python and get <code>[3,4,5]<\/code> (which in MATLAB it&#8217;s really <code>{3,4,5}<\/code> because lists in Python are heterogeneous containers like cells. The <code>3:5<\/code> in MATLAB gives out a genuine matrix like those used in numpy). <\/p>\n\n\n\n<p>You will have to cast <code>range(3,6)<\/code>, which is an iterator, into a list, aka <code>list(range(3,6))<\/code> if the function you call with it does not recognize iterators but instead want a generated list stored in memory. <\/p>\n\n\n\n<p>This is one of the big conveniences (compact syntax) that are lost with Python.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">More Operator Overloading<\/h2>\n\n\n\n<p>Transposes in Numpy is an example where CS people don&#8217;t get exposed to scientific computing enough to know which use case is more common:<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table><tbody><tr><td>MATLAB<\/td><td>Numpy<\/td><td>Meaning<\/td><\/tr><tr><td><code>a.'<\/code><\/td><td><code>a.transpose()<\/code>&nbsp;or&nbsp;<code>a.T<\/code><\/td><td>transpose of&nbsp;<code>a<\/code><\/td><\/tr><tr><td><code>a'<\/code><\/td><td><code>a.conj().transpose()<\/code>&nbsp;or&nbsp;<code>a.conj().T<\/code><\/td><td>conjugate transpose (Hermitian) of&nbsp;<code>a<\/code><\/td><\/tr><\/tbody><\/table><figcaption class=\"wp-element-caption\"><a href=\"https:\/\/numpy.org\/devdocs\/user\/numpy-for-matlab-users.html\">https:\/\/numpy.org\/devdocs\/user\/numpy-for-matlab-users.html<\/a><\/figcaption><\/figure>\n\n\n\n<p>Complex numbers are often not CS people&#8217;s strong suit. Whenever we do a &#8216;transpose&#8217; with a physical meaning or context attached to it, we almost always mean Hermitian (conjugate transpose)! Most often the matrix is real anyway so many of us got lazy and call simply call it it transpose (a special case), so it&#8217;s easy to overlook this if one design\/implement do not have a lot of firsthand experience with complex matrices in your math.<\/p>\n\n\n\n<p>MATLAB is not cheap on symbols and overloaded an operator for transposes, with the shorter version being the most frequent use case (Hermitian). In Python you are stuck with calling methods instead of typing these commonly used operators in scientific computing like they are equations.<\/p>\n\n\n\n<p>At least Python can do better by implementing a <code>a.hermitian()<\/code> and <code>a.H<\/code> method. But judging that the foresight isn&#8217;t there, the community that developed it are likely not the kind of people sophisticated enough in complex numbers to call conjugate transposes Hermitians.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Conventions that are more natural to scientific computing than programming<\/h2>\n\n\n\n<p>Slices notation in Python put the <code>step<\/code> size as the last (3rd) parameter, which makes perfect sense from the eyes of a programmer because it&#8217;s messy to have the second parameter mean step or end point depending on whether there&#8217;s one colon or two. By placing the step parameter consistently as the 3rd argument, the optional case is easier to program.<\/p>\n\n\n\n<p>To people who think in math, it&#8217;s more intuitive when you specify a slice\/range in the order you draw the dots on a numbered line: you start with a starting point, then you&#8217;ll need the step-size to move onto the next point, then you&#8217;ll need to know when to stop. This is why it&#8217;s <code>start:step:stop<\/code> in MATLAB.<\/p>\n\n\n\n<p>Python&#8217;s slice <code>start:stop_exclusive:step<\/code> convention reads like &#8220;let&#8217;s draw a line with a starting point and end points, then we figure out what points to put in between&#8221;. It&#8217;s usually mildly unpleasant to people who parse what they read on the fly (not buffering until the whole sentence is complete) because a 180 degree turn (meaning reversed) can appear at the end of a sentence (which happens a lot with Japanese or Reverse-Polish-Notation).<\/p>\n\n\n\n<p>Be careful that the end points in Python&#8217;s slice and C++&#8217;s STL <code>.end()<\/code> are exclusive (open), which means the exact endpoint is not included. <code>0<\/code>-based index systems (Python an C++) love to specify &#8220;one-past-last&#8221; instead of the included end points because it happens to align with the total count <code>N<\/code>: there are <code>N<\/code> points from <code>[0, N-1]<\/code> (note <code>N-1<\/code> is <strong>in<\/strong>clusive, or a <strong>closed<\/strong> end) which is equivalent to <code>[0, N)<\/code>, where <code>N<\/code> is an open end, for integers. This half-open (or open-end) convention avoids painfully typing <code>-1<\/code> all over the place in most use cases in a <code>0<\/code>-based indexing system.<\/p>\n\n\n\n<p>0-based indexing is convenient when doing modulo (which is more common with programmers) while 1-based indexing matches our intuition of natural numbers (which starts from 1, bite me. lol) so when we count to 5, there are 5 items total. My oscilloscope don&#8217;t call the first channel Channel 0 and I work with floats more than I work with modulo, so 1-based indexing has a slight edge for my use cases.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">MATLAB autoextends when assigning index out of range, not Python<\/h2>\n\n\n\n<p>This is one behavior I really hated Python for it, with passion. Enough for me to keep MATLAB in parallel with Python instead of ditching MATLAB completely.<\/p>\n\n\n\n<p>In MATLAB, I simply assign the result <code>x{3} = 4<\/code> even when the list <code>x<\/code> starts with an empty cell <code>x={}<\/code> and MATLAB will be smart enough to autoextend the list. Python will give you a nasty <code>IndexError: list assignment index out of range<\/code>.<\/p>\n\n\n\n<p>I pretty much have to preallocate my list with <code>[None] * target_list_size<\/code>. MATLAB are pretty tight-assed when it comes to not allowing syntax\/behaviors that allow users to hurt themselves in insidious ways, yet they figured if you expanded a matrix that you didn&#8217;t intend to, soon you&#8217;ll find out when the dimensions mismatch.<\/p>\n\n\n\n<p>Note that numpy array has the same behavior (refuses to autoexpand array when assigned an index out of the current range).<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">No consistent interface for concatenation in Python<\/h2>\n\n\n\n<p>In MATLAB, if you have a cell of tables <code>C<\/code>, you can simply vertically concatenate them simply with <code>vertcat(C{:})<\/code>, because MATLAB has a consistent interface for vertical concatenation, which is what the operator <code>[;]<\/code> calls. <\/p>\n\n\n\n<p>Note that cell unpack {:} in MATLAB translate to comma separated list, putting a square bracket over commas like as [C{:}] is <code>horzcat(C{:})<\/code> because it&#8217;s <code>[,]<\/code>. <\/p>\n\n\n\n<p>Python doesn&#8217;t have such consistent interface. Lists are concatenated by <code>+<\/code> operator while Dataframes are concatenated by <code>pd.concat(list_or_tuples_of_dataframes, ...)<\/code>, as <code>+<\/code> in Dataframes means <strong>elementwise<\/strong> application of <code>+<\/code> (whatever <code>+<\/code> means to the pair of elements involved). <\/p>\n\n\n\n<p>I just had a simple use case where I have a <strong>list containing dataframes<\/strong> corresponding to tests on each channel (index) that I&#8217;ll run the experiments on one by one. They don&#8217;t need to be run in order nor all of the tests need to be completed before I collect (vertically stack) the dataframes into one big dataframe!<\/p>\n\n\n\n<p>Vertically stacking such a list of Dataframes is a nightmare. Pandas haphazardly added a check that throws an error for <code>pandas.concat()<\/code> if everything in the list is <code>None<\/code>, which throws an error &#8220;<code>ValueError: All objects passed were None<\/code>&#8220;!<\/p>\n\n\n\n<p>If I haven&#8217;t run any tests yet, attempting to collect an aggregrate table from all empty tables should return None instead of throwing a <code>ValuerError<\/code>! Checks for attempting to do a <code>nop<\/code> depending on the source data should be left for users! It&#8217;s safer to do less and let users expand on it than nannying and have users painfully undo an unwanted goodwill!<\/p>\n\n\n\n<p>How empties are handled in each data type like <code>cell<\/code> or <code>table()<\/code> is a important part for a consistent generic interface to make sure different data type work together (cast or overload automatically) seamlessly. TMW support showed me a very <a href=\"https:\/\/www.mathworks.com\/help\/matlab\/math\/empty-arrays.html\" data-type=\"link\" data-id=\"https:\/\/www.mathworks.com\/help\/matlab\/math\/empty-arrays.html\">detailed thought process<\/a> on what to do when the row is empty (length 0) or column is empty (length 0) in our discussion getting into the implementation details or <code>dataset<\/code>\/<code>table <\/code>(heterogenous data type). I just haven&#8217;t seen the thoughtfulness in Python (lists), Numpy (array) or Pandas (dataframe) yet.<\/p>\n\n\n\n<p>Now that with the poorly thought out extra check in <code>pd.concat()<\/code>, I have to check if the list is all <code>None<\/code>. I often do not jump to listcomp or maps if there&#8217;s a more intuitive way as listcomps\/maps are shorthands for writing for-loops instead a expressing specific concept, such as <code>list.count(np.nan)==len(list)<\/code> or <code>set(list)=={'None'}<\/code> for checking if all entries in the list are <code>NaN<\/code> or <code>None<\/code>. Both of them are no-go because:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Dataframe broke <code>list.count()<\/code> with <code>TypeError: unhashable type: 'DataFrame'<\/code> because Dataframe has no <code>__hash__<\/code>, because it&#8217;s mutable (can do in-place operation through reference).<\/li>\n<\/ul>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Then dataframe breaks <code>set()<\/code> with <code>ValueError: The truth value of a DataFrame is ambiguous...<\/code>, because the meaning of <code>==<\/code> changed from object comparsion (which returns a simple boolean) to elementwise comparsion (that returns a non-singleton structure with the same shape\/frame as the Dataframe itself).<\/li>\n<\/ul>\n\n\n\n<p><\/p>\n\n\n\n<p><\/p>\n<div class=\"pvc_clear\"><\/div><p id=\"pvc_stats_6339\" class=\"pvc_stats all  \" data-element-id=\"6339\" 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>A lot of MATLAB&#8217;s conveniences over Python and vice versa stem from their design choices of what are first class citizens and what are afterthoughts. MATLAB has its roots from scientific computing, so operations (use cases) that are natural to &hellip; <a href=\"https:\/\/wonghoi.humgar.com\/blog\/2025\/04\/21\/matlab-features-conveniences-not-available-in-python-c\/\">Continue reading <span class=\"meta-nav\">&rarr;<\/span><\/a><\/p>\n<div class=\"pvc_clear\"><\/div>\n<p id=\"pvc_stats_6339\" class=\"pvc_stats all  \" data-element-id=\"6339\" 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-6339","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\/6339","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=6339"}],"version-history":[{"count":36,"href":"https:\/\/wonghoi.humgar.com\/blog\/wp-json\/wp\/v2\/posts\/6339\/revisions"}],"predecessor-version":[{"id":6607,"href":"https:\/\/wonghoi.humgar.com\/blog\/wp-json\/wp\/v2\/posts\/6339\/revisions\/6607"}],"wp:attachment":[{"href":"https:\/\/wonghoi.humgar.com\/blog\/wp-json\/wp\/v2\/media?parent=6339"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/wonghoi.humgar.com\/blog\/wp-json\/wp\/v2\/categories?post=6339"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/wonghoi.humgar.com\/blog\/wp-json\/wp\/v2\/tags?post=6339"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}