79-character limit doesn’t really matter in some cases like long error or log messages, in those cases the limit is more like 100 chars. The code itself however should generally be restricted to 80-ish chars.
The rules from Other Recommendations section of PEP-8 are mandatory. Don’t ever use semicolons or one-line if statements. On a related note, use ifexpressions only if they fit on one line.
Many of these rules only apply when in doubt. It’s hard to read source if the code style varies for no reason, but having just 75% of code conform to the same rules makes it so much more pleasant to navigate. 100% compliance to the rules is not required and is simply unnecessary. Still, having a habit of writing certain constructs in the same way saves time even when writing it, and a lot more when reading.
Also, whenever you can edit your source code not to require any comments — that’s much better than the best of comments or docstrings. Consider this:
# check if user is allowed to editif(user.logged_inand'edit'inRole.get(user).permissions):...
The second version is much better, because it’s easier to verify being correct, as it can be considered as two separate claims:
that we decide if the user can edit based on criteria on line 1
that the code in that if suite should only be executed if user may edit
This approach is much better than writing “clever” code that needs comments to explain what is it doing. Let the code explain itself.
That’s much easier to accomplish when you adopt some more high-level coding style. In other words if you name classes and names in such a way it’s easy to understand the API just by reading the names you’ll need fewer comments. This makes the code concise in a good way.
Writing your classes and packages to behave in similar ways helps save time on reading (and writing!) docs, debugging and adding features. This makes your code a little bit more boring, but I think we all would rather avoid the excitement of figuring something out, if that could have been made trivial in the first place. Please do repeat yourself whenever you did something well. Also repeat others as that’s how one learns. “Don’t repeat yourself” (aka DRY principle) only applies if what you tend to do is better avoided.
You will never get any good at cooking if you never cooks the same thing twice. The same applies to all occupations and programming is no different. A lot of practice and repeated attempts are required for attaining any level of skill and producing consistently high-quality code in any area without that preparation is simply impossible.
Code reuse is a good thing, but lets not forget that when programming we repeat ourselves in certain ways all the time, and intoducing a generic solution will quite often increase the amount of code one needs to write to do the same thing as before. Even if that is not the case, one should consider the time spent learning this new system and how proficient one would be with it compared to a simpler or more narrowly targetted implementation. People often blindly accept the claim that frameworks save them from repeating themselves, but at the same time one repeats the interaction with that framework again and again anyway, so it’s a choice between two cases of repetition and should be considered as such.
Apparently people get bored writing defs and ifs all the time, but a couple of ifs is usually better than a dict of undecipherable regular expressions and a 50Kb dependency.
Anyway, writing consitently good code is what matters in the end and a coding style is a good start.
String quoting
Use single-quoted strings for internal values and non-text.
Use double-quotes for strings that in some form will be output to the user.
For example:
getattr(ob,'attribute')struct.unpack('iii',data)print"Hello world"log.warning("Could not connect to %s",host)
This might seem unnecessary but in fact it helps grasp semantics of each string faster and you can adjust source code highlighting to further help you with that. It’s a very simple rule but more effective than it might seem at first.
Variable names
The following also applies to attribute, function and method names.
Always use underscore to separate word in composite names, even if you define a new function that could be named the same way as something in stdlib:
Also, when using composite variable names there is an issue of word order. For example an attribute holding an item class can be named item_cls or cls_item. If the name is one of a kind, natural word order should be preserved, but if there are a few related names sharing one of the words, then that word should always be placed first. So if there’s an OK button and a Cancel button, then they should be stored in btn_ok and btn_cancel attributes.
Abbreviations
Having a system of consistent abbreviations to use in variable names helps a lot when reading the code later and saves time when first naming them.
prefixes and suffixes: dir (directory), tmpl (template), impl (implementation), btn_* (button), chk_* (checkbox), *_mw (middleware)
callbacks: cb (callback) or, for more informative names on_click, on_close or cb_finished etc
fn for full filename, f for open file
_re_* for regexp strings, _rx_* for compiled regexps, m for matches
when working with etrees use elem for unknown elements and xroot, xbody, xrecord etc for elements of known name. This is very useful if you need to extract, for example, body: the body will be stored in variable named body and to avoid the clash the element it is being extracted from would be named xbody
r for future return value. For example:
defsum_list(list):r=0foriteminlist:r+=itemreturnr
Decorators
Use f, func, m or method for decorator argument, f is fine most of the time but func is a preferred attribute name for storing original function in decorators implemented as classes.
Use wrapped for closure names in decorators. For example:
defdouble_result(f):@functools.wraps(f)defwrapped(*args,**kw):# demoing another use of `r` herer=f(*args,**kw)returnr*2returnwrapped# note the use of `m` and `self`deflogged_method(m):@functools.wraps(m)defwrapped(self,*args,**kw):r=m(self,*args,**kw)self.log(m,args,kw,r)returnrreturnwrapped
Magic method names
You should not invent __smth__ and similar just as PEP-8 says, but there’s an exception:
Use the __smth__ method names for the [http://pypi.python.org/pypi/mext.dispatch mext.dispatch] adapters when appropriate. The name should be short, but informative of target type / interface.
Class names
Use CamelCase for every class name with exception of decorator implementations.
Abbreviations are treated as words. XmlText not XMLText, TcpIpDetector not TCPIPDetector, MimeType, not MIMEType.
If you’re forming a new abbreviation (e.g. when shortening a class name) use caps. However when reusing that name as part of a new one, treat it as a regular word again.
So, for example, if you have class TextuallyDefinedLanguageFormat it’s OK to make it class TDLF, but when reusing that abbreviation later, instead of TDLFParser make it TdlfParser.
Imports
import sys, os and similar is OK. Just make sure only stdlib imports are on the same line.
Do from mod import * if you use more than 3-4 items from that namespace
Instead of littering your code with os.path.dirname(os.path.abspath(fn)) do from os.path import * and then dirname(abspath(fn))
Always have an __all__ in all your modules. If unsure of what to put there, just make it __all__ = [] and fill it in later, based on usage.
Group items in __all__ logically
Comments formatting
Unlike what PEP-8 says, you should not use two spaces after a sentence-ending period in comments.
Comments explain code block below it. Comments are usually used for non-trivial blocks, so this way one reads them with better understanding of their purpose.
But if short enough, comment can be on the same line as the code.
I think those half-sentence comments are fine without first word capitalization or period.
Leave some space on both sides of the hash character.
Avoid padding with empty comment lines. For example:
## The following code does something# that is not trivial and warrants# some explanation...## That comment above can be improved -- first and last line of that# comment are empty and only add noise. Pad the comment with completely# blank lines instead.
Docstrings
Docstrings are triple double-quoted with no text on the lines of opening and closing quotes.
The text of the docstring must be indented.
Examples in the docstrings should be indented yet more
Do not write docstrings for documentation-generators, write them for people who will read the source code.
All of this applies to one-liners too (they become three-liners :)
When not in conflict with the above, rules from PEP-257 apply.
For example:
defnop(*args,**kw):""" Take arguments and do nothing. For example: nop(nop, nop()) """pass
Just like usual block indentation it helps discern logical blocks and makes reading source code much more pleaseant and fast.
Long expressions and argument lists
Don’t use newline escape, use brackets.
Operators go to the next line, before their second argument.
The contents of brackets should be indented one level with closing bracket on its own line, unindented.
The opening bracket should generally be immediately followed by newline.
But if the first item / argument is short enough it can be left on the first line (this does not apply to arguments passed by keyword).
Every keyword argument should get its own line with few exceptions
Consider using spaces on both sides of ’=’ signs to improve readability in these cases
Example:
print"[[[ %s-%s ]]]"% \
(var1,var2)# BAD -- newline escapeprint("[[[ %s-%s ]]]"%(var1,var2))# BAD -- closing bracket in the wrong placeprint("[[[ %s-%s ]]]"%(var1,var2)# BAD -- the operator is on the wrong line)print("[[[ %s-%s ]]]"%(var1,var2)# GOOD)
The only reason that looks silly is that there was no real need for line break in the first place. Look for real examples below.
Another example, which also shows why the last bracket rule matters:
# BADdeffunc(self,a,b,c,d,e,f,g):sum=a+b+c+dsum+=e+f+greturnsum# GOODdeffunc(self,a,b,c,d,e,f,g):sum=a+b+c+dsum+=e+f+greturnsum# GOOD (note the added docstring)deffunc(self,a,b,c,d,e,f,g):""" Return a sum of unnecessarily named arguments """sum=a+b+c+dsum+=e+f+greturnsum
Examples from real code
__all__=['STMHistory','Controller','LocalController','LockingController','AbstractSubject','AbstractListener','Link','CircularityError',]make.attrs(# note the spaces around the '=' sign, if argument passing takes# a lot of lines those spaces make it more readableitems=lambdaself:[self.file,self.tools,self.help,],# note indentation of this closing bracket -- lambda added one levelfile=lambdaself:Menu("&File",[# note the double quotes for menu item text# and single quotes for icon spec and keyMenu("Upload files",Icon('nav_upload_files'),key='upload_files'),Menu("Upload folder",Icon('nav_upload_folder'),key='upload_folder'),]),#...)# next example is borderline -- it's kept as one construct only for# demonstration purposes@event('menu','copy_url')defon_menu_copy_url(self):WxClipboard.data=wx.URLDataObject('\n'.join(map(self.generate_url,filter(lambdaitem:item.is_file,self.selected_objs()))))# note how ending bracket on its own line makes the code so much more readableifnot((0<=red<=1)and(0<=green<=1)and(0<=blue<=1)):raiseValueError("Color component out of range")# example of more than one keyword argument per linecreate_frame(allow_resize=True,allow_maximize=True,posx=50,posy=100)
Exception to the “no newline escapes” rule
Newline escape is OK for certain assign statements, but make sure to use indentation