Python Code Formatting Standards
Python Coding Guidelines
These guidelines should be followed when writing Python code. Patches and additions to any code base will be checked for adherence to these guidelines. If code is in violation, you will be asked to reformat it.
PEP-8
For most styling practices, follow the official PEP-8 Python style guide.
Exceptions to PEP-8
Line length
We use a maximum 100 character line length, or 99 characters + a newline.
(The only exception to this rule is configuring URL routing, e.g. in Django or Flask. In this case, readability of URL routes is significantly increased if each urlpattern
is on a single line.)
Continuing long lines
This should be less of a problem with a 100 character max, but you should always aim for readability.
-
For function or class definitions, use aligning indentation:
def create_user(self, email, password=None, first_name=None, last_name=None): class InstitutionSerializer(my_app.CustomMixin, serializers.ModelSerializer):
-
For function calls, list literals, tuple literals with short items, open on the beginning line, indent one level, and close at the end of the list:
user = self.model( email=email.lower(), first_name=first_name, last_name=last_name) fields = ( 'id', 'name', 'city', 'admissions_address', 'zipcode', 'population', 'is_valid') new_list = [ vegetable.is_pickle(brine) for vegetable in brine_function(veggie_list)]
-
For function calls with complicated keyword args, lists or tuples with long items, or dictionaries, use a trailing close:
fields = ( 'really really long item number one', 'really really long item number two', 'really really long item number three', 'really really long item number four', ) context = { 'icon_image_url': USER_ICON, 'subject': 'New Follower', 'first_name': user_profile.get_first_name(), 'profile_full_name': other_profile.get_name(), 'profile_first_name': other_profile.get_first_name(), 'profile_image_url': other_profile.get_profile_photo(), 'profile_url': 'https://{0}/community/profile/{1}'.format(DOMAIN_NAME, extra.pk), 'generic_image_url': GENERIC_AVATAR, } email = EmailMultiAlternatives( to=[user.username], subject=subject, body=text, )
Blank lines
-
As noted in PEP-8, there should be two blank lines before each class declaration and top-level function definition. Add one blank like between method definitions.
-
To aid in readability, add a blank around complicated
if/elif/else
blocks,try/except/finally
blocks, or other logical blocks of code. -
To make scanning a large file for class declarations easier, add a blank line after each class declaration:
class ListCreateRackItem(generics.ListCreateAPIView): model = RackItem def get_serializer_class(self): if self.request.method == 'POST': return NewRackItemSerializer return RackItemSerializer def get_queryset(self): return RackItem.objects.filter(shopper=self.request.user)
-
NB: The above rule does not apply to
class Meta
declarations.
Single vs double quotes
PEP-8 does not specify which to prefer, instead recommending to “[p]ick a rule and stick to it.” In general, you should use single quotes around strings (as the Python interpreter will do), except where using double quotes would avoid escaping a lot of single quotes, thereby “improving readability.” Per PEP-8, “[f]or triple-quoted strings, always use double quote characters to be consistent with the docstring convention in PEP-257.”
Ex:
my_dict['my_key'] = 'my_value'
sample = 'This is a string.'
double_sample = "I've double quoted this string, can't you see that it's better?"
"""
This is a doc string.
"""
Dealing with long if
clauses
Break up long if
clauses by assigning logical parts to variables. Then test the logic of the individual variables.
NO:
if (waist > shoulder * 0.75 and waist <= shoulder * 1.05) or (waist > hips * 0.75 and waist <= hips * 1.05):
do_something()
YES:
this = waist > shoulder * 0.75 and waist <= shoulder * 1.05
that = waist > hips * 0.75 and waist <= hips * 1.05
if this or that:
do_something()
Avoid for/else
and while/else
Python allows the construction of for/else
and while/else
loops where the else
clause is executed if the loop ends normally without encountering a break
.
Ex:
for thing in container:
if something_about(thing):
# Found it!
do_something(thing)
break
else:
# Didn't find it..
no_such_thing()
While offering some syntactic advantages, the syntax is confusing and even core Python developers believe it probably should not be used.
Do not use else
clauses on your for
or while
loops. It will likely result in subtle bugs that are difficult to debug. It also makes code review harder, since the reviewing may have to try and guess at the intention of the code if the use of else
may be ambiguous (e.g., near a for
loop nested in an if
conditional).
Class inheritance and using mixins
"Mixins" are typically small classes that encapsulate oft-used functionality to add to other base classes, used to keep code "DRY". These are particularly liberally used along with class-based views in web frameworks like Django and Django REST Framework. Python allows a class to inherit from multiple classes, but the order of inheritance might seem counterintuitive if you aren't familiar with Python internals. Just try and commit these two basic rules to memory:
- Mixins should always inherit from Python's root
object
class. - Base class goes on the right, mixins go on the left.
For example:
from django.db import models
class PrettyPrintModelMixin(object):
def pretty_print(self):
print '** {0}: {1} **'.format(self.__class__.__name__, self.name)
class MyFancyModel(PrettyPrintModelMixin, models.Model):
name = models.CharField()
timestamp = models.DateTimeField(auto_now_add=True)
If you are interacting with this model in the Python interpreter, you might see:
>>> fancy = MyFancyModel(name='Beetlejuice')
>>> fancy.pretty_print()
** MyFancyModel: Beetlejuice **
Imports
As noted in PEP-8, imports should generally come one on a line. When importing just a few classes or functions from a module, you can list them on a single line like so:
from django.db.models import Q, Max, Count
Sometimes verbose class names make it difficult to keep things on a single line. You can use the following line continuation:
from shopper.serializers import (
ShopperSerializer, RegisterFacebookSerializer, RegisterTwitterSerializer,
RegisterTiooSerializer, ShopperWithTokenSerializer, RequestPasswordResetSerializer)
If you're importing more than a few classes/methods and require more than three lines total, either import the entire module or consider a refactor to make the code more succinct and clear. Typing out the full namespace can become cumbersome or verbose; in such a case, use import ... as
, e.g.:
import yezzimixins as mxn
This allows you to do something like the following:
class MyView(mxn.FormatMixin, mxn.LocaleMixin):
It's also helpful to group related imports into import "blocks". You should group Django-specific, Django REST Framework-specific, and app-specific imports together, followed by general Python module imports—in that order. A single blank line should separate each block as shown below:
from django.core.urlresolvers import reverse
from rest_framework import status
from rest_framework.test import APITestCase
from models import Shopper
from tokens.models import FacebookToken, PasswordResetToken
from mock import patch
import requests