I was working on a little Python challenge that was a bit more difficult than expected: format any non-negative number so 1000 turns into "1,000" , 10000000 into "10,000,000" and so forth. I managed to do it, but I find my solution not very readable:

def formatting(n):
    m = str(n)  # Turn it into string
    m2 = []  # Empty list that will have the split version of the final number
    for i in range(0,len(m),3):  # Iterate every three elements

        m2.append(m[::-1][i:i+3][::-1])  # Append last three numbers, second to last three numbers, etc.
        m2.append(',')  # Add a comma between loops
    m3 = ''.join(m2[::-1])[1:] # Join and drop the first element which is always a comma.
    return m3

This works but it isn't as nice as it could be. What can be done?

  • 2
    \$\begingroup\$ Shouldn't m = str(m) be m = str(n)? \$\endgroup\$
    – Linny
    Sep 24, 2019 at 19:39
  • \$\begingroup\$ You're right! My code wasn't in function form so I had a little typo when I adapted it \$\endgroup\$
    – Juan C
    Sep 24, 2019 at 19:44

1 Answer 1


A few points:

  • Reversing the string m multiple times is unnecessary. Slicing can be done on the original string using correct indices. m[::-1][i:i+3][::-1] is equivalent to m[-i-3:len(m)-i]. Another approach is to traverse the indices in the reverse order for i in range(len(m)-1, -1, -3) and then perform slicing accordingly.

  • The logic can be simplified using str.join. m2.append(',') can be omitted. And you can just do

    m3 = ','.join(m2[::-1])
  • Further improvements can be achieved by using list comprehensions and traversing the string from the beginning rather than in the reverse order. Some calculation for the indices are needed:

    def formatting(n):
        m = str(n)  # Turn it into string
        num_digits = len(m)
        seg1_len = (num_digits % 3) or 3  # Calculate length of the first segment
        segments = [m[:seg1_len], *(m[i:i+3] for i in range(seg1_len, num_digits, 3))]
        return ','.join(segments)
  • Python 3's built-in formatting specification actually supports the thousands separator. So you can just do this to achieve the same functionality:

    formatted_n = f"{n:,}"

    Or equivalently:

    formatted_n = "{:,}".format(n)
  • \$\begingroup\$ Thanks! That was quite insightful! Also didn't know about that formatting option. Would have saved me of using locale multiple times. \$\endgroup\$
    – Juan C
    Sep 24, 2019 at 20:25
  • \$\begingroup\$ I'm not sure what you used locale for. According to the doc, For a locale aware separator, use the 'n' integer presentation type instead. \$\endgroup\$
    – GZ0
    Sep 24, 2019 at 20:28
  • \$\begingroup\$ When I'm working with money data that I have to format in some specific currency, but that's totally outside the scope of this \$\endgroup\$
    – Juan C
    Sep 24, 2019 at 20:45

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.