Skip to content

Instantly share code, notes, and snippets.

@yourmoonlight
Forked from sloria/bobp-python.md
Created March 22, 2016 16:33
Show Gist options
  • Save yourmoonlight/643fc8aab0f991f091f8 to your computer and use it in GitHub Desktop.
Save yourmoonlight/643fc8aab0f991f091f8 to your computer and use it in GitHub Desktop.

Revisions

  1. @sloria sloria created this gist Oct 16, 2013.
    300 changes: 300 additions & 0 deletions bobp-python.md
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,300 @@
    # The Best of the Best Practices (BOBP) Guide for Python

    A "Best of the Best Practices" (BOBP) guide to developing in Python.

    ## In General

    ### Values

    - "Build tools for others that you want to be built for you." - Kenneth Reitz
    - "Simplicity is alway better than functionality." - Pieter Hintjens
    - "Fit the 90% use-case. Ignore the nay sayers." - Kenneth Reitz
    - "Beautiful is better than ugly." - [PEP 20][]
    - Build for open source (even for closed source projects).

    ### General Development Guidelines

    - "Explicit is better than implicit" - [PEP 20][]
    - "Readability counts." - [PEP 20][]
    - "Anybody can fix anything." - [Khan Academy Development Docs][]
    - Fix each [broken window](http://www.artima.com/intv/fixit2.html) (bad design, wrong decision, or poor code) *as soon as it is discovered*.
    - "Now is better than never." - [PEP 20][]
    - Test ruthlessly. Write docs for new features.
    - Even more important that Test-Driven Development--*Human-Driven Development*
    - These guidelines may--and probably will--change.

    ## In Particular

    ### Style

    Follow [PEP 8][], when sensible.

    #### Naming

    - Variables, functions, methods, packages, modules
    - `lower_case_with_underscores`
    - Classes and Exceptions
    - `CapWords`
    - Protected methods and internal functions
    - `_single_leading_underscore(self, ...)`
    - Private methods
    - `__double_leading_underscore(self, ...)`
    - Constants
    - `ALL_CAPS_WITH_UNDERSCORES`

    ###### General Naming Guidelines

    Avoid one-letter variables (esp. `l`, `O`, `I`).

    *Exception*: In very short blocks, when the meaning is clearly visible from the immediate context

    **Fine**
    ```python
    for e in elements:
    e.mutate()
    ```

    Avoid redundant labeling.

    **Yes**
    ```python
    import audio

    core = audio.Core()
    controller = audio.Controller()
    ```

    **No**
    ```python
    import audio

    core = audio.AudioCore()
    controller = audio.AudioController()
    ```

    Prefer "reverse notation".

    **Yes**
    ```python
    elements = ...
    elements_active = ...
    elements_defunct = ...
    ```

    **No**
    ```python
    elements = ...
    active_elements = ...
    defunct_elements ...
    ```

    Avoid getter and setter methods.

    **Yes**
    ```python
    person.age = 42
    ```

    **No**
    ```python
    person.set_age(42)
    ```

    #### Indentation

    Use 4 spaces--never tabs. Enough said.

    #### Imports

    Import entire modules instead of individual symbols within a module. For example, for a top-level module `canteen` that has a file `canteen/sessions.py`,

    **Yes**

    ```python
    import canteen
    import canteen.sessions
    from canteen import sessions
    ```

    **No**

    ```python
    from canteen import get_user # Symbol from canteen/__init__.py
    from canteen.sessions import get_session # Symbol from canteen/sessions.py
    ```

    *Exception*: For third-party code where documentation explicitly says to import individual symbols.

    *Rationale*: Avoids circular imports. See [here](https://sites.google.com/a/khanacademy.org/forge/for-developers/styleguide/python#TOC-Imports).

    Put all imports at the top of the page with three sections, each separated by a blank line, in this order:

    1. System imports
    2. Third-party imports
    3. Local source tree imports

    *Rationale*: Makes it clear where each module is coming from.

    #### Documentation

    Follow [PEP 257][]'s docstring guidelines. [reStructured Text](http://docutils.sourceforge.net/docs/user/rst/quickref.html) and [Sphinx](http://sphinx-doc.org/) can help to enforce these standards.

    Use one-line docstrings for obvious functions.

    ```python
    """Return the pathname of ``foo``."""
    ```

    Multiline docstrings should include

    - Summary line
    - Use case, if appropriate
    - Args
    - Return type and semantics, unless ``None`` is returned

    ```python
    """Train a model to classify Foos and Bars.
    Usage::
    >>> import klassify
    >>> data = [("green", "foo"), ("orange", "bar")]
    >>> classifier = klassify.train(data)
    :param train_data: A list of tuples of the form ``(color, label)``.
    :rtype: A :class:`Classifier <Classifier>`
    """
    ```

    Notes

    - Use action words ("Return") rather than descriptions ("Returns").
    - Document `__init__` methods in the docstring for the class.

    ```python
    class Person(object):
    """A simple representation of a human being.
    :param name: A string, the person's name.
    :param age: An int, the person's age.
    """
    def __init__(self, name, age):
    self.name = name
    self.age = age
    ```

    ##### On comments

    Use them sparingly. Prefer code readability to writing a lot of comments. Often, small methods are more effective than comments.

    *No*

    ```python
    # If the sign is a stop sign
    if sign.color == 'red' and sign.sides == 8:
    stop()
    ```

    *Yes*

    ```python
    def is_stop_sign(sign):
    return sign.color == 'red' and sign.sides == 8

    if is_stop_sign(sign):
    stop()
    ```

    When you do write comments, remember: "Strunk and White apply." - [PEP 8][]

    #### Line lengths

    Don't stress over it. 80-100 characters is fine.

    Use parentheses for line continuations.

    ```python
    wiki = (
    "The Colt Python is a .357 Magnum caliber revolver formerly manufactured "
    "by Colt's Manufacturing Company of Hartford, Connecticut. It is sometimes "
    'referred to as a "Combat Magnum". It was first introduced in 1955, the '
    "same year as Smith & Wesson's M29 .44 Magnum."
    )
    ```

    ### Testing

    Strive for 100% code coverage, but don't get obsess over the coverage score.

    #### General testing guidelines

    - Use long, descriptive names. This often obviates the need for doctrings in test methods.
    - Tests should be isolated. Don't interact with a real database or network. Use a separate test database that gets torn down or use mock objects.
    - Prefer [factories](https://github.com/rbarrois/factory_boy) to fixtures.
    - Never let incomplete tests pass, else you run the risk of forgetting about them. Instead, add a placeholder like `assert False, "TODO: finish me"`.

    #### Unit Tests

    - Focus on one tiny bit of functionality.
    - Should be fast, but a slow test is better than no test.
    - It often makes sense to have one testcase class for a single class or model.

    ```python
    import unittest
    import factories

    class PersonTest(unittest.TestCase):
    def setUp(self):
    self.person = factories.PersonFactory()

    def test_has_age_in_dog_years(self):
    self.assertEqual(self.person.dog_years, self.person.age / 7)
    ```

    #### Functional Tests

    Functional tests are higher level tests that are closer to how an end-user would interact with your application. They are typically used for web and GUI applications.

    - Write tests as scenarios. Testcase and test method names should read like a scenario description.
    - Use comments to write out stories, *before writing the test code*.

    ```python
    import unittest

    class TestAUser(unittest.TestCase):

    def test_can_write_a_blog_post(self):
    # Goes to the her dashboard
    ...
    # Clicks "New Post"
    ...
    # Fills out the post form
    ...
    # Clicks "Submit"
    ...
    # Can see the new post
    ...
    ```

    Notice how the testcase and test method read together like "Test A User can write a blog post".


    ## Inspired by...

    - [PEP 20 (The Zen of Python)][PEP 20]
    - [PEP 8 (Style Guide for Python)][PEP 8]
    - [The Hitchiker's Guide to Python][python-guide]
    - [Khan Academy Development Docs][]
    - [Python Best Practice Patterns][]
    - [Pythonic Sensibilities][]
    - [The Pragmatic Programmer][]
    - and many other bits and bytes

    [Pythonic Sensibilities]: http://www.nilunder.com/blog/2013/08/03/pythonic-sensibilities/
    [Python Best Practice Patterns]: http://youtu.be/GZNUfkVIHAY
    [python-guide]: http://docs.python-guide.org/en/latest/
    [PEP 20]: http://www.python.org/dev/peps/pep-0020/
    [PEP 257]: http://www.python.org/dev/peps/pep-0257/
    [PEP 8]: http://www.python.org/dev/peps/pep-0008/
    [Khan Academy Development Docs]: https://sites.google.com/a/khanacademy.org/forge/for-developers
    [The Pragmatic Programmer]: http://www.amazon.com/The-Pragmatic-Programmer-Journeyman-Master/dp/020161622X/ref=sr_1_1?ie=UTF8&qid=1381886835&sr=8-1&keywords=pragmatic+programmer