index.html (5914B)
1 <!DOCTYPE html> 2 <html> 3 <head> 4 <title>banna - ramblings</title> 5 <meta name="viewport" content="width=device-width, initial-scale=1"> 6 <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> 7 <link rel="stylesheet" href="/styles/style.css" /> 8 <link rel="stylesheet" href="/styles/look.css" /> 9 <link rel="stylesheet" href="/styles/codetheme.css" /> 10 <script src="/scripts/highlight.js"></script> 11 <script>hljs.initHighlightingOnLoad();</script> 12 </head> 13 14 <body> 15 <div id="sidebar"> 16 <div id="logo"> 17 <a href="/"> 18 <img src="/banna.png" alt="banna.tech" height="45" width="201" /> 19 </a> 20 </div> 21 <div id="nav"> 22 <a class="current" href="/post">blog</a> 23 <a class="" href="/things">things</a> 24 <a class="" href="/about">about</a> 25 </div> 26 </div> 27 28 <div id="wrapper"> 29 <div id="content"> 30 <div class="post"> 31 <a href="/post/chained_conditional_expressions_in_python" class="link"><h1 class="header">Chained conditional expressions in python</h1></a> 32 <span class="date">Posted on July, 12 2015</span> 33 <div class="content"> 34 <blockquote> 35 <p>NOTE: Before you start reading this, read up on <a href="https://en.wikipedia.org/wiki/Stack_machine">stack machines</a> if you have not done so yet. In this post I'll be going over some python bytecode, knowing what a stack machine will help a lot!</p> 36 </blockquote> 37 <pre><code>False == False in [False] 38 </code></pre> 39 <p>A friend had recently showed this statement to me, and asked what it evaluates 40 to. I replied 'False', of course, since <code>False == False</code> is <code>True</code>, and <code>True</code> 41 is not in the list <code>[False]</code>. Until, of course, I threw it in the python 42 interpreter.</p> 43 <pre><code>>>> False == False in [False] 44 True 45 </code></pre> 46 <p>Now, now... what's this madness? Is python pulling a javascript? To answer this 47 problem we will disassemble a python function into python bytecode and evaluate 48 it line-by-line.</p> 49 <pre><code>>>> def func(): 50 ... # We'll name these constants as they pop up in the stack 51 ... # A B C 52 ... return False == False in [False] 53 ... 54 >>> import dis 55 >>> dis.dis(func) 56 2 0 LOAD_GLOBAL 0 (False) 57 3 LOAD_GLOBAL 0 (False) 58 6 DUP_TOP 59 7 ROT_THREE 60 8 COMPARE_OP 2 (==) 61 11 JUMP_IF_FALSE_OR_POP 24 62 14 LOAD_GLOBAL 0 (False) 63 17 BUILD_LIST 1 64 20 COMPARE_OP 6 (in) 65 23 RETURN_VALUE 66 >> 24 ROT_TWO 67 25 POP_TOP 68 26 RETURN_VALUE 69 </code></pre> 70 <h3>Lines 0-3</h3> 71 <p>On lines 0-3, <code>LOAD_GLOBAL</code> is called twice to load <code>A</code> and <code>B</code> into the stack. Below is a representation of the stack, with the label/ID mnemonic above each stack element. </p> 72 <pre><code># TOS (top-of-stack), TOS1 73 # B A 74 stack = [False, False] 75 </code></pre> 76 <h3>Line 6</h3> 77 <p><code>DUP_TOP</code> duplicates the 'top' of the stack.</p> 78 <pre><code># TOS , TOS1 79 # DUP.B B A 80 stack = [False, False, False] 81 </code></pre> 82 <h3>Line 7</h3> 83 <p><code>ROT_THREE</code> rotates the top three elements in the stack.</p> 84 <pre><code># TOS , TOS1 85 # B A DUP.B 86 stack = [False, False, False] 87 </code></pre> 88 <h3>Line 8</h3> 89 <p>The next instruction is <code>COMPARE_OP(==)</code>, which compares if <code>TOS == TOS1</code>, 90 pops <code>TOS</code> and <code>TOS1</code> before pushing the result of <code>TOS == TOS1</code>.</p> 91 <pre><code># TOS , TOS1 92 # A==B, DUP.B 93 stack = [True, False] 94 </code></pre> 95 <h3>Line 11</h3> 96 <p><code>JUMP_IF_FALSE_OR_POP</code> jumps if <code>TOS == False</code>, else it pops the stack and moves 97 on</p> 98 <pre><code># TOS 99 # DUP.B 100 stack = [False] 101 </code></pre> 102 <p>Huh, won't you look at that? The result of <code>False == False</code> was thrown away 103 completely, leaving only the duplicate of B in the stack. This tells me that my 104 initial theory was completely wrong, python is not chaining these operations 105 together at all!</p> 106 <h3>Lines 14-17</h3> 107 <p>Now, the next set of instructions (14-17) builds the list, by pushing <code>False</code> on 108 the stack and using <code>BUILD_LIST(size)</code> to create the list.</p> 109 <pre><code># TOS , TOS1 110 # C B 111 stack = [List, False] 112 </code></pre> 113 <h3>Line 20</h3> 114 <p><code>COMPARE_OP(in)</code> tests whether <code>TOS1</code> is in list <code>TOS</code>, which is <code>True</code>.</p> 115 <pre><code># TOS 116 stack = [True] 117 </code></pre> 118 <p>The line after that is <code>RETURN_VALUE</code>, which returns the value of <code>TOS</code>, which 119 is what we get when we run func()</p> 120 <h2>Analysis</h2> 121 <p>From what we can see, python does not chain these operations together as 122 expected. It actually doesn't chain the operations together at all.</p> 123 <p>Looking at what values are in the stack at each instruction, we can see that 124 instead of keeping the result of <code>A == B</code>, it keeps what we named as <code>B</code> 125 in the stack and tested whether or not <code>B</code> was in the list <code>C</code>. On line 11, 126 <code>JUMP_IF_FALSE_OR_POP</code> was used. If <code>A == B</code> were to evaluate to <code>False</code> 127 , it would end up jumping to line <code>24</code> and return <code>False</code>.</p> 128 <h2>Conclusion</h2> 129 <p>In short, what the bytecode tells us is that the statement we wrote as</p> 130 <p><code>A == B in [C]</code> is interpreted as <code>A == B and B in C</code>.</p> 131 <p>This must mean that given a statement structure as <code>A is B is C</code>, the statement 132 is expanded into <code>A is B and B is C</code>.</p> 133 </div> 134 </div> 135 136 137 </div> 138 </div> 139 </body> 140 </html>