12

PEP 8 has conflicting code examples (in my opinion), and I'm curious what the convention is for positioning closing braces.
Under the top of indentation they're on the same line as the parameters. Near the bottom it discusses positioning and instead says:

The closing brace/bracket/parenthesis on multiline constructs may either line up under the first non-whitespace character of the last line of list[...] or it may be lined up under the first character of the line that starts the multiline construct[...]

which directly conflicts the code examples above.
Where do you commonly position your closing braces for multi-line statements, and what do you think is best practice in terms of convention?

Just for clarity, here are code examples which demonstrate the differences.

foo = long_function_name(
    var_one, var_two,
    var_three, var_four)

result = some_function_that_takes_arguments(
    'a', 'b', 'c',
    'd', 'e', 'f',
    )

result = some_function_that_takes_arguments(
    'a', 'b', 'c',
    'd', 'e', 'f',
)
Jacob Stein
  • 312
  • 3
  • 15
  • 1
    It might be good to quote which example you are referring to as well as why you think its a contradiction: the lesser effort you impose on the reader the more likely you are to get an answer... – Julien Jun 25 '18 at 04:53
  • 1
    This may sound like a cop-out, but honestly whatever is most easily read. Typically I'll have it on the same line as the last parameter, but if that parameter is a function call and has it's own brace, or is deeply nested, I'll often put it on the next line. – Turksarama Jun 25 '18 at 04:54
  • And for the record asking about an (apparent?) contradiction is valid, asking for one's preference is off-topic. – Julien Jun 25 '18 at 04:55
  • @Julien my mistake, I tried to keep it minimal. In the past I've had the bulk of my posts with code examples removed. I don't think that's off-topic however, I am asking for how experienced Python developers position their braces, as that's what makes up the convention. – Jacob Stein Jun 25 '18 at 05:29
  • Whether or not it fits PEP convention is objective and as such is a valid question, but personal preference among accepted PEP options (meaning there will be experienced developers using both) is purely opinion based and as such off-topic... – Julien Jun 25 '18 at 07:08

3 Answers3

6

The two sections you mention are different in that the first is about continuation lines that are followed by a block (such as a multiline def or if statement) while the second is about closing braces and parentheses on affectation and function calls. When starting a block, you wouldn't want to put the closing parenthesis at the beginning of the next line because going back to the original indentation conveys the end of the block. A few examples that clearly look odd:

def long_function_foo(
    var_one, var_two, var_three,
    var_four
):
    print('This code really looks out of place')

def long_function_bar(
   var_one,
   var_two
):
    print('and so does this one')

PEP8 allows what they call vertical alignment, and many examples in various PEPs use this convention which has become an automated feature of Python IDEs:

def long_function_name(var_one, var_two, var_three,
                       var_four, var_five):
    """Documentation would go here, which makes it look better."""
    print(var_one + var_two + var_three)

But I personally avoid it. This is an opinion-based topic, but I don't like relying on alignment through a specific number of spaces. It's tedious to maintain and relies too much on IDE smart indents. I prefer this notation, which is allowed by PEP8 but doesn't seem as popular. Note the double-indent used for distinction from the function body:

def long_function_name(
        alpha, bravo, charlie, delta, echo, foxtrot,
        hotel, indiana):
    """Documentation would go here."""
    print(var_one + var_two + var_three)

When it comes to function calls and assignments, PEP8 doesn't have a clear answer. One might indent the closing parenthesis, as a way to mimic how blocks end when the next instruction is less indented.

foo = bar(
    1, 2, 3
    )

Vertical alignment is very popular and I'll admit it looks good, but again I don't want to force indentation size on future readers of my code so I avoid this:

foo = bar(1, 2, 3, 5, 6, 7, 8, 9,
          10, 11, 12, 13, 14)

Or one can also put the closing brace/parenthesis aligned to the left:

foo = bar(
    1, 2, 3
)

Coming from a C++, Java and JavaScript background, I use the latter option. Technically, you could also put the closing parenthesis on the same line as the arguments, but then it makes it look like an indented code block too much for my tastes, and it's not something I've really seen people do.

Domino
  • 6,314
  • 1
  • 32
  • 58
  • 1
    I appreciate the detailed answer, thank you. I too followed the latter convention for function calls as it seems familiar from C#. – Jacob Stein Jun 25 '18 at 05:36
  • That is a very articulate answer, thanks. What do you think of your last example but with the closing brace on the same line than the parameters instead? It is not in the PEP 8 (nor your last example but one) but I like it a lot and it is consistent with continuation lines followed by a block. – Géry Ogam Oct 02 '19 at 14:44
  • And by the way: I haven't accepted any answer yet and I would be very interested to have your opinion on how to break this Python line [here](https://stackoverflow.com/questions/58203140/how-to-break-this-python-line). – Géry Ogam Oct 02 '19 at 14:53
  • I think that looks a bit odd when you only have one line of arguments, but I don't have any rational argument for or against it. As for long affectations, I use temporary variables like others have mentioned in that question :) – Domino Oct 02 '19 at 17:38
5

There's no conflict here, since PEP8 specifically says:

The closing brace/bracket/parenthesis on multiline constructs may either line up under the first non-whitespace character of the last line of list, as in:

my_list = [
    1, 2, 3,
    4, 5, 6,
    ]
result = some_function_that_takes_arguments(
    'a', 'b', 'c',
    'd', 'e', 'f',
    )

or it may be lined up under the first character of the line that starts the multiline construct, as in:

my_list = [
    1, 2, 3,
    4, 5, 6,
]
result = some_function_that_takes_arguments(
    'a', 'b', 'c',
    'd', 'e', 'f',
)

so both conventions are acceptable.

I personally prefer the latter convention, but that's just me.

blhsing
  • 91,368
  • 6
  • 71
  • 106
  • 2
    At the top of the Indentation section the closing brace is on the last line of parameters in multiple code examples. – Jacob Stein Jun 25 '18 at 05:33
3

These are two code snippets from google's tensorflow and facebook's pytorch.

Tensorflow

if (not input_saved_model_dir and
      not saver_lib.checkpoint_exists(input_checkpoint)):
    print("Input checkpoint '" + input_checkpoint + "' doesn't exist!")

Pytorch

ALL_TENSORTYPES = [torch.float,
                   torch.double,
                   torch.half]

In both, they used the same line closing braces strategy. So, in my opinion, It is better to follow that.

JISHAD A.V
  • 361
  • 1
  • 9