The Best of the Best Practices (BOBP) Guide for Python
A "Best of the Best Practices" (BOBP) guide to developing in Python.
- "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 (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.
Follow PEP 8, when sensible.
- Variables, functions, methods, packages, modules
- Classes and Exceptions
- Protected methods and internal functions
- Private methods
General Naming Guidelines
Avoid one-letter variables (esp.
Exception: In very short blocks, when the meaning is clearly visible from the immediate context
for e in elements: e.mutate()
Avoid redundant labeling.
import audio core = audio.Core() controller = audio.Controller()
import audio core = audio.AudioCore() controller = audio.AudioController()
Prefer "reverse notation".
elements = ... elements_active = ... elements_defunct = ...
elements = ... active_elements = ... defunct_elements ...
Avoid getter and setter methods.
person.age = 42
Use 4 spaces--never tabs. Enough said.
Import entire modules instead of individual symbols within a module. For example, for a top-level module
canteen that has a file
import canteen import canteen.sessions from canteen import sessions
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.
Put all imports at the top of the page with three sections, each separated by a blank line, in this order:
- System imports
- Third-party imports
- Local source tree imports
Rationale: Makes it clear where each module is coming from.
Use one-line docstrings for obvious functions.
"""Return the pathname of ``foo``."""
Multiline docstrings should include
- Summary line
- Use case, if appropriate
- Return type and semantics, unless
"""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>` """
- Use action words ("Return") rather than descriptions ("Returns").
__init__methods in the docstring for the class.
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
Use them sparingly. Prefer code readability to writing a lot of comments. Often, small methods are more effective than comments.
# If the sign is a stop sign if sign.color == 'red' and sign.sides == 8: stop()
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
Don't stress over it. 80-100 characters is fine.
Use parentheses for line continuations.
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." )
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 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".
- 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.
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 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.
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".