Documented here for the benefit of subcontractors and anyone curious.
Recent updates: abbreviations.
Documented here for the benefit of subcontractors and anyone curious.
Recent updates: abbreviations.
Most rules from PEP-8 apply, except:
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 if expressions 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 edit
if (user.logged_in and 'edit' in Role.get(user).permissions):
...
and this:
may_edit = user.logged_in and 'edit' in Role.get(user).permissions
if may_edit:
...
The second version is much better, because it’s easier to verify being correct, as it can be considered as two separate claims:
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.
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.
The following also applies to attribute, function and method names.
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.
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.
def sum_list(list):
r = 0
for item in list:
r += item
return r
def double_result(f):
@functools.wraps(f)
def wrapped(*args, **kw):
# demoing another use of `r` here
r = f(*args, **kw)
return r * 2
return wrapped
# note the use of `m` and `self`
def logged_method(m):
@functools.wraps(m)
def wrapped(self, *args, **kw):
r = m(self, *args, **kw)
self.log(m, args, kw, r)
return r
return wrapped
You should not invent __smth__ and similar just as PEP-8 says, but there’s an exception:
#
# 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.
For example:
def nop(*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.
Example:
print "[[[ %s-%s ]]]" % \
(var1, var2) # BAD -- newline escape
print ("[[[ %s-%s ]]]" %
(var1, var2)) # BAD -- closing bracket in the wrong place
print ("[[[ %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:
# BAD
def func(self, a, b, c, d,
e, f, g):
sum = a + b + c + d
sum += e + f + g
return sum
# GOOD
def func(self,
a, b, c, d,
e, f, g
):
sum = a + b + c + d
sum += e + f + g
return sum
# GOOD (note the added docstring)
def func(self,
a, b, c, d,
e, f, g
):
"""
Return a sum of unnecessarily named arguments
"""
sum = a + b + c + d
sum += e + f + g
return sum
__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 readable
items = lambda self: [
self.file,
self.tools,
self.help,
], # note indentation of this closing bracket -- lambda added one level
file = lambda self:
Menu("&File", [
# note the double quotes for menu item text
# and single quotes for icon spec and key
Menu("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')
def on_menu_copy_url(self):
WxClipboard.data = wx.URLDataObject('\n'.join(
map(self.generate_url,
filter(lambda item: item.is_file,
self.selected_objs()
)
)
))
# note how ending bracket on its own line makes the code so much more readable
if not (
(0 <= red <= 1)
and (0 <= green <= 1)
and (0 <= blue <= 1)
):
raise ValueError("Color component out of range")
# example of more than one keyword argument per line
create_frame(
allow_resize=True, allow_maximize=True,
posx=50, posy=100
)
def prepare_ctx_menu(self, menu):
items = ...
files = ...
menu.copy_url.enabled = bool(files)
menu.download.enabled = \
menu.delete.enabled = \
menu.properties.enabled = bool(items)
#menu. ...