{ "metadata": { "name": "", "signature": "sha256:58252474399869da2ad828422f1f1147d57e27daa013058e2f295c8dc22f4c09" }, "nbformat": 3, "nbformat_minor": 0, "worksheets": [ { "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# TOP 10 PYTHON IDIOMS I WISH I'D LEARNED EARLIER #\n", "\n", "By David \"Prooffreader -- with two f's, that's the joke!\" Taylor\n", "\n", "This [also appears as a blog post](http://prooffreaderplus.blogspot.ca/2014/11/top-10-python-idioms-i-wished-id.html).\n", "\n", "If you'd like to know an easy way to turn an IPython Notebook into a blog post, I [wrote a blog post about that too!](http://prooffreaderplus.blogspot.ca/2014/11/how-to-quickly-turn-ipython-notebook.html)\n", "\n", "I've been programming all my life, but never been *a* programmer. Most of my work was done in Visual Basic because it's what I was most comfortable with, plus a smattering of other languages (R, C, JavaScript, etc.). A couple of years ago, I decided to use Python exclusively so that I could improve my coding. I ended up re-inventing many wheels -- which I didn't mind too much, because I enjoy solving puzzles. But sometimes it's good to have a more efficient, Pythonesque approach, and time after time I found myself having \"aha!\" moments, realizing I'd been doing things the hard, excessively verbose way for no reason. Here is a list of ten Python idioms that would have made my life much easier if I'd thought to search for them early on.\n", "\n", "Missing from this list are some idioms such as list comprehensions and lambda functions, which are very Pythonesque and very efficient and very cool, but also very difficult to miss because they're mentioned on StackOverflow every other answer! Also ternary x if y else z constructions, decorators and generators, because I don't use them very often." ] }, { "cell_type": "code", "collapsed": false, "input": [ "########################################" ], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 1 }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 1. Python 3-style printing in Python 2 #\n", "\n", "One of the things that kept me from concentrating on Python was this whole version 2 - version 3 debacle. Finally I went with Python 2 because all the libraries I wanted were not 3-compatible, and I figured if I needed to, I would laboriously adjust my code later.\n", " But really, the biggest differences in everyday programming are printing and division, and now I just import from future.\n", "\n" ] }, { "cell_type": "code", "collapsed": false, "input": [ "mynumber = 5\n", "\n", "print \"Python 2:\"\n", "print \"The number is %d\" % (mynumber)\n", "print mynumber / 2,\n", "print mynumber // 2\n", "\n", "from __future__ import print_function\n", "from __future__ import division\n", "\n", "print('\\nPython 3:')\n", "print(\"The number is {}\".format(mynumber))\n", "print(mynumber / 2, end=' ')\n", "print(mynumber // 2)" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "Python 2:\n", "The number is 5\n", "2 2\n", "\n", "Python 3:\n", "The number is 5\n", "2.5 2\n" ] } ], "prompt_number": 2 }, { "cell_type": "markdown", "metadata": {}, "source": [ "Oh, and here's an easter egg for C programmers:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "from __future__ import braces" ], "language": "python", "metadata": {}, "outputs": [ { "ename": "SyntaxError", "evalue": "not a chance (, line 1)", "output_type": "pyerr", "traceback": [ "\u001b[1;36m File \u001b[1;32m\"\"\u001b[1;36m, line \u001b[1;32m1\u001b[0m\n\u001b[1;33m from __future__ import braces\u001b[0m\n\u001b[1;31mSyntaxError\u001b[0m\u001b[1;31m:\u001b[0m not a chance\n" ] } ], "prompt_number": 3 }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 2. enumerate(list) #\n", "\n", "It might seem obvious that you should be able to iterate over a list and its index at the same time, but I used counter variables or slices for an embarrassingly long time." ] }, { "cell_type": "code", "collapsed": false, "input": [ "mylist = [\"It's\", 'only', 'a', 'model']\n", "\n", "for index, item in enumerate(mylist):\n", " print(index, item)" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "0 It's\n", "1 only\n", "2 a\n", "3 model\n" ] } ], "prompt_number": 4 }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 3. Chained comparison operators #\n", "\n", "Because I was so used to statically typed languages (where this idiom would be ambiguous), it never occurred to me to put two operators in the same expression. In many languages, 4 > 3 > 2 would return as False, because (4 > 3) would be evaluated as a boolean, and then True > 2 would be evaluated as False." ] }, { "cell_type": "code", "collapsed": false, "input": [ "mynumber = 3\n", "\n", "if 4 > mynumber > 2:\n", " print(\"Chained comparison operators work! \\n\" * 3)" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "Chained comparison operators work! \n", "Chained comparison operators work! \n", "Chained comparison operators work! \n", "\n" ] } ], "prompt_number": 5 }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 4. collections.Counter #\n", "\n", "The collections library is, like, the best thing ever. Stackoverflow turned me on to ordered dicts early on, but I kept using a snippet to create dicts to count occurrences of results in my code. One of these days, I'll figure out a use for collections.deque." ] }, { "cell_type": "code", "collapsed": false, "input": [ "from collections import Counter\n", "from random import randrange\n", "import pprint\n", "mycounter = Counter()\n", "for i in range(100):\n", " random_number = randrange(10)\n", " mycounter[random_number] += 1\n", "for i in range(10):\n", " print(i, mycounter[i])" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "0 10\n", "1 10\n", "2 13\n", "3 6\n", "4 6\n", "5 11\n", "6 10\n", "7 14\n", "8 12\n", "9 8\n" ] } ], "prompt_number": 6 }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 5. Dict comprehensions #\n", "\n", "A rite of passage for a Python programmer is understanding list comprehensions, but eventually I realized dict comprehensions are just as useful -- especially for reversing dicts." ] }, { "cell_type": "code", "collapsed": false, "input": [ "my_phrase = [\"No\", \"one\", \"expects\", \"the\", \"Spanish\", \"Inquisition\"]\n", "my_dict = {key: value for value, key in enumerate(my_phrase)}\n", "print(my_dict)\n", "reversed_dict = {value: key for key, value in my_dict.items()}\n", "print(reversed_dict)" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "{'Inquisition': 5, 'No': 0, 'expects': 2, 'one': 1, 'Spanish': 4, 'the': 3}\n", "{0: 'No', 1: 'one', 2: 'expects', 3: 'the', 4: 'Spanish', 5: 'Inquisition'}\n" ] } ], "prompt_number": 7 }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 6. Executing shell commands with subprocess #\n", "\n", "I used to use the os library exclusively to manipulate files; now I can even programmatically call complex command-line tools like ffmpeg for video editing" ] }, { "cell_type": "code", "collapsed": false, "input": [ "import subprocess\n", "output = subprocess.check_output('dir', shell=True)\n", "print(output)" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ " Volume in drive C is OS\r\n", " Volume Serial Number is ECAC-AE50\r\n", "\r\n", " Directory of C:\\Users\\David\\Documents\\Dropbox\\IPython_Synced\\GitHub\\Misc_ipynb\r\n", "\r\n", "2014-11-26 06:04 AM .\r\n", "2014-11-26 06:04 AM ..\r\n", "2014-11-23 11:47 AM .git\r\n", "2014-11-26 06:06 AM .ipynb_checkpoints\r\n", "2014-11-23 08:59 AM CCCma\r\n", "2014-09-03 06:58 AM 19,450 colorbrewdict.py\r\n", "2014-09-03 06:58 AM 92,175 imagecompare.ipynb\r\n", "2014-11-23 08:41 AM Japan_Earthquakes\r\n", "2014-09-03 06:58 AM 1,100 LICENSE\r\n", "2014-09-03 06:58 AM 5,263 monty_monte.ipynb\r\n", "2014-09-03 06:58 AM 31,082 pocket_tumblr_reddit_api.ipynb\r\n", "2014-11-26 06:04 AM 3,211 README.md\r\n", "2014-11-26 06:14 AM 19,898 top_10_python_idioms.ipynb\r\n", "2014-09-03 06:58 AM 5,813 tree_convert_mega_to_gexf.ipynb\r\n", "2014-09-03 06:58 AM 5,453 tree_convert_mega_to_json.ipynb\r\n", "2014-09-03 06:58 AM 1,211 tree_convert_newick_to_json.py\r\n", "2014-09-03 06:58 AM 55,970 weather_ML.ipynb\r\n", " 11 File(s) 240,626 bytes\r\n", " 6 Dir(s) 180,880,490,496 bytes free\r\n", "\n" ] } ], "prompt_number": 8 }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 7. dict .get() and .iteritems() methods #\n", "\n", "Having a default value when a key does not exist has all kinds of uses, and just like enumerate() for lists, you can iterate over key, value tuples in dicts" ] }, { "cell_type": "code", "collapsed": false, "input": [ "my_dict = {'name': 'Lancelot', 'quest': 'Holy Grail', 'favourite_color': 'blue'}\n", "\n", "print(my_dict.get('airspeed velocity of an unladen swallow', 'African or European?\\n'))\n", "\n", "for key, value in my_dict.iteritems():\n", " print(key, value, sep=\": \")" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "African or European?\n", "\n", "quest: Holy Grail\n", "name: Lancelot\n", "favourite_color: blue\n" ] } ], "prompt_number": 9 }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 8. Tuple unpacking for switching variables #\n", "\n", "Do you know how many times I had to use a third, temporary dummy variable in VB? c = a; a = b; b = c?" ] }, { "cell_type": "code", "collapsed": false, "input": [ "a = 'Spam'\n", "b = 'Eggs'\n", "\n", "print(a, b)\n", "\n", "a, b = b, a\n", "\n", "print(a, b)" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "Spam Eggs\n", "Eggs Spam\n" ] } ], "prompt_number": 10 }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 9. Introspection with help() #\n", "\n", "I was aware of dir(), but I had assumed help() would do the same thing as IPython's ? magic command. It does way more. (This post has been updated after some great advice from reddit's /r/python which, indeed, I wish I'd known about before!)" ] }, { "cell_type": "code", "collapsed": false, "input": [ "my_dict = {'That': 'an ex-parrot!'}\n", "\n", "help(my_dict)" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "Help on dict object:\n", "\n", "class dict(object)\n", " | dict() -> new empty dictionary\n", " | dict(mapping) -> new dictionary initialized from a mapping object's\n", " | (key, value) pairs\n", " | dict(iterable) -> new dictionary initialized as if via:\n", " | d = {}\n", " | for k, v in iterable:\n", " | d[k] = v\n", " | dict(**kwargs) -> new dictionary initialized with the name=value pairs\n", " | in the keyword argument list. For example: dict(one=1, two=2)\n", " | \n", " | Methods defined here:\n", " | \n", " | __cmp__(...)\n", " | x.__cmp__(y) <==> cmp(x,y)\n", " | \n", " | __contains__(...)\n", " | D.__contains__(k) -> True if D has a key k, else False\n", " | \n", " | __delitem__(...)\n", " | x.__delitem__(y) <==> del x[y]\n", " | \n", " | __eq__(...)\n", " | x.__eq__(y) <==> x==y\n", " | \n", " | __ge__(...)\n", " | x.__ge__(y) <==> x>=y\n", " | \n", " | __getattribute__(...)\n", " | x.__getattribute__('name') <==> x.name\n", " | \n", " | __getitem__(...)\n", " | x.__getitem__(y) <==> x[y]\n", " | \n", " | __gt__(...)\n", " | x.__gt__(y) <==> x>y\n", " | \n", " | __init__(...)\n", " | x.__init__(...) initializes x; see help(type(x)) for signature\n", " | \n", " | __iter__(...)\n", " | x.__iter__() <==> iter(x)\n", " | \n", " | __le__(...)\n", " | x.__le__(y) <==> x<=y\n", " | \n", " | __len__(...)\n", " | x.__len__() <==> len(x)\n", " | \n", " | __lt__(...)\n", " | x.__lt__(y) <==> x x!=y\n", " | \n", " | __repr__(...)\n", " | x.__repr__() <==> repr(x)\n", " | \n", " | __setitem__(...)\n", " | x.__setitem__(i, y) <==> x[i]=y\n", " | \n", " | __sizeof__(...)\n", " | D.__sizeof__() -> size of D in memory, in bytes\n", " | \n", " | clear(...)\n", " | D.clear() -> None. Remove all items from D.\n", " | \n", " | copy(...)\n", " | D.copy() -> a shallow copy of D\n", " | \n", " | fromkeys(...)\n", " | dict.fromkeys(S[,v]) -> New dict with keys from S and values equal to v.\n", " | v defaults to None.\n", " | \n", " | get(...)\n", " | D.get(k[,d]) -> D[k] if k in D, else d. d defaults to None.\n", " | \n", " | has_key(...)\n", " | D.has_key(k) -> True if D has a key k, else False\n", " | \n", " | items(...)\n", " | D.items() -> list of D's (key, value) pairs, as 2-tuples\n", " | \n", " | iteritems(...)\n", " | D.iteritems() -> an iterator over the (key, value) items of D\n", " | \n", " | iterkeys(...)\n", " | D.iterkeys() -> an iterator over the keys of D\n", " | \n", " | itervalues(...)\n", " | D.itervalues() -> an iterator over the values of D\n", " | \n", " | keys(...)\n", " | D.keys() -> list of D's keys\n", " | \n", " | pop(...)\n", " | D.pop(k[,d]) -> v, remove specified key and return the corresponding value.\n", " | If key is not found, d is returned if given, otherwise KeyError is raised\n", " | \n", " | popitem(...)\n", " | D.popitem() -> (k, v), remove and return some (key, value) pair as a\n", " | 2-tuple; but raise KeyError if D is empty.\n", " | \n", " | setdefault(...)\n", " | D.setdefault(k[,d]) -> D.get(k,d), also set D[k]=d if k not in D\n", " | \n", " | update(...)\n", " | D.update([E, ]**F) -> None. Update D from dict/iterable E and F.\n", " | If E present and has a .keys() method, does: for k in E: D[k] = E[k]\n", " | If E present and lacks .keys() method, does: for (k, v) in E: D[k] = v\n", " | In either case, this is followed by: for k in F: D[k] = F[k]\n", " | \n", " | values(...)\n", " | D.values() -> list of D's values\n", " | \n", " | viewitems(...)\n", " | D.viewitems() -> a set-like object providing a view on D's items\n", " | \n", " | viewkeys(...)\n", " | D.viewkeys() -> a set-like object providing a view on D's keys\n", " | \n", " | viewvalues(...)\n", " | D.viewvalues() -> an object providing a view on D's values\n", " | \n", " | ----------------------------------------------------------------------\n", " | Data and other attributes defined here:\n", " | \n", " | __hash__ = None\n", " | \n", " | __new__ = \n", " | T.__new__(S, ...) -> a new object with type S, a subtype of T\n", "\n" ] } ], "prompt_number": 1 }, { "cell_type": "markdown", "metadata": {}, "source": [ "# 10. PEP-8 compliant string chaining #\n", "\n", "[PEP8](https://www.python.org/dev/peps/pep-0008) is the style guide for Python code. Among other things, it directs that lines not be over 80 characters long and that indenting by consistent over line breaks.\n", "\n", "This can be accomplished with a combination of backslashes \\; parentheses () with commas , ; and addition operators +; but every one of these solutions is awkward for multiline strings. There is a multiline string signifier, the triple quote, but it does not allow consistent indenting over line breaks. \n", "\n", "There is a solution: parentheses without commas. I don't know why this works, but I'm glad it does.\n" ] }, { "cell_type": "code", "collapsed": false, "input": [ "my_long_text = (\"We are no longer the knights who say Ni! \"\n", " \"We are now the knights who say ekki-ekki-\"\n", " \"ekki-p'tang-zoom-boing-z'nourrwringmm!\")\n", "print(my_long_text)" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "We are no longer the knights who say Ni! We are now the knights who say ekki-ekki-ekki-p'tang-zoom-boing-z'nourrwringmm!\n" ] } ], "prompt_number": 12 }, { "cell_type": "code", "collapsed": false, "input": [], "language": "python", "metadata": {}, "outputs": [] } ], "metadata": {} } ] }