diff --git a/.ipynb_checkpoints/not_so_obvious_python_stuff-checkpoint.ipynb b/.ipynb_checkpoints/not_so_obvious_python_stuff-checkpoint.ipynb index 9b60f15..5497f91 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:ef925e4eaf58c6daf50a495a77d38aa0b02a365dfc972bb5c0174332360b5adb" + "signature": "sha256:35f23d6f2bbaa131a262533ed080684ed53fc0260430780f248c09af8c645759" }, "nbformat": 3, "nbformat_minor": 0, @@ -69,7 +69,8 @@ "- [List slicing using indexes that are \"out of range](#out_of_range_slicing)\n", "- [Reusing global variable names and UnboundLocalErrors](#unboundlocalerror)\n", "- [Creating copies of mutable objects](#copy_mutable)\n", - "- [Key differences between Python 2 and 3](#python_differences)" + "- [Key differences between Python 2 and 3](#python_differences)\n", + "- [Function annotations - What are those `->`'s in my Python code?](#function_annotation)" ] }, { @@ -1807,6 +1808,7 @@ "source": [ "
\n", "
\n", + "\n", "## Key differences between Python 2 and 3\n", "\n", "There are some good articles already that are summarizing the differences between Python 2 and 3, e.g., \n", @@ -2043,6 +2045,15 @@ "metadata": {}, "outputs": [] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Handling exceptions\n", + "\n", + "Also the handling of excecptions has slightly changed in Python 3. Now, we have to use the `as` keyword!" + ] + }, { "cell_type": "code", "collapsed": false, @@ -2061,11 +2072,266 @@ "... except NameError as err:\n", "... print(err, '--> our error msg')\n", "... \n", - "name 'blabla' is not defined --> our error msg\n" + "name 'blabla' is not defined --> our error msg" ], "language": "python", "metadata": {}, "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### The `next()` function and `.next()` method\n", + "\n", + "Where you can use both function and method in Python 2.7.5, the `next()` function is all that remain in Python 3!" + ] + }, + { + "cell_type": "code", + "collapsed": false, + "input": [ + "# Python 2\n", + ">>> my_generator = (letter for letter in 'abcdefg')\n", + ">>> my_generator.next()\n", + "'a'\n", + ">>> next(my_generator)\n", + "'b'\n", + "\n", + "# Python 3\n", + ">>> my_generator = (letter for letter in 'abcdefg')\n", + ">>> next(my_generator)\n", + "'a'\n", + ">>> my_generator.next()\n", + "Traceback (most recent call last):\n", + " File \"\", line 1, in \n", + "AttributeError: 'generator' object has no attribute 'next'" + ], + "language": "python", + "metadata": {}, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
\n", + "
\n", + "\n", + "## Function annotations - What are those `->`'s in my Python code?\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Have you ever seen any Python code that used colons inside the parantheses of a function definition?" + ] + }, + { + "cell_type": "code", + "collapsed": false, + "input": [ + "def foo1(x: 'insert x here', y: 'insert x^2 here'):\n", + " print('Hello, World')\n", + " return" + ], + "language": "python", + "metadata": {}, + "outputs": [], + "prompt_number": 8 + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "And what about the fancy arrow here?" + ] + }, + { + "cell_type": "code", + "collapsed": false, + "input": [ + "def foo2(x, y) -> 'Hi!':\n", + " print('Hello, World')\n", + " return" + ], + "language": "python", + "metadata": {}, + "outputs": [], + "prompt_number": 10 + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Q: Is this valid Python syntax? \n", + "A: Yes!\n", + " \n", + " \n", + "Q: So, what happens if I *just call* the function? \n", + "A: Nothing!\n", + " \n", + "Here is the proof!" + ] + }, + { + "cell_type": "code", + "collapsed": false, + "input": [ + "foo1(1,2)" + ], + "language": "python", + "metadata": {}, + "outputs": [ + { + "output_type": "stream", + "stream": "stdout", + "text": [ + "Hello, World\n" + ] + } + ], + "prompt_number": 9 + }, + { + "cell_type": "code", + "collapsed": false, + "input": [ + "foo2(1,2) " + ], + "language": "python", + "metadata": {}, + "outputs": [ + { + "output_type": "stream", + "stream": "stdout", + "text": [ + "Hello, World\n" + ] + } + ], + "prompt_number": 11 + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**So, those are function annotations ... ** \n", + "- the colon for the function parameters \n", + "- the arrow for the return value \n", + "\n", + "You probably will never make use of them (or at least very rarely). Usually, we write good function documentations below the function as a docstring - or at least this is how I would do it (okay this case is a little bit extreme, I have to admit):" + ] + }, + { + "cell_type": "code", + "collapsed": false, + "input": [ + "def is_palindrome(a):\n", + " \"\"\"\n", + " Case-and punctuation insensitive check if a string is a palindrom.\n", + " \n", + " Keyword arguments:\n", + " a (str): The string to be checked if it is a palindrome.\n", + " \n", + " Returns `True` if input string is a palindrome, else False.\n", + " \n", + " \"\"\"\n", + " stripped_str = [l for l in my_str.lower() if l.isalpha()]\n", + " return stripped_str == stripped_str[::-1]\n", + " " + ], + "language": "python", + "metadata": {}, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "However, function annotations can be useful to indicate that work is still in progress in some cases. But they are optional and I see them very very rarely.\n", + "\n", + "As it is stated in [PEP3107](http://legacy.python.org/dev/peps/pep-3107/#fundamentals-of-function-annotations):\n", + "\n", + "1. Function annotations, both for parameters and return values, are completely optional.\n", + "\n", + "2. Function annotations are nothing more than a way of associating arbitrary Python expressions with various parts of a function at compile-time.\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The nice thing about function annotations is their `__annotations__` attribute, which is dictionary of all the parameters and/or the `return` value you annotated." + ] + }, + { + "cell_type": "code", + "collapsed": false, + "input": [ + "foo1.__annotations__" + ], + "language": "python", + "metadata": {}, + "outputs": [ + { + "metadata": {}, + "output_type": "pyout", + "prompt_number": 17, + "text": [ + "{'y': 'insert x^2 here', 'x': 'insert x here'}" + ] + } + ], + "prompt_number": 17 + }, + { + "cell_type": "code", + "collapsed": false, + "input": [ + "foo2.__annotations__" + ], + "language": "python", + "metadata": {}, + "outputs": [ + { + "metadata": {}, + "output_type": "pyout", + "prompt_number": 18, + "text": [ + "{'return': 'Hi!'}" + ] + } + ], + "prompt_number": 18 + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**When are they useful?**" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Function annotations can be useful for a couple of things \n", + "- Documentation in general\n", + "- pre-condition testing\n", + "- [type checking](http://legacy.python.org/dev/peps/pep-0362/#annotation-checker)\n", + " \n", + "..." + ] + }, + { + "cell_type": "code", + "collapsed": false, + "input": [], + "language": "python", + "metadata": {}, + "outputs": [] } ], "metadata": {} diff --git a/not_so_obvious_python_stuff.ipynb b/not_so_obvious_python_stuff.ipynb index ab5feb2..5497f91 100644 --- a/not_so_obvious_python_stuff.ipynb +++ b/not_so_obvious_python_stuff.ipynb @@ -1,7 +1,7 @@ { "metadata": { "name": "", - "signature": "sha256:ce4b364bfb6414a1b40e25c6ed5320cec6bdbb11d75ce3e44632fe27504ace7e" + "signature": "sha256:35f23d6f2bbaa131a262533ed080684ed53fc0260430780f248c09af8c645759" }, "nbformat": 3, "nbformat_minor": 0, @@ -69,7 +69,8 @@ "- [List slicing using indexes that are \"out of range](#out_of_range_slicing)\n", "- [Reusing global variable names and UnboundLocalErrors](#unboundlocalerror)\n", "- [Creating copies of mutable objects](#copy_mutable)\n", - "- [Key differences between Python 2 and 3](#python_differences)" + "- [Key differences between Python 2 and 3](#python_differences)\n", + "- [Function annotations - What are those `->`'s in my Python code?](#function_annotation)" ] }, { @@ -2109,6 +2110,228 @@ "language": "python", "metadata": {}, "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
\n", + "
\n", + "\n", + "## Function annotations - What are those `->`'s in my Python code?\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Have you ever seen any Python code that used colons inside the parantheses of a function definition?" + ] + }, + { + "cell_type": "code", + "collapsed": false, + "input": [ + "def foo1(x: 'insert x here', y: 'insert x^2 here'):\n", + " print('Hello, World')\n", + " return" + ], + "language": "python", + "metadata": {}, + "outputs": [], + "prompt_number": 8 + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "And what about the fancy arrow here?" + ] + }, + { + "cell_type": "code", + "collapsed": false, + "input": [ + "def foo2(x, y) -> 'Hi!':\n", + " print('Hello, World')\n", + " return" + ], + "language": "python", + "metadata": {}, + "outputs": [], + "prompt_number": 10 + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Q: Is this valid Python syntax? \n", + "A: Yes!\n", + " \n", + " \n", + "Q: So, what happens if I *just call* the function? \n", + "A: Nothing!\n", + " \n", + "Here is the proof!" + ] + }, + { + "cell_type": "code", + "collapsed": false, + "input": [ + "foo1(1,2)" + ], + "language": "python", + "metadata": {}, + "outputs": [ + { + "output_type": "stream", + "stream": "stdout", + "text": [ + "Hello, World\n" + ] + } + ], + "prompt_number": 9 + }, + { + "cell_type": "code", + "collapsed": false, + "input": [ + "foo2(1,2) " + ], + "language": "python", + "metadata": {}, + "outputs": [ + { + "output_type": "stream", + "stream": "stdout", + "text": [ + "Hello, World\n" + ] + } + ], + "prompt_number": 11 + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**So, those are function annotations ... ** \n", + "- the colon for the function parameters \n", + "- the arrow for the return value \n", + "\n", + "You probably will never make use of them (or at least very rarely). Usually, we write good function documentations below the function as a docstring - or at least this is how I would do it (okay this case is a little bit extreme, I have to admit):" + ] + }, + { + "cell_type": "code", + "collapsed": false, + "input": [ + "def is_palindrome(a):\n", + " \"\"\"\n", + " Case-and punctuation insensitive check if a string is a palindrom.\n", + " \n", + " Keyword arguments:\n", + " a (str): The string to be checked if it is a palindrome.\n", + " \n", + " Returns `True` if input string is a palindrome, else False.\n", + " \n", + " \"\"\"\n", + " stripped_str = [l for l in my_str.lower() if l.isalpha()]\n", + " return stripped_str == stripped_str[::-1]\n", + " " + ], + "language": "python", + "metadata": {}, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "However, function annotations can be useful to indicate that work is still in progress in some cases. But they are optional and I see them very very rarely.\n", + "\n", + "As it is stated in [PEP3107](http://legacy.python.org/dev/peps/pep-3107/#fundamentals-of-function-annotations):\n", + "\n", + "1. Function annotations, both for parameters and return values, are completely optional.\n", + "\n", + "2. Function annotations are nothing more than a way of associating arbitrary Python expressions with various parts of a function at compile-time.\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The nice thing about function annotations is their `__annotations__` attribute, which is dictionary of all the parameters and/or the `return` value you annotated." + ] + }, + { + "cell_type": "code", + "collapsed": false, + "input": [ + "foo1.__annotations__" + ], + "language": "python", + "metadata": {}, + "outputs": [ + { + "metadata": {}, + "output_type": "pyout", + "prompt_number": 17, + "text": [ + "{'y': 'insert x^2 here', 'x': 'insert x here'}" + ] + } + ], + "prompt_number": 17 + }, + { + "cell_type": "code", + "collapsed": false, + "input": [ + "foo2.__annotations__" + ], + "language": "python", + "metadata": {}, + "outputs": [ + { + "metadata": {}, + "output_type": "pyout", + "prompt_number": 18, + "text": [ + "{'return': 'Hi!'}" + ] + } + ], + "prompt_number": 18 + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**When are they useful?**" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Function annotations can be useful for a couple of things \n", + "- Documentation in general\n", + "- pre-condition testing\n", + "- [type checking](http://legacy.python.org/dev/peps/pep-0362/#annotation-checker)\n", + " \n", + "..." + ] + }, + { + "cell_type": "code", + "collapsed": false, + "input": [], + "language": "python", + "metadata": {}, + "outputs": [] } ], "metadata": {}