Correct style for line breaks when chaining methods in Python
Asked Answered
F

3

85

I have some code like this. Should the break occur before the periods or after?

# before
my_var = somethinglikethis.where(we=do_things).where(we=domore).where(we=everdomore)

# this way
my_var = somethinglikethis.where(we=do_things) \
                          .where(we=domore) \
                          .where(we=everdomore)

# or this way
my_var = somethinglikethis.where(we=do_things). \
                           where(we=domore). \
                           where(we=everdomore)
Furgeson answered 30/10, 2011 at 0:26 Comment(0)
D
135

PEP 8 recommends using parenthesis so that you don't need \, and gently suggests breaking before binary operators instead of after them. Thus, the preferred way of formatting you code is like this:

my_var = (somethinglikethis
          .where(we=do_things)
          .where(we=domore)
          .where(we=everdomore))

The two relevant passages are this one from the Maximum Line Length section:

The preferred way of wrapping long lines is by using Python's implied line continuation inside parentheses, brackets and braces. Long lines can be broken over multiple lines by wrapping expressions in parentheses. These should be used in preference to using a backslash for line continuation.

... and the entire Should a line break before or after a binary operator? section:

Should a line break before or after a binary operator?

For decades the recommended style was to break after binary operators. But this can hurt readability in two ways: the operators tend to get scattered across different columns on the screen, and each operator is moved away from its operand and onto the previous line. Here, the eye has to do extra work to tell which items are added and which are subtracted:

# No: operators sit far away from their operands
income = (gross_wages +
          taxable_interest +
          (dividends - qualified_dividends) -
          ira_deduction -
          student_loan_interest)

To solve this readability problem, mathematicians and their publishers follow the opposite convention. Donald Knuth explains the traditional rule in his Computers and Typesetting series: "Although formulas within a paragraph always break after binary operations and relations, displayed formulas always break before binary operations"

Following the tradition from mathematics usually results in more readable code:

# Yes: easy to match operators with operands
income = (gross_wages
          + taxable_interest
          + (dividends - qualified_dividends)
          - ira_deduction
          - student_loan_interest)

In Python code, it is permissible to break before or after a binary operator, as long as the convention is consistent locally. For new code Knuth's style is suggested.

Note that, as indicated in the quote above, PEP 8 used to give the opposite advice about where to break around an operator, quoted below for posterity:

The preferred way of wrapping long lines is by using Python's implied line continuation inside parentheses, brackets and braces. Long lines can be broken over multiple lines by wrapping expressions in parentheses. These should be used in preference to using a backslash for line continuation. Make sure to indent the continued line appropriately. The preferred place to break around a binary operator is after the operator, not before it. Some examples:

class Rectangle(Blob):

    def __init__(self, width, height,
                 color='black', emphasis=None, highlight=0):
        if (width == 0 and height == 0 and
            color == 'red' and emphasis == 'strong' or
            highlight > 100):
            raise ValueError("sorry, you lose")
        if width == 0 and height == 0 and (color == 'red' or
                                           emphasis is None):
            raise ValueError("I don't think so -- values are %s, %s" %
                             (width, height))
        Blob.__init__(self, width, height,
                      color, emphasis, highlight)
Donalddonaldson answered 30/10, 2011 at 0:42 Comment(2)
The break-after-the-operator recommendation makes sense for operators like "+" and "or" which normally have spaces around them. In the case of the "." operator, it likely makes sense to break-before-the-operator. Otherwise, our experiences with English, lead us to mentally read a trailing dot as a period rather than as method/attribute lookup.Befall
Note the Rectangle example from PEP8 was updated in 2013 with new multiline-if indentation. (I tried to edit the answer, but that didn't go over.)Shellieshellproof
S
3

FWIW, autopep8 (with an --aggressive flag) produced this from your original code:

my_var = somethinglikethis.where(
    we=do_things).where(
    we=domore).where(
    we=everdomore)

But I agree -- Bastien's solution is more elegant.

Slumberland answered 18/2, 2014 at 15:44 Comment(1)
That is just awful.Russellrusset
C
2

Do what works.

Also, check out this whitepaper on the myths of indentation in Python. That can be found here.

It starts out with:

"Whitespace is significant in Python source code."

No, not in general. Only the indentation level of your statements is significant (i.e. the whitespace at the very left of your statements). Everywhere else, whitespace is not significant and can be used as you like, just like in any other language. You can also insert empty lines that contain nothing (or only arbitrary whitespace) anywhere.

I hope that helps.

Cheery answered 30/10, 2011 at 0:39 Comment(1)
-1; "You can also insert empty lines that contain nothing (or only arbitrary whitespace) anywhere." is patently untrue. print('Hello, World') will not work if you put a newline between print and the open parenthesis. I can't think of any language about which "whitespace is not significant and can be used as you like" is a true statement; return1 in a function is generally a syntax error while return 1 is legal.Grethel

© 2022 - 2024 — McMap. All rights reserved.