Chained method calls indentation style in Python

PythonDjangoCoding StylePep8

Python Problem Overview


From reading PEP-8, I get it that you should put the closing parenthesis on the same line as the last argument in function calls:

ShortName.objects.distinct().filter(
    product__photo__stockitem__isnull=False)

Probably, long expressions are best to avoid at all. But if it's undesirable, how would you go about multiple chained method calls? Should the closing paren be on a new line?

ShortName.objects.distinct().filter(
    product__photo__stockitem__isnull=False
).values_list('value', flat=True)

What about no-arguments methods? How to write them on multiple lines without referencing the intermediate return values?

ShortName.objects.distinct(
    ).filter().values() # looks ugly

Update: There's a duplicate question of https://stackoverflow.com/questions/4768941/how-to-break-a-line-of-chained-methods-in-python. The accepted answer suggests a familiar from jQuery style of starting each new line with a dot. The author doesn't provide any reasons or authoritative references, so I'd like to get a confirmation on such style or an alternative.

Python Solutions


Solution 1 - Python

This is a case where a line continuation character is preferred to open parentheses.

ShortName.objects.distinct() \
         .filter().values()      # looks better

The need for this style becomes more obvious as method names get longer and as methods start taking arguments:

return some_collection.get_objects(locator=l5) \
                      .get_distinct(case_insensitive=True) \
                      .filter(predicate=query(q5)) \
                      .values()

PEP 8 is intend to be interpreted with a measure of common-sense and an eye for both the practical and the beautiful. Happily violate any PEP 8 guideline that results in ugly or hard to read code.

That being said, if you frequently find yourself at odds with PEP 8, it may be a sign that there are readability issues that transcend your choice of whitespace :-)

Solution 2 - Python

I think the best is to use () to force line joining, and to do this:

(ShortName.objects.distinct() # Look ma!
 .filter(product__photo__stickitem__isnull=False) # Comments are allowed
 .values_list('value', flat=True))

It's not ideal, but I like that it stands out visually and makes it somewhat obvious what the chain of calls is. It allows end-of-line comments, which \ newline does not.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionkatspaughView Question on Stackoverflow
Solution 1 - PythonRaymond HettingerView Answer on Stackoverflow
Solution 2 - Pythonuser97370View Answer on Stackoverflow