diff options
author | neodarz <neodarz@neodarz.net> | 2017-04-28 19:05:18 +0200 |
---|---|---|
committer | neodarz <neodarz@neodarz.net> | 2017-04-28 19:05:18 +0200 |
commit | cdabe7a75ea14f14ca8d4cd3bf9ac36cb1817531 (patch) | |
tree | a3a9557d77f376394bb8e49fc48dcdef556ecb38 /build/blog/2015-12-27-lesson-on-magic-method-access-of-python-new-style-classes-from-my-failed-python3-port-of-tomorrow.html | |
parent | d08bc695f396f8e2d7fc96de7efe61818c7c9a15 (diff) | |
download | my_new_personal_website-cdabe7a75ea14f14ca8d4cd3bf9ac36cb1817531.tar.xz my_new_personal_website-cdabe7a75ea14f14ca8d4cd3bf9ac36cb1817531.zip |
Delete some usless file
Diffstat (limited to 'build/blog/2015-12-27-lesson-on-magic-method-access-of-python-new-style-classes-from-my-failed-python3-port-of-tomorrow.html')
-rw-r--r-- | build/blog/2015-12-27-lesson-on-magic-method-access-of-python-new-style-classes-from-my-failed-python3-port-of-tomorrow.html | 118 |
1 files changed, 0 insertions, 118 deletions
diff --git a/build/blog/2015-12-27-lesson-on-magic-method-access-of-python-new-style-classes-from-my-failed-python3-port-of-tomorrow.html b/build/blog/2015-12-27-lesson-on-magic-method-access-of-python-new-style-classes-from-my-failed-python3-port-of-tomorrow.html deleted file mode 100644 index 839ce5d5..00000000 --- a/build/blog/2015-12-27-lesson-on-magic-method-access-of-python-new-style-classes-from-my-failed-python3-port-of-tomorrow.html +++ /dev/null @@ -1,118 +0,0 @@ -<!DOCTYPE html> -<html> -<head> -<meta charset="utf-8"/> -<meta content="pandoc" name="generator"/> -<meta content="Zhiming Wang" name="author"/> -<meta content="2015-12-27T16:47:05-08:00" name="date"/> -<title>Lesson on magic method access of Python new-style classes (from my failed Python3 port of Tomorrow)</title> -<link href="/img/apple-touch-icon-152.png" rel="apple-touch-icon-precomposed"/> -<meta content="#FFFFFF" name="msapplication-TileColor"/> -<meta content="/img/favicon-144.png" name="msapplication-TileImage"/> -<meta content="width=device-width, initial-scale=1" name="viewport"/> -<link href="/css/normalize.min.css" media="all" rel="stylesheet" type="text/css"/> -<link href="/css/theme.css" media="all" rel="stylesheet" type="text/css"/> -<link href="/css/highlight.css" media="all" rel="stylesheet" type="text/css"/> -</head> -<body> -<div id="archival-notice">This blog has been archived.<br/>Visit my home page at <a href="https://zhimingwang.org">zhimingwang.org</a>.</div> -<nav class="nav"> -<a class="nav-icon" href="/" title="Home"><!--blog icon--></a> -<a class="nav-title" href="/"><!--blog title--></a> -<a class="nav-author" href="https://github.com/zmwangx" target="_blank"><!--blog author--></a> -</nav> -<article class="content"> -<header class="article-header"> -<h1 class="article-title">Lesson on magic method access of Python new-style classes (from my failed Python3 port of Tomorrow)</h1> -<div class="article-metadata"> -<time class="article-timestamp" datetime="2015-12-27T16:47:05-08:00">December 27, 2015</time> -</div> -</header> -<p>I know the title is formidably long, but I can't find something more accurate (and my homegrown mini CMS doesn't support subtitle), so please bear with me.</p> -<p>So, I have <a href="https://github.com/madisonmay/Tomorrow">madisonmay/Tomorrow</a> — "magic decorator syntax for asynchronous code in Python 2.7" — bookmarked for a long time<a class="footnoteRef" href="#fn1" id="fnref1"><sup>1</sup></a> without ever trying it, because I simply don't write Python 2 code any more (except when I try to maintain compatibililty). I felt kind of strange that a ~50-line project with ~1000 stars on GitHub hasn't been ported to Python 3 already, so I gave it a shot just now.</p> -<p>I thought it would be easy:</p> -<ol style="list-style-type: decimal"> -<li>Modernize the old-style class <code>Tomorrow</code>;</li> -<li>Replace <code>__getattr__</code> with <code>__getattribute__</code> for unconditional attribute routing, then make a few exceptions to prevent infinite recursion;</li> -<li><code>2to3</code> test cases;</li> -<li>Make meta changes, like removing the <code>futures</code> dependency.</li> -</ol> -<p>However, after doing 1–3, I ran the tests, and out of the five test cases, three failed and one errored. I tried to isolate the problem, and ended up with the following piece of proof-of-concept:</p> -<div class="sourceCode"><pre class="sourceCode python"><code class="sourceCode python"><span class="kw">class</span> PassThrough(<span class="bu">object</span>): - - <span class="kw">def</span> <span class="fu">__init__</span>(<span class="va">self</span>, obj): - <span class="va">self</span>._obj <span class="op">=</span> obj - - <span class="kw">def</span> <span class="fu">__getattribute__</span>(<span class="va">self</span>, name): - <span class="cf">if</span> name <span class="op">==</span> <span class="st">"_obj"</span>: - <span class="cf">return</span> <span class="bu">object</span>.<span class="fu">__getattribute__</span>(<span class="va">self</span>, name) - <span class="bu">print</span>(<span class="st">"Accessing '</span><span class="sc">%s</span><span class="st">'"</span> <span class="op">%</span> name) - <span class="cf">return</span> <span class="va">self</span>._obj.<span class="fu">__getattribute__</span>(name)</code></pre></div> -<p>This snippet is valid in both Python 2.7 and Python 3, but here's the surprise:</p> -<div class="sourceCode"><pre class="sourceCode python"><code class="sourceCode python"><span class="op">>>></span> g <span class="op">=</span> PassThrough(<span class="dv">0</span>) -<span class="op">>>></span> <span class="bu">print</span>(g) -<span class="op"><</span>__main__.PassThrough <span class="bu">object</span> at <span class="bn">0x10c662e48</span><span class="op">></span> -<span class="op">>>></span> <span class="bu">str</span>(g) -<span class="co">'<__main__.PassThrough object at 0x10c662e48>'</span> -<span class="op">>>></span> <span class="bu">hasattr</span>(g, <span class="st">'__str__'</span>) -Accessing <span class="st">'__str__'</span> -<span class="va">True</span> -<span class="op">>>></span> g.<span class="fu">__str__</span>() -Accessing <span class="st">'__str__'</span> -<span class="co">'0'</span></code></pre></div> -<p>In addition, here's what happens if you try to "pass through" a function:</p> -<div class="sourceCode"><pre class="sourceCode python"><code class="sourceCode python"><span class="op">>>></span> <span class="kw">def</span> f(): <span class="cf">return</span> <span class="va">True</span> -<span class="op">>>></span> g <span class="op">=</span> PassThrough(f) -<span class="op">>>></span> g() -Accessing <span class="st">'__class__'</span> -Accessing <span class="st">'__class__'</span> -Traceback (most recent call last): - File <span class="st">"<ipython-input-6-d65ffd94a45c>"</span>, line <span class="dv">1</span>, <span class="kw">in</span> <span class="op"><</span>module<span class="op">></span> - g() -<span class="pp">TypeError</span>: <span class="st">'PassThrough'</span> <span class="bu">object</span> <span class="kw">is</span> <span class="kw">not</span> <span class="bu">callable</span> - -<span class="op">>>></span> <span class="bu">callable</span>(g) -<span class="va">False</span> -<span class="op">>>></span> <span class="bu">hasattr</span>(g, <span class="st">'__call__'</span>) -Accessing <span class="st">'__call__'</span> -<span class="va">True</span> -<span class="op">>>></span> g.<span class="fu">__call__</span>() -Accessing <span class="st">'__call__'</span> -<span class="va">True</span></code></pre></div> -<p>As you can tell, although <code>__str__</code> or <code>__call__</code> may have been implemented through <code>__getattribute__</code>, and <code>hasattr</code> (which in turn depends on <code>getattr</code>) has no trouble finding them, they are not picked up by <code>str</code> or function call <code>(...)</code>. At this point, one would suspect that this is due to <code>str</code> or function call only looking at the class instance's <code>__dict__</code>. Compare this to the behavior of an old-style class:</p> -<div class="sourceCode"><pre class="sourceCode python"><code class="sourceCode python"><span class="kw">class</span> PassThrough(): - - <span class="kw">def</span> <span class="fu">__init__</span>(<span class="va">self</span>, obj): - <span class="va">self</span>._obj <span class="op">=</span> obj - - <span class="kw">def</span> <span class="fu">__getattr__</span>(<span class="va">self</span>, name): - <span class="bu">print</span>(<span class="st">"Acessing '</span><span class="sc">%s</span><span class="st">'"</span> <span class="op">%</span> name) - <span class="cf">return</span> <span class="va">self</span>._obj.<span class="fu">__getattribute__</span>(name)</code></pre></div> -<p>Now:</p> -<div class="sourceCode"><pre class="sourceCode python"><code class="sourceCode python"><span class="op">>>></span> g <span class="op">=</span> PassThrough(<span class="dv">0</span>) -<span class="op">>>></span> <span class="bu">print</span>(g) -Acessing <span class="st">'__str__'</span> -<span class="dv">0</span> -<span class="op">>>></span> <span class="kw">def</span> f(): <span class="cf">return</span> <span class="va">True</span> -<span class="op">>>></span> g <span class="op">=</span> PassThrough(f) -<span class="op">>>></span> g() -Acessing <span class="st">'__call__'</span> -<span class="va">True</span></code></pre></div> -<p>Note that magic method access is always routed through <code>__getattr__</code>.</p> -<p>After some digging, my suspicion was confirmed: indeed, for new-style classes, rather than invoking <code>__getattribute__</code>, the Python interpreter only looks for magic methods in <code>__dict__</code>. But is there a workaround for implementing something like the <code>PassThrough</code> class above? There's a <a href="http://stackoverflow.com/a/9059858/">nice answer</a> on StackOverflow that uses a metaclass to "automatically add proxies for magic methods at the time of class creation", to quote the author. However, the thing about Tomorrow is that <em>we don't have the result and don't know whatever magic methods it might have at class creation</em> — after all, Python isn't a statically typed language. It is possible for programmers to offer hints, but then Tomorrow won't be as elegant and magical anymore. Therefore, unfortunately enough, Tomorrow isn't portable to Python 3 — at least not without a substantial hack that's beyond my knowledge, or a complete overhaul of its logic (haven't thought about that).</p> -<div class="footnotes"> -<hr/> -<ol> -<li id="fn1"><p>Pretty much since the beginning, I believe (the <a href="https://github.com/madisonmay/Tomorrow/commit/22a53dfbcf9b516ecd1770eeca9fcf1720271240">initial commit</a> was from July 24 of this year). I don't remember how I came accross it though.<a class="footnotes-backlink" href="#fnref1">↩︎</a></p></li> -</ol> -</div> -</article> -<hr class="content-separator"/> -<footer class="footer"> -<span class="rfooter"> -<a class="rss-icon" href="/rss.xml" target="_blank" title="RSS feed"><!--RSS feed icon--></a><a class="atom-icon" href="/atom.xml" target="_blank" title="Atom feed"><!--Atom feed icon--></a><a class="cc-icon" href="https://creativecommons.org/licenses/by/4.0/" target="_blank" title="Released under the Creative Commons Attribution 4.0 International license."><!--CC icon--></a> -<a href="https://github.com/zmwangx" target="_blank">Zhiming Wang</a> -</span> -</footer> -</body> -</html> |