diff --git a/.ipynb_checkpoints/not_so_obvious_python_stuff-checkpoint.ipynb b/.ipynb_checkpoints/not_so_obvious_python_stuff-checkpoint.ipynb index be8ba25..1e0ef3c 100644 --- a/.ipynb_checkpoints/not_so_obvious_python_stuff-checkpoint.ipynb +++ b/.ipynb_checkpoints/not_so_obvious_python_stuff-checkpoint.ipynb @@ -1,7 +1,7 @@ { "metadata": { "name": "", - "signature": "sha256:3b7a6d43400c23d25d965b726e6fba3db0b9b0d2d0bbad73c96b8857f8eaa7ee" + "signature": "sha256:0e1c6e74b301e23ea4146d660afb3f07765686c6c7fa4752f3a4495da7949787" }, "nbformat": 3, "nbformat_minor": 0, @@ -13,7 +13,7 @@ "metadata": {}, "source": [ "Sebastian Raschka \n", - "last updated: 05/02/2014 ([Changelog](#changelog))\n", + "last updated: 05/03/2014 ([Changelog](#changelog))\n", "\n", "[Link to this IPython Notebook on GitHub](https://github.com/rasbt/python_reference/blob/master/not_so_obvious_python_stuff.ipynb)\n", "\n", @@ -83,7 +83,9 @@ "- [Assigning types to variables as values](#variable_types)\n", "- [Only the first clause of generators is evaluated immediately](#generator_rhs)\n", "- [Keyword argument unpacking syntax - `*args` and `**kwargs`](#splat_op)\n", - "- [Metaclasses - What creates a new instance of a class?](#new_instance)" + "- [Metaclasses - What creates a new instance of a class?](#new_instance)\n", + "- [Else-clauses: \"conditional else\" and \"completion else\"](#else_clauses)\n", + "- [Interning of compile-time constants vs. run-time expressions](#string_interning)" ] }, { @@ -3278,6 +3280,13 @@ "## Metaclasses - What creates a new instance of a class?" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "[[back to top](#sections)]" + ] + }, { "cell_type": "markdown", "metadata": {}, @@ -3361,6 +3370,545 @@ ], "prompt_number": 54 }, + { + "cell_type": "code", + "collapsed": false, + "input": [ + "for i in range(5):\n", + " if i == 1:\n", + " print('in for')\n", + "else:\n", + " print('in else')\n", + "print('after for-loop')" + ], + "language": "python", + "metadata": {}, + "outputs": [ + { + "output_type": "stream", + "stream": "stdout", + "text": [ + "in for\n", + "in else\n", + "after for-loop\n" + ] + } + ], + "prompt_number": 5 + }, + { + "cell_type": "code", + "collapsed": false, + "input": [ + "for i in range(5):\n", + " if i == 1:\n", + " break\n", + "else:\n", + " print('in else')\n", + "print('after for-loop')" + ], + "language": "python", + "metadata": {}, + "outputs": [ + { + "output_type": "stream", + "stream": "stdout", + "text": [ + "after for-loop\n" + ] + } + ], + "prompt_number": 6 + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "
\n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Else-clauses: \"conditional else\" and \"completion else\"" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "[[back to top](#sections)]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "I would claim that the conditional \"else\" is every programmer's daily bread and butter. However, there is a second flavor of \"else\"-clauses in Python, which I will call \"completion else\" (for reason that will become clear later). \n", + "But first, let us take a look at our \"traditional\" conditional else that we all are familiar with. \n", + "### Conditional else:" + ] + }, + { + "cell_type": "code", + "collapsed": false, + "input": [ + "# conditional else\n", + "\n", + "a_list = [1,2]\n", + "if a_list[0] == 1:\n", + " print('Hello, World!')\n", + "else:\n", + " print('Bye, World!')" + ], + "language": "python", + "metadata": {}, + "outputs": [ + { + "output_type": "stream", + "stream": "stdout", + "text": [ + "Hello, World!\n" + ] + } + ], + "prompt_number": 3 + }, + { + "cell_type": "code", + "collapsed": false, + "input": [ + "# conditional else\n", + "\n", + "a_list = [1,2]\n", + "if a_list[0] == 2:\n", + " print('Hello, World!')\n", + "else:\n", + " print('Bye, World!')" + ], + "language": "python", + "metadata": {}, + "outputs": [ + { + "output_type": "stream", + "stream": "stdout", + "text": [ + "Bye, World!\n" + ] + } + ], + "prompt_number": 4 + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Why am I showing those simple examples? I think they are good to highlight some of the key points: It is **either** the code under the `if` clause that is executed, **or** the code under the `else` block, but not both. \n", + "If the condition of the `if` clause evaluates to `True`, the `if`-block is exectured, and if it evaluated to `False`, it is the `else` block. \n", + "\n", + "### Completion else\n", + "**In contrast** to the **either...or*** situation that we know from the conditional `else`, the completion `else` is executed if a code block finished. \n", + "To show you an example, let us use `else` for error-handling:" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Completion else (try-except)" + ] + }, + { + "cell_type": "code", + "collapsed": false, + "input": [ + "try:\n", + " print('first element:', a_list[0])\n", + "except IndexError:\n", + " print('raised IndexError')\n", + "else:\n", + " print('no error in try-block')" + ], + "language": "python", + "metadata": {}, + "outputs": [ + { + "output_type": "stream", + "stream": "stdout", + "text": [ + "first element: 1\n", + "no error in try-block\n" + ] + } + ], + "prompt_number": 5 + }, + { + "cell_type": "code", + "collapsed": false, + "input": [ + "try:\n", + " print('third element:', a_list[2])\n", + "except IndexError:\n", + " print('raised IndexError')\n", + "else:\n", + " print('no error in try-block')" + ], + "language": "python", + "metadata": {}, + "outputs": [ + { + "output_type": "stream", + "stream": "stdout", + "text": [ + "raised IndexError\n" + ] + } + ], + "prompt_number": 6 + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
\n", + "In the code above, we can see that the code under the **`else`-clause is only executed if the `try-block` was executed without encountering an error, i.e., if the `try`-block is \"complete\".** \n", + "The same rule applies to the \"completion\" `else` in while- and for-loops, which you can confirm in the following samples below." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Completion else (while-loop)" + ] + }, + { + "cell_type": "code", + "collapsed": false, + "input": [ + "i = 0\n", + "while i < 2:\n", + " print(i)\n", + " i += 1\n", + "else:\n", + " print('in else')" + ], + "language": "python", + "metadata": {}, + "outputs": [ + { + "output_type": "stream", + "stream": "stdout", + "text": [ + "0\n", + "1\n", + "in else\n" + ] + } + ], + "prompt_number": 7 + }, + { + "cell_type": "code", + "collapsed": false, + "input": [ + "i = 0\n", + "while i < 2:\n", + " print(i)\n", + " i += 1\n", + " break\n", + "else:\n", + " print('completed while-loop')" + ], + "language": "python", + "metadata": {}, + "outputs": [ + { + "output_type": "stream", + "stream": "stdout", + "text": [ + "0\n" + ] + } + ], + "prompt_number": 8 + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Completion else (for-loop)" + ] + }, + { + "cell_type": "code", + "collapsed": false, + "input": [ + "for i in range(2):\n", + " print(i)\n", + "else:\n", + " print('completed for-loop')" + ], + "language": "python", + "metadata": {}, + "outputs": [ + { + "output_type": "stream", + "stream": "stdout", + "text": [ + "0\n", + "1\n", + "completed for-loop\n" + ] + } + ], + "prompt_number": 9 + }, + { + "cell_type": "code", + "collapsed": false, + "input": [ + "for i in range(2):\n", + " print(i)\n", + " break\n", + "else:\n", + " print('completed for-loop')" + ], + "language": "python", + "metadata": {}, + "outputs": [ + { + "output_type": "stream", + "stream": "stdout", + "text": [ + "0\n" + ] + } + ], + "prompt_number": 10 + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "
\n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Interning of compile-time constants vs. run-time expressions" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "[[back to top](#sections)]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "This might not be particularly useful, but it is nonetheless interesting: Python's interpreter is interning compile-time constants but not run-time expressions (note that this is implementation-specific).\n", + "\n", + "(Original source: [Stackoverflow](http://stackoverflow.com/questions/15541404/python-string-interning))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Let us have a look at the simple example below. Here we are creating 3 variables and assign the value \"Hello\" to them in different ways before we test them for identity." + ] + }, + { + "cell_type": "code", + "collapsed": false, + "input": [ + "hello1 = 'Hello'\n", + "\n", + "hello2 = 'Hell' + 'o'\n", + "\n", + "hello3 = 'Hell'\n", + "hello3 = hello3 + 'o'\n", + "\n", + "print('hello1 is hello2:', hello1 is hello2)\n", + "print('hello1 is hello3:', hello1 is hello3)" + ], + "language": "python", + "metadata": {}, + "outputs": [ + { + "output_type": "stream", + "stream": "stdout", + "text": [ + "hello1 is hello2: True\n", + "hello1 is hello3: False\n" + ] + } + ], + "prompt_number": 34 + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now, how does it come that the first expression evaluates to true, but the second does not? To answer this question, we need to take a closer look at the underlying byte codes:" + ] + }, + { + "cell_type": "code", + "collapsed": false, + "input": [ + "import dis\n", + "def hello1_func():\n", + " s = 'Hello'\n", + " return s\n", + "dis.dis(hello1_func)" + ], + "language": "python", + "metadata": {}, + "outputs": [ + { + "output_type": "stream", + "stream": "stdout", + "text": [ + " 3 0 LOAD_CONST 1 ('Hello')\n", + " 3 STORE_FAST 0 (s)\n", + "\n", + " 4 6 LOAD_FAST 0 (s)\n", + " 9 RETURN_VALUE\n" + ] + } + ], + "prompt_number": 38 + }, + { + "cell_type": "code", + "collapsed": false, + "input": [ + "def hello2_func():\n", + " s = 'Hell' + 'o'\n", + " return s\n", + "dis.dis(hello2_func)" + ], + "language": "python", + "metadata": {}, + "outputs": [ + { + "output_type": "stream", + "stream": "stdout", + "text": [ + " 2 0 LOAD_CONST 3 ('Hello')\n", + " 3 STORE_FAST 0 (s)\n", + "\n", + " 3 6 LOAD_FAST 0 (s)\n", + " 9 RETURN_VALUE\n" + ] + } + ], + "prompt_number": 39 + }, + { + "cell_type": "code", + "collapsed": false, + "input": [ + "def hello3_func():\n", + " s = 'Hell'\n", + " s = s + 'o'\n", + " return s\n", + "dis.dis(hello3_func)" + ], + "language": "python", + "metadata": {}, + "outputs": [ + { + "output_type": "stream", + "stream": "stdout", + "text": [ + " 2 0 LOAD_CONST 1 ('Hell')\n", + " 3 STORE_FAST 0 (s)\n", + "\n", + " 3 6 LOAD_FAST 0 (s)\n", + " 9 LOAD_CONST 2 ('o')\n", + " 12 BINARY_ADD\n", + " 13 STORE_FAST 0 (s)\n", + "\n", + " 4 16 LOAD_FAST 0 (s)\n", + " 19 RETURN_VALUE\n" + ] + } + ], + "prompt_number": 40 + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
\n", + "It looks like that `'Hello'` and `'Hell'` + `'o'` are both evaluated and stored as `'Hello'` at compile-time, whereas the third version \n", + "`s = 'Hell'` \n", + "`s = s + 'o'` seems to be not interned. Let us quickly confirm the behavior with the following code:" + ] + }, + { + "cell_type": "code", + "collapsed": false, + "input": [ + "print(hello1_func() is hello2_func())\n", + "print(hello1_func() is hello3_func())" + ], + "language": "python", + "metadata": {}, + "outputs": [ + { + "output_type": "stream", + "stream": "stdout", + "text": [ + "True\n", + "False\n" + ] + } + ], + "prompt_number": 42 + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Finally, to show that this hypothesis is the answer to this rather unexpected observation, let us `intern` the value manually:" + ] + }, + { + "cell_type": "code", + "collapsed": false, + "input": [ + "import sys\n", + "\n", + "print(hello1_func() is sys.intern(hello3_func()))" + ], + "language": "python", + "metadata": {}, + "outputs": [ + { + "output_type": "stream", + "stream": "stdout", + "text": [ + "True\n" + ] + } + ], + "prompt_number": 45 + }, { "cell_type": "markdown", "metadata": {}, @@ -3390,8 +3938,12 @@ "cell_type": "markdown", "metadata": {}, "source": [ + "#### 05/03/2014\n", + "- new section: else clauses: conditional vs. completion\n", + "- new section: Interning of compile-time constants vs. run-time expressions\n", + "\n", "#### 05/02/2014\n", - "- new section in Python 3.x and Python 2.x key differences: for-loop leak<\n", + "- new section in Python 3.x and Python 2.x key differences: for-loop leak\n", "- new section: Metaclasses - What creates a new instance of a class? \n", "\n", "#### 05/01/2014\n", diff --git a/not_so_obvious_python_stuff.ipynb b/not_so_obvious_python_stuff.ipynb index be8ba25..1e0ef3c 100644 --- a/not_so_obvious_python_stuff.ipynb +++ b/not_so_obvious_python_stuff.ipynb @@ -1,7 +1,7 @@ { "metadata": { "name": "", - "signature": "sha256:3b7a6d43400c23d25d965b726e6fba3db0b9b0d2d0bbad73c96b8857f8eaa7ee" + "signature": "sha256:0e1c6e74b301e23ea4146d660afb3f07765686c6c7fa4752f3a4495da7949787" }, "nbformat": 3, "nbformat_minor": 0, @@ -13,7 +13,7 @@ "metadata": {}, "source": [ "Sebastian Raschka \n", - "last updated: 05/02/2014 ([Changelog](#changelog))\n", + "last updated: 05/03/2014 ([Changelog](#changelog))\n", "\n", "[Link to this IPython Notebook on GitHub](https://github.com/rasbt/python_reference/blob/master/not_so_obvious_python_stuff.ipynb)\n", "\n", @@ -83,7 +83,9 @@ "- [Assigning types to variables as values](#variable_types)\n", "- [Only the first clause of generators is evaluated immediately](#generator_rhs)\n", "- [Keyword argument unpacking syntax - `*args` and `**kwargs`](#splat_op)\n", - "- [Metaclasses - What creates a new instance of a class?](#new_instance)" + "- [Metaclasses - What creates a new instance of a class?](#new_instance)\n", + "- [Else-clauses: \"conditional else\" and \"completion else\"](#else_clauses)\n", + "- [Interning of compile-time constants vs. run-time expressions](#string_interning)" ] }, { @@ -3278,6 +3280,13 @@ "## Metaclasses - What creates a new instance of a class?" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "[[back to top](#sections)]" + ] + }, { "cell_type": "markdown", "metadata": {}, @@ -3361,6 +3370,545 @@ ], "prompt_number": 54 }, + { + "cell_type": "code", + "collapsed": false, + "input": [ + "for i in range(5):\n", + " if i == 1:\n", + " print('in for')\n", + "else:\n", + " print('in else')\n", + "print('after for-loop')" + ], + "language": "python", + "metadata": {}, + "outputs": [ + { + "output_type": "stream", + "stream": "stdout", + "text": [ + "in for\n", + "in else\n", + "after for-loop\n" + ] + } + ], + "prompt_number": 5 + }, + { + "cell_type": "code", + "collapsed": false, + "input": [ + "for i in range(5):\n", + " if i == 1:\n", + " break\n", + "else:\n", + " print('in else')\n", + "print('after for-loop')" + ], + "language": "python", + "metadata": {}, + "outputs": [ + { + "output_type": "stream", + "stream": "stdout", + "text": [ + "after for-loop\n" + ] + } + ], + "prompt_number": 6 + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "
\n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Else-clauses: \"conditional else\" and \"completion else\"" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "[[back to top](#sections)]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "I would claim that the conditional \"else\" is every programmer's daily bread and butter. However, there is a second flavor of \"else\"-clauses in Python, which I will call \"completion else\" (for reason that will become clear later). \n", + "But first, let us take a look at our \"traditional\" conditional else that we all are familiar with. \n", + "### Conditional else:" + ] + }, + { + "cell_type": "code", + "collapsed": false, + "input": [ + "# conditional else\n", + "\n", + "a_list = [1,2]\n", + "if a_list[0] == 1:\n", + " print('Hello, World!')\n", + "else:\n", + " print('Bye, World!')" + ], + "language": "python", + "metadata": {}, + "outputs": [ + { + "output_type": "stream", + "stream": "stdout", + "text": [ + "Hello, World!\n" + ] + } + ], + "prompt_number": 3 + }, + { + "cell_type": "code", + "collapsed": false, + "input": [ + "# conditional else\n", + "\n", + "a_list = [1,2]\n", + "if a_list[0] == 2:\n", + " print('Hello, World!')\n", + "else:\n", + " print('Bye, World!')" + ], + "language": "python", + "metadata": {}, + "outputs": [ + { + "output_type": "stream", + "stream": "stdout", + "text": [ + "Bye, World!\n" + ] + } + ], + "prompt_number": 4 + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Why am I showing those simple examples? I think they are good to highlight some of the key points: It is **either** the code under the `if` clause that is executed, **or** the code under the `else` block, but not both. \n", + "If the condition of the `if` clause evaluates to `True`, the `if`-block is exectured, and if it evaluated to `False`, it is the `else` block. \n", + "\n", + "### Completion else\n", + "**In contrast** to the **either...or*** situation that we know from the conditional `else`, the completion `else` is executed if a code block finished. \n", + "To show you an example, let us use `else` for error-handling:" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Completion else (try-except)" + ] + }, + { + "cell_type": "code", + "collapsed": false, + "input": [ + "try:\n", + " print('first element:', a_list[0])\n", + "except IndexError:\n", + " print('raised IndexError')\n", + "else:\n", + " print('no error in try-block')" + ], + "language": "python", + "metadata": {}, + "outputs": [ + { + "output_type": "stream", + "stream": "stdout", + "text": [ + "first element: 1\n", + "no error in try-block\n" + ] + } + ], + "prompt_number": 5 + }, + { + "cell_type": "code", + "collapsed": false, + "input": [ + "try:\n", + " print('third element:', a_list[2])\n", + "except IndexError:\n", + " print('raised IndexError')\n", + "else:\n", + " print('no error in try-block')" + ], + "language": "python", + "metadata": {}, + "outputs": [ + { + "output_type": "stream", + "stream": "stdout", + "text": [ + "raised IndexError\n" + ] + } + ], + "prompt_number": 6 + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
\n", + "In the code above, we can see that the code under the **`else`-clause is only executed if the `try-block` was executed without encountering an error, i.e., if the `try`-block is \"complete\".** \n", + "The same rule applies to the \"completion\" `else` in while- and for-loops, which you can confirm in the following samples below." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Completion else (while-loop)" + ] + }, + { + "cell_type": "code", + "collapsed": false, + "input": [ + "i = 0\n", + "while i < 2:\n", + " print(i)\n", + " i += 1\n", + "else:\n", + " print('in else')" + ], + "language": "python", + "metadata": {}, + "outputs": [ + { + "output_type": "stream", + "stream": "stdout", + "text": [ + "0\n", + "1\n", + "in else\n" + ] + } + ], + "prompt_number": 7 + }, + { + "cell_type": "code", + "collapsed": false, + "input": [ + "i = 0\n", + "while i < 2:\n", + " print(i)\n", + " i += 1\n", + " break\n", + "else:\n", + " print('completed while-loop')" + ], + "language": "python", + "metadata": {}, + "outputs": [ + { + "output_type": "stream", + "stream": "stdout", + "text": [ + "0\n" + ] + } + ], + "prompt_number": 8 + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Completion else (for-loop)" + ] + }, + { + "cell_type": "code", + "collapsed": false, + "input": [ + "for i in range(2):\n", + " print(i)\n", + "else:\n", + " print('completed for-loop')" + ], + "language": "python", + "metadata": {}, + "outputs": [ + { + "output_type": "stream", + "stream": "stdout", + "text": [ + "0\n", + "1\n", + "completed for-loop\n" + ] + } + ], + "prompt_number": 9 + }, + { + "cell_type": "code", + "collapsed": false, + "input": [ + "for i in range(2):\n", + " print(i)\n", + " break\n", + "else:\n", + " print('completed for-loop')" + ], + "language": "python", + "metadata": {}, + "outputs": [ + { + "output_type": "stream", + "stream": "stdout", + "text": [ + "0\n" + ] + } + ], + "prompt_number": 10 + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "
\n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Interning of compile-time constants vs. run-time expressions" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "[[back to top](#sections)]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "This might not be particularly useful, but it is nonetheless interesting: Python's interpreter is interning compile-time constants but not run-time expressions (note that this is implementation-specific).\n", + "\n", + "(Original source: [Stackoverflow](http://stackoverflow.com/questions/15541404/python-string-interning))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Let us have a look at the simple example below. Here we are creating 3 variables and assign the value \"Hello\" to them in different ways before we test them for identity." + ] + }, + { + "cell_type": "code", + "collapsed": false, + "input": [ + "hello1 = 'Hello'\n", + "\n", + "hello2 = 'Hell' + 'o'\n", + "\n", + "hello3 = 'Hell'\n", + "hello3 = hello3 + 'o'\n", + "\n", + "print('hello1 is hello2:', hello1 is hello2)\n", + "print('hello1 is hello3:', hello1 is hello3)" + ], + "language": "python", + "metadata": {}, + "outputs": [ + { + "output_type": "stream", + "stream": "stdout", + "text": [ + "hello1 is hello2: True\n", + "hello1 is hello3: False\n" + ] + } + ], + "prompt_number": 34 + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now, how does it come that the first expression evaluates to true, but the second does not? To answer this question, we need to take a closer look at the underlying byte codes:" + ] + }, + { + "cell_type": "code", + "collapsed": false, + "input": [ + "import dis\n", + "def hello1_func():\n", + " s = 'Hello'\n", + " return s\n", + "dis.dis(hello1_func)" + ], + "language": "python", + "metadata": {}, + "outputs": [ + { + "output_type": "stream", + "stream": "stdout", + "text": [ + " 3 0 LOAD_CONST 1 ('Hello')\n", + " 3 STORE_FAST 0 (s)\n", + "\n", + " 4 6 LOAD_FAST 0 (s)\n", + " 9 RETURN_VALUE\n" + ] + } + ], + "prompt_number": 38 + }, + { + "cell_type": "code", + "collapsed": false, + "input": [ + "def hello2_func():\n", + " s = 'Hell' + 'o'\n", + " return s\n", + "dis.dis(hello2_func)" + ], + "language": "python", + "metadata": {}, + "outputs": [ + { + "output_type": "stream", + "stream": "stdout", + "text": [ + " 2 0 LOAD_CONST 3 ('Hello')\n", + " 3 STORE_FAST 0 (s)\n", + "\n", + " 3 6 LOAD_FAST 0 (s)\n", + " 9 RETURN_VALUE\n" + ] + } + ], + "prompt_number": 39 + }, + { + "cell_type": "code", + "collapsed": false, + "input": [ + "def hello3_func():\n", + " s = 'Hell'\n", + " s = s + 'o'\n", + " return s\n", + "dis.dis(hello3_func)" + ], + "language": "python", + "metadata": {}, + "outputs": [ + { + "output_type": "stream", + "stream": "stdout", + "text": [ + " 2 0 LOAD_CONST 1 ('Hell')\n", + " 3 STORE_FAST 0 (s)\n", + "\n", + " 3 6 LOAD_FAST 0 (s)\n", + " 9 LOAD_CONST 2 ('o')\n", + " 12 BINARY_ADD\n", + " 13 STORE_FAST 0 (s)\n", + "\n", + " 4 16 LOAD_FAST 0 (s)\n", + " 19 RETURN_VALUE\n" + ] + } + ], + "prompt_number": 40 + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
\n", + "It looks like that `'Hello'` and `'Hell'` + `'o'` are both evaluated and stored as `'Hello'` at compile-time, whereas the third version \n", + "`s = 'Hell'` \n", + "`s = s + 'o'` seems to be not interned. Let us quickly confirm the behavior with the following code:" + ] + }, + { + "cell_type": "code", + "collapsed": false, + "input": [ + "print(hello1_func() is hello2_func())\n", + "print(hello1_func() is hello3_func())" + ], + "language": "python", + "metadata": {}, + "outputs": [ + { + "output_type": "stream", + "stream": "stdout", + "text": [ + "True\n", + "False\n" + ] + } + ], + "prompt_number": 42 + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Finally, to show that this hypothesis is the answer to this rather unexpected observation, let us `intern` the value manually:" + ] + }, + { + "cell_type": "code", + "collapsed": false, + "input": [ + "import sys\n", + "\n", + "print(hello1_func() is sys.intern(hello3_func()))" + ], + "language": "python", + "metadata": {}, + "outputs": [ + { + "output_type": "stream", + "stream": "stdout", + "text": [ + "True\n" + ] + } + ], + "prompt_number": 45 + }, { "cell_type": "markdown", "metadata": {}, @@ -3390,8 +3938,12 @@ "cell_type": "markdown", "metadata": {}, "source": [ + "#### 05/03/2014\n", + "- new section: else clauses: conditional vs. completion\n", + "- new section: Interning of compile-time constants vs. run-time expressions\n", + "\n", "#### 05/02/2014\n", - "- new section in Python 3.x and Python 2.x key differences: for-loop leak<\n", + "- new section in Python 3.x and Python 2.x key differences: for-loop leak\n", "- new section: Metaclasses - What creates a new instance of a class? \n", "\n", "#### 05/01/2014\n",