{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Python Style Guide" ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
" ], "text/plain": [ "" ] }, "execution_count": 1, "metadata": {}, "output_type": "execute_result" } ], "source": [ "import addutils.toc ; addutils.toc.js(ipy_notebook=True)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "You will soon realize that the Python community often cite the *Pythonic approach to do things*.\n", "\n", "*'Pythonic'* refers to the principles laid out in *'The Zen of Python'*: a collection of basic coding rules to allow Python code to be more readable, maintainable and accessible to both novice and experienced developers. You can access *'The Zen of Python'* by typing at any time `import this` in any Python interpreter." ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [ { "data": { "text/html": [ "\n", "\n" ], "text/plain": [ "" ] }, "execution_count": 2, "metadata": {}, "output_type": "execute_result" } ], "source": [ "from addutils import css_notebook\n", "css_notebook()" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "The Zen of Python, by Tim Peters\n", "\n", "Beautiful is better than ugly.\n", "Explicit is better than implicit.\n", "Simple is better than complex.\n", "Complex is better than complicated.\n", "Flat is better than nested.\n", "Sparse is better than dense.\n", "Readability counts.\n", "Special cases aren't special enough to break the rules.\n", "Although practicality beats purity.\n", "Errors should never pass silently.\n", "Unless explicitly silenced.\n", "In the face of ambiguity, refuse the temptation to guess.\n", "There should be one-- and preferably only one --obvious way to do it.\n", "Although that way may not be obvious at first unless you're Dutch.\n", "Now is better than never.\n", "Although never is often better than *right* now.\n", "If the implementation is hard to explain, it's a bad idea.\n", "If the implementation is easy to explain, it may be a good idea.\n", "Namespaces are one honking great idea -- let's do more of those!\n" ] } ], "source": [ "import this" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 1 PEP8" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "PEP is an acronym for Python Enhancement Proposals. The whole list can be found [HERE](http://www.python.org/dev/peps/).\n", "\n", "[PEP8](http://www.python.org/dev/peps/pep-0008/) in particular contains coding convections and is perhaps the most explicit example of idioms within the Python community. In Computer Science, idioms are preferred ways of writing code (often there are many possible coding solutions to reach the same results, and some solutions are considered better than others, both for readability and performance reasons). We highly recommend to read PEP8 before start your career as a Python Programmer.\n", "It will be higly recommendable to use a PEP8 style-checking plugin to be shure your code is fully compliant with that rules." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 2 Syntax and Naming Conventions" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 2.1 Avoid using semicolons at the end of lines" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Even if allowed, python doesn't need it!" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [], "source": [ "# WRONG\n", "var = 15;\n", "s = \"I like semicolons\";\n", "a = 1; b =2; c = 3" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [], "source": [ "# CORRECT\n", "var = 15\n", "s = \"No semicolons!\"\n", "a, b, c = 1, 2, 3" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 2.2 Avoid having multiple statements on a single line" ] }, { "cell_type": "code", "execution_count": 6, "metadata": { "collapsed": true }, "outputs": [], "source": [ "condition = True\n", "# WRONG\n", "if condition: a = 1; b =2; c = 3\n", "\n", "# CORRECT\n", "if condition:\n", " a, b, c = 1, 2, 3" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 2.3 Indent your code block with 4 spaces" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Never use tabs or mix tabs and spaces. In cases of implied line continuation, you should align wrapped elements either vertically, as for the examples in the line length section; or using a hanging indent of 4 spaces, in which case there should be no argument on the first line." ] }, { "cell_type": "raw", "metadata": {}, "source": [ "# WRONG - Stuff on first line forbidden\n", "lfunc = long_function_name(var_one, var_two,\n", " var_three, var_four)\n", " \n", "# CORRECT - Aligned with opening delimiter\n", "lfunc = long_function_name(var_one, var_two,\n", " var_three, var_four)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 2.4 Imports: Should always be on separate lines, if refered to different modules." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Always on top of modules. Avoid **from module import *** if not strictly required. \n", "Imports should be grouped in the following order:\n", "\n", "* standard library imports\n", "* related third party imports\n", "* local application/library specific imports" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [], "source": [ "# WRONG \n", "import sys, os\n", "from math import *" ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [], "source": [ "# CORRECT\n", "import sys\n", "import os\n", "from math import gamma" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 2.5 Whitespace: required or not?" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "No spaces in parentheses, no whitespace before a comma, semicolon, or colon. Do use whitespace after a comma, semicolon, or colon except at the end of the line." ] }, { "cell_type": "raw", "metadata": {}, "source": [ "# WRONG\n", "spam( ham[ 1 ], { eggs: 2 }, [ ] )\n", " \n", "#CORRECT\n", "spam(ham[1], {eggs: 2}, [])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "No whitespace before the open paren/bracket that starts an argument list, indexing or slicing. " ] }, { "cell_type": "raw", "metadata": {}, "source": [ "# WRONG\n", "spam (1)\n", "dict ['key'] = list [index]\n", "\n", "# CORRECT\n", "spam(1)\n", "dict['key'] = list[index]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Don't use spaces around the **=** operator when used to indicate a keyword argument or a default parameter value." ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [], "source": [ "# WRONG\n", "def complex(real, imag = 0.0):\n", " return magic(r = real, i = imag)" ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [], "source": [ "# CORRECT\n", "def complex(real, imag=0.0):\n", " return magic(r=real, i=imag)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Surround binary operators with a single space on either side for assignment (**=**), comparisons (**==**, **<**, **>**, **!=**, **<>**, **<=**, **>=**, **in**, **not in**, **is**, **is not**), and Booleans (**and**, **or**, **not**). Use your better judgment for the insertion of spaces around arithmetic operators but always be consistent about whitespace on either side of a binary operator. \n", "```python\n", " # WRONG\n", " x<1\n", " \n", "\n", " # CORRECT\n", " x == 1\n", "``` " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Don't use spaces to vertically align tokens on consecutive lines, since it becomes a maintenance burden (applies to **:**, **#**, **=**, etc.): " ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [], "source": [ "# WRONG\n", "short = 1000 # comment\n", "long_name = 2 # comment that should not be aligned\n", "\n", "dictionary = {\n", " 'foo' : 1,\n", " 'long_name': 2,\n", "}" ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [], "source": [ "# CORRECT\n", "short = 1000 # comment\n", "long_name = 2 # comment that should not be aligned\n", "\n", "dictionary = {\n", " 'foo': 1,\n", " 'long_name': 2,\n", "}" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 2.6 Naming: names in python should be chosen according to the following conventions:" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "* object naming: \n", "`module_name, package_name, ClassName, method_name, ExceptionName, function_name, GLOBAL_CONSTANT_NAME, global_var_name, instance_var_name, function_parameter_name, local_var_name`. \n", "* avoid single character names except for counters or iterators\n", "* avoid dashes (-) in any package/module name\n", "* `__double_leading_and_trailing_underscore` names (reserved by Python)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 3 Working with Data" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 3.1 Avoid using a temporary variable when swapping two variables" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "There is no reason to swap using a temporary variable in Python. We can use tuples to make our code more readable" ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [], "source": [ "# WRONG - Swap two variables using a temporary variable\n", "a, b = 10, 20\n", "temp = a\n", "a = b\n", "b = temp" ] }, { "cell_type": "code", "execution_count": 14, "metadata": {}, "outputs": [], "source": [ "# CORRECT - Swap two variables using tuples\n", "(a,b) = (b,a) # Parentesis can be omitted" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 3.2 Use tuples to unpack data" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "In Python, it is possible to 'pack' and 'unpack' data. This is mostly used while passing multiple values to functions." ] }, { "cell_type": "code", "execution_count": 15, "metadata": {}, "outputs": [], "source": [ "# WRONG - Unpack values with single instructions\n", "packed = ['car', 'Cevy', 160] \n", "vehicle = packed[0]\n", "make = packed[1]\n", "speed = packed[2]" ] }, { "cell_type": "code", "execution_count": 16, "metadata": {}, "outputs": [], "source": [ "# CORRECT - Unpack values using tuples\n", "packed = ['car', 'Cevy', 160] \n", "(vehicle, make, speed) = packed" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 3.3 Strings: Use join and list to create a string from a list and viceversa" ] }, { "cell_type": "code", "execution_count": 17, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "['t', 'h', 'i', 's', ' ', 'w', 'i', 'l', 'l', ' ', 'b', 'e', ' ', 'a', ' ', 'l', 'i', 's', 't']\n" ] } ], "source": [ "mylist = ['u', 's', 'e', ' ', 'j', 'o', 'i', 'n', '(', '', ')']\n", "# WRONG\n", "mystr = ''\n", "for letter in mylist:\n", " mystr = mystr + letter\n", " \n", "# CORRECT\n", "mystr = ''.join(mylist)\n", "\n", "# CORRECT: generate a list from a string\n", "mylist2 = list('this will be a list')\n", "print(mylist2)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 3.4 Strings: Use startswith and endswith instead of string slicing to check for prefixes or suffixes." ] }, { "cell_type": "code", "execution_count": 18, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "True\n", "True\n" ] } ], "source": [ "mystr = 'Check me'\n", "# WRONG\n", "print(mystr[:5] == 'Check')\n", "\n", "# CORRECT\n", "print(mystr.startswith('Check'))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 3.5 Dictionaries: Use the default parameter of dict.get to provide default values" ] }, { "cell_type": "code", "execution_count": 19, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "0.1\n" ] } ], "source": [ "# WRONG\n", "power = dict([('on', 100), ('off', 0)])\n", "if 'idle' in power:\n", " print(power['idle'])\n", "else:\n", " print(0.1)" ] }, { "cell_type": "code", "execution_count": 20, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "0.1\n" ] } ], "source": [ "# CORRECT\n", "print(power.get('idle', 0.1))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 3.6 File IO: Use Context Managers to ensure resources are properly managed" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ " # WRONG - If the function 'do_something' generates an exception\n", " # the file will never be closed\n", " file_handle = open('myfile', 'r')\n", " for line in file_handle.readlines():\n", " do_something(line):\n", " file_handle.close()\n", "\n", " # CORRECT - Using 'with' you don''t need to explicitly call 'close'\n", " with open('myfile', 'r') as file_handle:\n", " for line in file_handle.readlines():\n", " do_something(line):" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 4 Control Structures" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 4.1 Don't compare boolean values to True or False using ==" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ " # WRONG\n", " if condition == True:\n", " if condition is True:\n", "\n", " # CORRECT\n", " if condition:" ] }, { "cell_type": "code", "execution_count": 21, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "There is something to do!\n" ] } ], "source": [ "# Remember that empty strings, lists or tuples are false\n", "mylist = [1, 2, 3]\n", "if mylist:\n", " print('There is something to do!')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 4.2 Avoid placing conditional branch on the same line as the colon" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ " # WRONG\n", " if condition: print (something)\n", "\n", " # CORRECT\n", " if condition:\n", " print (something)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 4.3 Comparisons to singletons like None should always be done with is or is not." ] }, { "cell_type": "code", "execution_count": 22, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "This is wrong\n", "This is correct\n" ] } ], "source": [ "# WRONG\n", "mytest = None\n", "if mytest == None:\n", " print('This is wrong')\n", "\n", "# CORRECT\n", "if mytest is None:\n", " print('This is correct')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 4.4 Avoid repeating variable name in compound if Statement" ] }, { "cell_type": "code", "execution_count": 23, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "This works but it's wrong\n", "This is correct!\n" ] } ], "source": [ "a = 'two'\n", "# WRONG\n", "if a == 'one' or a == 'two' or a == 'three':\n", " print(\"This works but it's wrong\")\n", "\n", "# CORRECT\n", "if a in ('one', 'two', 'three'):\n", " print('This is correct!')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 4.5 Use list comprehensions to create lists that are subsets of existing data" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "List comprehensions, when used judiciously, increase clarity in code that builds a list from existing data. Moreover list comprehensions can speed-up the code." ] }, { "cell_type": "code", "execution_count": 24, "metadata": {}, "outputs": [], "source": [ "# WRONG\n", "origin = range(1, 100)\n", "mylist = list()\n", "for item in origin:\n", " if not item%5:\n", " mylist.append(item+0.1)\n", "\n", "# CORRECT\n", "mylist = [item+0.1 for item in origin if not item%5]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 4.6 Indexes in for Loops" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Programmers coming from other languages are used to iterate over a container by accessing elements via index. Python's **in** keyword handles this gracefully." ] }, { "cell_type": "code", "execution_count": 25, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "abc123\n", "\n", "abc123" ] } ], "source": [ "mylist = ['a', 'b', 'c', 1, 2, 3]\n", "# WRONG\n", "index = 0\n", "while index < len(mylist):\n", " print((mylist[index]), end=''),\n", " index+=1\n", " \n", "print('\\n')\n", "\n", "# CORRECT\n", "for item in mylist:\n", " print(item, end='')" ] }, { "cell_type": "code", "execution_count": 26, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "0 a -- 1 b -- 2 c -- 3 1 -- 4 2 -- 5 3 -- " ] } ], "source": [ "# CORRECT - When the index is needed for any reason, use 'enumerate'\n", "for index, item in enumerate(mylist):\n", " print(index, item, ' -- ', end='')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 4.7 `range` and `xrange`" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "In Python e3 `range()` now behaves like `xrange()` used to behave, except it works with values of arbitrary size. The latter no longer exists." ] }, { "cell_type": "code", "execution_count": 27, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Sum1: 312,499,987,500,000\n" ] } ], "source": [ "# IN PYTHON 3 THIS IS CORRECT\n", "sum1 = 0\n", "for x in range(5000*5000):\n", " sum1 = sum1+x\n", "print('Sum1: {0:,d}'.format(sum1))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "---\n", "\n", "Visit [www.add-for.com]() for more tutorials and updates.\n", "\n", "This work is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License." ] } ], "metadata": { "kernelspec": { "display_name": "Python [conda env:addfor_tutorials]", "language": "python", "name": "conda-env-addfor_tutorials-py" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.6.4" } }, "nbformat": 4, "nbformat_minor": 1 }