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",