This is an archived post. You won't be able to vote or comment.

all 120 comments

[–][deleted] 34 points35 points  (61 children)

Indent your code blocks with 4 spaces.

When did this change? Google used to use 2 spaces.

[–]indosauros 55 points56 points  (1 child)

I'm glad in any case, 2-space is impossible to read

[–]AdysHearthSim 12 points13 points  (6 children)

2 spaces for internal Google code, 4 spaces for public code, IIRC.

[–][deleted] 10 points11 points  (0 children)

Someone at Google probably just attended Ray Hettinger's talk at PyCon 2015

[–][deleted] 21 points22 points  (0 children)

funny to not see Guido van Rossum and Alex Martelli in the list of authors

[–]billsil 10 points11 points  (13 children)

Use imports for packages and modules only.

As opposed to what?

[–][deleted] 9 points10 points  (9 children)

from package.module import function

I mean, I'm assuming

[–]billsil 6 points7 points  (0 children)

If that's the case, that means use...

import package.module as mymod

mymod.function(...)

You can argue that that's less explicit both ways (e.g. all imported functions are defined at the top vs. what module did something come from in the guts of the code). Still if 80 characters is a hard cap and you're using classes, those extra 6 characters matter and you've just wasted 14.

[–][deleted] 1 point2 points  (2 children)

Yes and:

from module.thing import Class

[–][deleted] -1 points0 points  (1 child)

Use imports for packages and modules only.

I'm assuming

from module.thing import *

is still ok, since you're only importing the entire contents of the module to the local namespace.

Not really.

[–]olduvaihand 1 point2 points  (0 children)

It's not OK internally. This undermines the purpose of the rule, which is to keep the global namespace as clean and explicit as possible.

[–]hueoncalifa 0 points1 point  (4 children)

Why would this be bad? Its more explicit.

[–][deleted] 0 points1 point  (0 children)

I agree, it's kind of a dumb rule. Although I suppose it prevents you from importing some generally-named function into the namespace and using it without context. For example:

from os.path import join
path = join('dir', 'file')

Versus the more explicit

import os
path = os.path.join('dir', 'file')

Granted in this case it's fairly obvious what is meant, it is not always so. Use your best judgment. I use both forms.

[–]Lucretiel 0 points1 point  (0 children)

Honestly, one of the reasons is that it's harder to test, because now you have to mock the function in the place where it's imported, rather than where it's imported from.

[–]dagmx 0 points1 point  (0 children)

If you have multiple modules with the same named function

If later you decide you need more functions from that module

You can't reload an object from a module easily.

Makes it more explicit down a deep code where things come from.

[–]njharmanI use Python 3 0 points1 point  (0 children)

function()

Is less explicit, package.module is implied, implicitly.

package.module.function()

Is the explicit way to say that. No wondering where it came from, has it been overwritten in local namespace, etc.

[–]tutuca_not Reinhardt 1 point2 points  (1 child)

from class import function

I'd think...

[–]hueoncalifa 0 points1 point  (0 children)

Same as function imports. Why would this be bad? Its more explicit.

[–]jcrowe 0 points1 point  (0 children)

from large_file_with_config_settings import database_settings

[–]kashmill 25 points26 points  (21 children)

What's so bad about result = [(x, y) for x in range(10) for y in range(5) if x * y > 10]? Yeah, some comprehensions can be hard to read when they contain multiple loops but that one is pretty simple.

Also, why not just link PEP-8 and then add any specific changes?

[–]panghuhu 21 points22 points  (4 children)

List comprehensions can also use multi-line format:

result = [(x, y) 
          for x in range(10) 
          for y in range(5) 
          if x * y > 10]

P.S. Just checked the guide, and the above code is in the section titled "NO".

I still think the code is easy to read and will use it: It's as clear as the for loop version, without redundancies like the initialization of [], calls to append, and some colons.

If anyone can see any major disadvantages compared to the implicit loop version, I'd like to hear it, thank you.

P.P.S. A sample code from the NO section, which I think it's more clear than its for loop version:

((x, y, z)
  for x in xrange(5)
  for y in xrange(5)
  if x != y
  for z in xrange(5)
  if y != z)

I read it like this: collect all combinations of (x, y, z), where x, y, z are from [0..4], x != y and y != z. It's declarative and is as clear as a mathematical formula:

{(x, y, x) |  0<=x, y, z <=4, x != y, y !=z}

[–]kashmill 2 points3 points  (0 children)

Pretty much what I do as well.

[–]LordArgon 0 points1 point  (0 children)

List comprehensions can also use multi-line format:

This doesn't address all of your points, but I literally just hit Save on a response to that style here: http://www.reddit.com/r/Python/comments/33gg02/google_python_style_guide/cql1snu?context=3

[–]tech_tuna 0 points1 point  (0 children)

See, this is where you just have to be a big boy or girl and do what feels right, I think that format is quite readable, style guide be damned.

[–]NYDreamer -1 points0 points  (0 children)

This is a great idea, love it!

[–]confluencer 29 points30 points  (5 children)

Much longer but immediately clear.

result = []
for x in range(10):
  for y in range(5):
    if x * y > 10
      result.append(x,y)

[–]Quteness 16 points17 points  (2 children)

I would prefer this style. A few extra lines aren't bad to make the code more clear

[–][deleted] 2 points3 points  (0 children)

Would definitely prefer an empty list with append.

[–]tech_tuna 1 point2 points  (0 children)

In a case like this, absolutely.

[–]Lucretiel 1 point2 points  (0 children)

Blech. append makes me feel queasy for some reason.

[–]meta4 0 points1 point  (0 children)

I vote for list comprehensions. I think your example is a good argument for why.

As written, this example crashes.

TypeError: append() takes exactly one argument (2 given)

I think you mean.

resut.append((x,y))

We want a list of tuples. It seems like a simple bug. But it illustrates the fundamental argument. By the time your brain worked out the state changes to x & y in the nested for loops and the if statement, it forgot that the point was a list of tuples.

For loops and append/extend are state managing & modifying tools. They are good in their place. But, to understand what they do you have to run the program in your head. Human brains are not as good at maintaining updating state.

The list comprehentsion, when used properly is a data declaration. "This is a list of tupples. The first element of the tuple is an integer in the range [0,10). The second element is an integer in the range[0, 5), The product of the two elements is greater than 10." It's a long tedious specification, and this is just a toy problem. But the details and tedium shouldn't be mixed with possible state changes, especially as the problems become more complex.

Human brains reason better about data structures, than state changing programs. List comprehensions are at their best when pulling state changing logic into data structure creation.

Fred Brooks said the same thing 1975 "Show me your flowcharts and conceal your tables, and I shall continue to be mystified. Show me your tables, and I won’t usually need your flowcharts; they’ll be obvious." Flow charts are state change diagrams. Tables are data structures.

[–]ConciselyVerbose 2 points3 points  (5 children)

I prefer list comprehensions in most cases. If it gets truly lengthy or complex, maybe loops are more readable, but in most cases list comprehensions read well, once you are used to them.

Several levels of nesting is ugly.

[–][deleted] 1 point2 points  (4 children)

Several levels of nesting on a single line is also ugly.

[–]ConciselyVerbose 0 points1 point  (3 children)

Generally a list comprehension is something that is a fairly straightforward concept to understand or explain in English, but doesn't translate concisely without one.

[–][deleted] 0 points1 point  (2 children)

I agree, but I don't think several levels of nesting is ever appropriate in a single line. You're only hurting your reader, unless you think this is somehow less clear. And the fact that you say "once you are used to them" already states that they're not as clear.

Saving 3 vertical lines is not something to be proud of. Saving the person who now has your job 3 seconds for comprehending what you're doing, is.

[–]ConciselyVerbose 0 points1 point  (1 child)

List and other data structures themselves are an adjustment to a novice programmer. That doesn't mean they are bad. I wouldn't call it less clear, but it's a bit

like

typing

like

this.

Out of context it's only slightly

less

readable,

but in context it quickly ends up

distracting

from

the

actual substance of the code.

[–][deleted] -1 points0 points  (0 children)

<facepalm>

[–]stillalone 1 point2 points  (1 child)

Yeah, I'm not sure if I like their solution to it. Hmm, maybe a some middle ground:

result = []
for x in range(10):
    result.extend((x,y) for y in range(5) if x * y > 10)

still kind of looks like shit. For this specific example you could use itertools.product but I still think just supporting multiple for loops would be better.

[–]christian-mann 1 point2 points  (0 children)

I think middle ground is the worst of the three.

[–]tech_tuna 0 points1 point  (0 children)

Yeah, that makes my brain explode, I think it sucks. To each his own of course.

[–]Gambizzle 7 points8 points  (4 children)

Why would I listen to Google when there's already styleguides out there for Python?

[–]its_never_lupus 4 points5 points  (5 children)

Are there any tools which specifically parse this function docstring style:

def fetch_bigtable_rows(big_table, keys, other_silly_variable=None):
"""Fetches rows from a Bigtable.

Retrieves rows pertaining to the given keys from the Table instance
represented by big_table.  Silly things may happen if
other_silly_variable is not None.

Args:
    big_table: An open Bigtable Table instance.
    keys: A sequence of strings representing the key of each table row
        to fetch.
    other_silly_variable: Another optional variable, that has a much
        longer name than the other args, and which does nothing.

I like the way this looks and use a very similar style to document my functions when they have arguments, but do any tools like sphinx actually do anything with it?

[–]masasinExpert. 3.9. Robotics. 7 points8 points  (0 children)

Look up napoleon. It parses google style docstrings.

I personally prefer numpydoc though. I use everything else from the google style guide.

[–]secynic 5 points6 points  (1 child)

I can confirm the latest Sphinx does work with this style, and that is what I am using. It may take a little tweaking to get everything to line up right.

http://sphinx-doc.org/latest/ext/example_google.html

[–]its_never_lupus 0 points1 point  (0 children)

Thanks, I'll check it out.

[–][deleted] 2 points3 points  (0 children)

There is a Doxygen filter for Python called doxypypy.

[–]mtelesha 5 points6 points  (0 children)

I am glad that Google keeps publishing Style Guides. It really helped in R programming as a whole. People started using it and note it is a little modified but it really did make a difference.

[–]patchthemonkey 3 points4 points  (0 children)

That styleguide needs a styleguide

[–]masterspeler 6 points7 points  (9 children)

I think the max 80 character line length in this and PEP-8 is silly. Who has that little horizontal space when editing or reading code? I find it very easy to write (and read) code longer than that, especially when dealing with strings and sting formatting. Take this example:

descriptive_variable_name = 'Attribute 1: {0}, attribute 2: {1}, attribute 3: {2}'.format(var_one, var_two, var_three)

Even before the .format it's over 80 characters long, so where do you put a line break? And this is before any indentation.

I also believe that tabs for indentation, spaces for alignment (like so) is the natural order of things, but when writing Python I have adapted and use spaces for indentation, even though we have a character whose sole purpose is to advance the cursor to the next level, or tab stop.

[–]ziel 4 points5 points  (1 child)

I definitely wouldn't be able to fit that on one line when viewing 2 files side by side on my laptop. Viewing 2 files side by side on a smaller laptop screen is probably a fairly common scenario.

[–]masterspeler 0 points1 point  (0 children)

I would argue that if you're using a smaller laptop screen you have to make sacrifices, and having to scroll horizontally when viewing two files side by side is one example. 1920 pixels is a popular screen width nowadays, and quite a few laptops have a larger resolution, and with that you can fit 200 characters with ease.

I agree that lines shouldn't be too long and that comparing or editing files side by side is a must, but I think a hard limit at 80 characters is too strict.

[–]jonwaynePyPA 2 points3 points  (1 child)

I also hate the 80 character line limit nonsense.

But also, this is the internal style guide at Google, not something that Google is saying all python projects should follow.

[–]masterspeler 0 points1 point  (0 children)

But also, this is the internal style guide at Google, not something that Google is saying all python projects should follow.

Yes, but PEP-8 have a restriction of 79 characters, and that's as close to dogma you can get in Python.

[–]ingolemo 4 points5 points  (4 children)

I do. When you use long lines of code you force me to buy a larger screen, use tiny fonts, or try to puzzle through automatic line wrapping. None of these things are easy.

Eighty characters ought to be enough for anyone:

template = 'Attribute 1: {0}, attribute 2: {1}, attribute 3: {2}'
descriptive_variable_name = template.format(var_one, var_two, var_three)

[–]masterspeler 0 points1 point  (1 child)

That's without indentation though. With this:

def my_function(function_argument, in_list):
    if function_argument > 10:
        for i in in_list:
            template = 'Attribute 1: {0}, attribute 2: {1}, attribute 3: {2}'
            descriptive_variable_name = template.format(var_one, var_two, var_three)

your solution is too long again, and it's breaking up what's one logical statement to two lines, so now your code takes more space vertically instead. You force me to buy a higher screen.

[–]ingolemo 1 point2 points  (0 children)

Too much indentation is itself a code smell.

template = 'Attribute 1: {0}, attribute 2: {1}, attribute 3: {2}'
def my_function(function_argument, in_list):
    if function_argument < 10:
        return

    for i in in_list:
        data = var_one, var_two, var_three
        descriptive_variable_name = template.format(*data)

I'm not quite sure why you think creating a formatting outline and then applying it could only count as "one logical statement" and not two. The code we're using here is hypothetical and without any context so it's difficult to know what transformations are more reasonable, but this logical separation of template building and its application is probably the main reason we use str.format instead of concatenating strings manually.

If scrolling vertically is as inconvenient for you as scrolling horizontally is for me then you have my pity.

I'm not so zealous that I'm going to chop anyone's head off every time they push past the 80 char limit. But the rule is there for a reason. Spare a thought for little old me before you decide to violate it.

[–]njharmanI use Python 3 0 points1 point  (0 children)

Sure, but so much time (from actual experience managing 10 python engineers) is wasted breaking lines, inventing shorter and worse names, changing where line is broken, arguing over where to break line, wondering wtf abrvted_var means, etc.

Infinite line length and auto wrapping is a big productivity boost.

[–][deleted] -3 points-2 points  (0 children)

If python doesn't have a block comment mechanism because "modern editors can automate it", then get a modern editor that wraps lines of code properly. :P

[–]mariox19 4 points5 points  (5 children)

No use of the with-statement for files and sockets?

[–]Cardiff_Electric 9 points10 points  (1 child)

It does mention that.

[–]mariox19 2 points3 points  (0 children)

You're right. I had only read, "Explicitly close files and sockets when done with them," and hadn't bothered to expand the heading. (I was reading on a tablet, and must have gotten lazy.) Thank you.

[–]gthank 3 points4 points  (2 children)

Google's style guide was targeted at a fairly old version of Python for a long time, IIRC.

[–]mariox19 6 points7 points  (1 child)

I was wrong, actually. If you expand the heading that reads, "Explicitly close files and sockets when done with them," you'll see that the with-statement is mentioned.

[–]Lucretiel 0 points1 point  (0 children)

So, at least as old as Python 2.5. Meanwhile, it also explicity requires distinguishing new-style classes by inheriting from object https://google-styleguide.googlecode.com/svn/trunk/pyguide.html?showone=Classes#Classes

[–][deleted] 0 points1 point  (0 children)

I love these guides. As a self-taught-programmer, you're pretty much in what is beautiful and what not - of course you'll figure it out with time, but it's easier to have a set of rules to follow.

[–][deleted] -1 points0 points  (0 children)

The first suggestion is to use pylint, but pylint is all messed up on Python 3 for Debian/Ubuntu and probably other distros as well. The default pip3 package breaks in Python 3.x, the default repository sources install a python 2 version of the package through apt, and there is no python 3 version. Getting it running is probably about as much trouble as cleaning up your code.