Search code examples
pythonstringformattingline-endingscorrectness

What is the meaning of this Python statement?


More specifically, I'm not sure what the "%" and "\" symbols coming right after each other are supposed to mean:

return 'guitar { id: %d, relevant_properties: (%.02f, %.02f, %.02f), irrelevant_properties: (%.02f, %.02f, %.02f) }' % \
    (self.id, a, b, c, d, e, f)

Is this return statement considered to be one line of code? Because I originally thought the "\" was there to chop up what seems to be an excessively long line into two pieces to enhance readability and to avoid wrap-around.

Also, I the tabulation here reflects what is in my code. The part that says '(self.id, a, b, c, d, e, f)' is indeed tabbed once further than the beginning of the return statement.

Basically, is the statement equivalent to:

return 'guitar { id: %d, relevant_properties: (%.02f, %.02f, %.02f), irrelevant_properties: (%.02f, %.02f, %.02f) }' % (self.id, a, b, c, d, e, f)

in this one there is no "\" involved....


Solution

  • The % symbol is known as the string formatting or string interpolation operator and is described in String Formatting Operations. Also, note that in my examples I moved the % symbol to the next line instead of leaving it on the end of the previous line. Both forms are valid, however putting it on the same line as the data used for interpolation tends to make it clearer to read and easier to maintain.

    Yes, the two pieces of code are equivalent since the backslash treats two or more physical lines as a single logical line as described in the Python documentation about the \ symbol in the section about Explicit Line Joining. However I would avoid using explicit line joining and instead use this third equivalent way called Implicit Line Joining:

    return ('guitar { id: %d, relevant_properties: (%.02f, %.02f, %.02f), irrelevant_properties: (%.02f, %.02f, %.02f) }'
            % (self.id, a, b, c, d, e, f))
    

    Because it is less prone to errors when reformatting and also allows you to use comments, such as in this reformatted example:

    return ('guitar { id: %d, relevant_properties: (%.02f, %.02f, %.02f), irrelevant_properties: (%.02f, %.02f, %.02f) }' 
            % (self.id, 
               a, 
               b, 
               # c1, 
               c2, 
               d, 
               e, 
               f))
    

    Try doing that same thing with backslashes... not only is it more arduous, but you also cannot use comments.

    Note that the parentheses around the return value are not superfluous as some have suggested; they are what allows the use implicit line joining in the context of the variable interpolation (which is separate from the tuple following the % which is an additional context where implicit line joining also happens):

    Expressions in parentheses, square brackets or curly braces can be split over more than one physical line without using backslashes.

    In summary... please avoid explicit line joining with backslashes wherever possible! (It is almost never necessary given the alternatives).

    A related subject to explicit/implicit line joining is the use of triple-quoted strings discussed in the section on String Literals. Given the C-style block syntax of your guitar example, I expect it would probably be valid to reformat your return value into multiple lines like this:

    class c(object):
        def f(self):
            return """
    guitar {
      id: %d, 
      relevant_properties: (%.02f, %.02f, %.02f),
      irrelevant_properties: (%.02f, %.02f, %.02f)
    }
    """ % (self.id, a, b, c, d, e, f)
    

    I have put this example in the fuller context of a class method definition so it will be clearer what the formatting will look like; the multi-line string will be flush against the left side of the buffer and any spaces will appear verbatim in the output.

    Be aware, however, the formatting above may introduce unwanted leading or trailing newlines, so this is one of the few cases where I occasionally recommend the use of explicit line joining. The reason here is to eliminate unwanted extra newline characters as a trade-off for the enhanced code-readability that triple-quoted strings gives us since it allows us to see the complete stanza more or less the way we would see it in the final output; comparing above you'll see just the addition of a single \ character at the end of the first occurrence of the """:

    class c(object):
        def f(self):
            return """\
    guitar {
      id: %d, 
      relevant_properties: (%.02f, %.02f, %.02f),
      irrelevant_properties: (%.02f, %.02f, %.02f)
    }
    """ % (self.id, a, b, c, d, e, f)