Tuesday, December 11, 2012

2 Python Style Rules

source: http://google-styleguide.googlecode.com/svn/trunk/pyguide.html#True/False_evaluations

1. Semicolons
Do not terminate your lines with semi-colons and do not use semi-colons to put two commands on the same line.
2. Line Length

Maximum line length is 80 characters.

Exception: lines importing modules may end up longer than 80 characters only if using Python 2.4 or earlier.
Do not use backslash line continuation.
Make use of Python's implicit line joining inside parentheses, brackets and braces. If necessary, you can add an extra pair of parentheses around an expression.
Yes: foo_bar(self, width, height, color='black', design=None, x='foo',
             emphasis=None, highlight=0)

     if (width == 0 and height == 0 and
         color == 'red' and emphasis == 'strong'):
When a literal string won't fit on a single line, use parentheses for implicit line joining.
x = ('This will build a very long long ' 
     'long long long long long long string')

3. Parenthesis

It is not C++: Use parentheses sparingly.

Do not use them in return statements or conditional statements unless using parentheses for implied line continuation. (See above.) It is however fine to use parentheses around tuples.
Yes: if foo:
         bar()
     while x:
         x = bar()
     if x and y:
         bar()
     if not x:
         bar()
     return foo
     for (x, y) in dict.items(): ...
No:  if (x):
         bar()
     if not(x):
         bar()
     return (foo)

4. Indentation

Indent your code blocks with 4 spaces. (PyCharm does not use tab character by default, you can use tab but Pycharm interpreted as spaces rather than tab character)

Never use tabs or mix tabs and spaces. In cases of implied line continuation, you should align wrapped elements either vertically, as per 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.

       # 4-space hanging indent; nothing on first line
       foo = long_function_name(
           var_one, var_two, var_three,
           var_four)
No:    # Stuff on first line forbidden
       foo = long_function_name(var_one, var_two,
           var_three, var_four)

       # 2-space hanging indent forbidden
       foo = long_function_name(
         var_one, var_two, var_three,
         var_four)

5. Blank Line
Two blank lines between top-level definitions, one blank line between method definitions.

6. Whitespace

Follow standard typographic rules for the use of spaces around punctuation.

No whitespace inside parentheses, brackets or braces.
Yes: spam(ham[1], {eggs: 2}, [])

No: spam( ham[ 1 ], { eggs: 2 }, [ ] )


No whitespace before a comma, semicolon, or colon. Do use whitespace after a comma, semicolon, or colon except at the end of the line.
Yes: if x == 4: print x, y x, y = y, x

No: if x == 4 : print x , y x , y = y , x
No whitespace before the open paren/bracket that starts an argument list, indexing or slicing.
Yes: spam(1)

No: spam (1)

Yes: dict['key'] = list[index]

No: dict ['key'] = list [index]
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.
Yes: x == 1  BOOLEAN
No:  x<1
Don't use spaces around the '=' sign when used to indicate a keyword argument or a default parameter value.
But for normal assignment "leave space"
Yes: def complex(real, imag=0.0): return magic(r=real, i=imag)
No:  def complex(real, imag = 0.0): return magic(r = real, i = imag)
Don't use spaces to vertically align tokens on consecutive lines, since it becomes a maintenance burden (applies to :#=, etc.):
Yes:
  foo = 1000  # comment
  long_name = 2  # comment that should not be aligned

  dictionary = {
      'foo': 1,
      'long_name': 2,
      }
No:
  foo       = 1000  # comment
  long_name = 2     # comment that should not be aligned

  dictionary = {
      'foo'      : 1,
      'long_name': 2,
      }

7. Shebang Line
#!/usr/bin/python.
normally no need

8. Comment
Be sure to use the right style for module, function, method and in-line comments.

Doc Strings
Python has a unique commenting style using doc strings. A doc string is a string that is the first statement in a package, module, class or function. These strings can be extracted automatically through the __doc__ member of the object and are used by pydoc. (Try running pydoc on your module to see how it looks.) Our convention for doc strings is to use the three double-quote format for strings. A doc string should be organized as a summary line (one physical line) terminated by a period, question mark, or exclamation point, followed by a blank line, followed by the rest of the doc string starting at the same cursor position as the first quote of the first line. 
""" XXXXXXXXXXXXXXXXXXXXX."""
blank line
<body> 
.....
</body>
Modules (current no need for me)
Every file should contain license boilerplate. Choose the appropriate boilerplate for the license used by the project (for example, Apache 2.0, BSD, LGPL, GPL)
Functions and Methods
As used in this section "function" applies to methods, function, and generators.
A function must have a docstring, UNLESS it meets all of the following criteria:
  • not externally visible
  • very short
  • obvious
Guideline for docstring of a function. A docstring should give enough information to write a call to the function without reading the function's code. A docstring should describe the function's calling syntax and its semantics, not its implementation. For tricky code, comments alongside the code are more appropriate than using docstrings.
Certain aspects of a function should be documented in special sections, listed below. Each section begins with a heading line, which ends with a colon. Sections should be indented two spaces, except for the heading.

For commercial functions and large software project
Args:
List each parameter by name. A description should follow the name, and be separated by a colon and a space. If the description is too long to fit on a single 80-character line, use a hanging indent of 2 or 4 spaces (be consistent with the rest of the file).The description should mention required type(s) and the meaning of the argument.
If a function accepts *foo (variable length argument lists) and/or **bar (arbitrary keyword arguments), they should be listed as *foo and **bar.
Returns: (or Yields: for generators)
Describe the type and semantics of the return value. If the function only returns None, this section is not required.
Raises:
List all exceptions that are relevant to the interface.
def fetch_bigtable_rows(big_table, keys, other_silly_variable=None):
    """Fetches rows from a Bigtable.

    Retrieves rows pertaining to the given keys from the Table instance
    represented by big_table.  Silly things may happen if
    other_silly_variable is not None.

    Args:
        big_table: An open Bigtable Table instance.
        keys: A sequence of strings representing the key of each table row
            to fetch.
        other_silly_variable: Another optional variable, that has a much
            longer name than the other args, and which does nothing.

    Returns:
        A dict mapping keys to the corresponding table row data
        fetched. Each row is represented as a tuple of strings. For
        example:

        {'Serak': ('Rigel VII', 'Preparer'),
         'Zim': ('Irk', 'Invader'),
         'Lrrr': ('Omicron Persei 8', 'Emperor')}

        If a key from the keys argument is missing from the dictionary,
        then that row was not found in the table.

    Raises:
        IOError: An error occurred accessing the bigtable.Table object.
    """
    pass

Classes
Classes should have a doc string below the class definition describing the class. If your class has public attributes, they should be documented here in an Attributes section and follow the same formatting as a function's Args section.
class SampleClass(object):
    """Summary of class here.

    Longer class information....
    Longer class information....

    Attributes:
        likes_spam: A boolean indicating if we like SPAM or not.
        eggs: An integer count of the eggs we have laid.
    """

    def __init__(self, likes_spam=False):
        """Inits SampleClass with blah."""
        self.likes_spam = likes_spam
        self.eggs = 0

    def public_method(self):
        """Performs operation blah."""

Block and Inline Comments
The final place to have comments is in tricky parts of the code. If you're going to have to explain it at the next code review, you should comment it now.  
# We use a weighted dictionary search to find out where i is in
# the array.  We extrapolate position based on the largest num
# in the array and the array size and then do binary search to
# get the exact number.

Complicated operations get a few lines of comments before the operations commence.

if i & (i-1) == 0:        # true iff i is a power of 2  Non-obvious ones get comments at the end of the line.


To improve legibility, these comments should be at least 2 spaces away from the code.
On the other hand, never describe the code. Assume the person reading the code knows Python (though not what you're trying to do) better than you do.
# BAD COMMENT: Now go through the b array and make sure whenever i occurs 
# the next element is i+1
^
Do not describe the code: The assumption here is that reader of your codes know how Python works, and I cannot insult the IQ of the reader by describing trivial things
Currently, the only thing that you need to do is to write the docstring summary line. Other detailed comments are not required when developing, but are when publishing

9. String Formatting

Use the % operator for formatting strings, even when the parameters are all strings. Use your best judgement to decide between + and % though.


  • use + when it is straightforward
  • use % formatting when it is complicated


Yes: x = a + b
     x = '%s, %s!' % (imperative, expletive)
     x = 'name: %s; score: %d' % (name, n)
No: x = '%s%s' % (a, b)  # use + in this case 
    x = imperative + ', ' + expletive + '!'  
    x = 'name: ' + name + '; score: ' + str(n)
Use list for string accumulation: Avoid using the + and += operators to accumulate a string within a loop. Since strings are immutable, this creates unnecessary temporary objects and results in quadratic rather than linear running time. Instead, add each substring to a list and ''.join the list after the loop terminates (or, write each substring to a cStringIO.StringIO buffer).
Yes: items = ['<table>']
     for last_name, first_name in employee_list:
         items.append('<tr><td>%s, %s</td></tr>' % (last_name, first_name))
     items.append('</table>')
     employee_table = ''.join(items)
No: employee_table = '<table>'
    for last_name, first_name in employee_list:
        employee_table += '<tr><td>%s, %s</td></tr>' % (last_name, first_name)
    employee_table += '</table>'
Use """ for multi-line strings rather than '''. Note, however, that it is often cleaner to use implicit line joining since multi-line strings do not flow with the indentation of the rest of the program:
Yes:
  print ("This is much nicer.\n"                      implicit line joining
         "Do it this way.\n")
  No:
    print """This is pretty ugly.
Don't do this.
"""

10. Closing files and sockets
Explicitly close files and sockets when done with them.


The preferred way to manage files is using the "with" statement:
with open("hello.txt") as hello_file:
    for line in hello_file:
        print line
For file-like objects that do not support the "with" statement, use contextlib.closing():
import contextlib

with contextlib.closing(urllib.urlopen("http://www.python.org/")) as front_page:
    for line in front_page:
        print line


11. TODO comments
Use TODO comments for code that is temporary, a short-term solution, or good-enough but not perfect.
example:

# TODO(kl@gmail.com): Use a "*" here for string repetition.
# TODO(Zeke) Change this to use relations.



12. Import 

Imports should be on separate lines.

E.g.:
Yes: import os
     import sys
No:  import os, sys
Imports are always put at the top of the file, just after any module comments and doc strings and before module globals and constants. Imports should be grouped with the order being most generic to least generic:
  1. standard library imports
  2. third-party imports
  3. application-specific imports
Within each grouping, imports should be sorted lexicographically, ignoring case, according to each module's full package path.
import foo
from foo import bar
from foo.bar import baz
from foo.bar import Quux
from Foob import ar



13. One statements per Line UNLESS Trivial

Generally only one statement per line.

However, you may put the result of a test on the same line as the test only if the entire statement fits on one line. In particular, you can never do so with try/except since the try andexcept can't both fit on the same line, and you can only do so with an if if there is no else.
Yes:

  if foo: bar(foo)
No:

  if foo: bar(foo)
  else:   baz(foo)

  try:               bar(foo)
  except ValueError: baz(foo)

  try:
      bar(foo)
  except ValueError: baz(foo)


14. Setter in the OOP
If an accessor function would be trivial you should use public variables instead of accessor functions to avoid the extra cost of function calls in Python. When more functionality is added you can use property to keep the syntax consistent.

Reason behind: it is Python (interpreted) not Java (Compiled)

15. Main()

Even a file meant to be used as a script should be importable and a mere import should not have the side effect of executing the script's main functionality. The main functionality should be in a main() function.

In Python, pycheckerpydoc, and unit tests require modules to be importable. Your code should always check if __name__ == '__main__' before executing your main program so that the main program is not executed when the module is imported.
def main():
      ...

if __name__ == '__main__':
    main()

All code at the top level will be executed when the module is imported. Be careful not to call functions, create objects, or perform other operations that should not be executed when the file is being pychecked or pydoced.

done

1 comment: