Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Most pythonic way to format a number with commas?

1 view
Skip to first unread message

Ned Batchelder

unread,
Jul 11, 2002, 8:26:18 PM7/11/02
to
For dir/ls replacement script, I wanted to format file sizes with
commas as a thousands separator. I didn't find anything in the
standard library, but figured it couldn't be too hard to do with list
manipulation. I came up with this:

def formatNumber(num):
"""Format a number for display"""
sl = [c for c in str(num)]
for i in range(len(sl)-3, 0, -3):
sl[i:i] = [',']
return string.join(sl, '')

While this works, and uses list ops to do it, I thought it would be
possible to get a simpler solution.

Is there one?

--Ned.
http://www.nedbatchelder.com

Emile van Sebille

unread,
Jul 11, 2002, 8:51:29 PM7/11/02
to
Ned Batchelder

> For dir/ls replacement script, I wanted to format file sizes with
> commas as a thousands separator. I didn't find anything in the
> standard library,
[snip]
> Is there one?

It's buried in locale...

>>> import locale
>>> locale.setlocale(locale.LC_ALL, "")
'English_United States.1252'
>>> locale.format("%8.2f", 1234.56)
' 1234.56'
>>> locale.format("%8.2f", 1234.56,3)
'1,234.56'

--

Emile van Sebille
em...@fenx.com

---------

Alex Martelli

unread,
Jul 12, 2002, 5:12:20 AM7/12/02
to
Emile van Sebille wrote:

> Ned Batchelder
>> For dir/ls replacement script, I wanted to format file sizes with
>> commas as a thousands separator. I didn't find anything in the
>> standard library,
> [snip]
>> Is there one?
>
> It's buried in locale...

"buried" is a loaded word...:-)

>>>> import locale
>>>> locale.setlocale(locale.LC_ALL, "")
> 'English_United States.1252'
>>>> locale.format("%8.2f", 1234.56)
> ' 1234.56'
>>>> locale.format("%8.2f", 1234.56,3)
> '1,234.56'

Yes, but this example risks being a BIT misleading...:

>>> import locale
>>> locale.setlocale(locale.LC_ALL, "")
'en_US'
>>> for x in range(1, 5):
... print locale.format("%8.2f", 1234.56, x)
...
1,234.56
1,234.56
1,234.56
1,234.56
>>>

I.e., the third argument to locale.format is taken as a boolean --
if it's true, then digit grouping is performed as the locale
requires, if false or missing, no grouping.

There is, unfortunately, no "architected" way to perform
digit grouping with simply-specified number of digits and
separators -- you have to cheat. Specifically, for example,
suppose you need digits to be grouped by-four...:

>>> locale.format("%d", 1112345673489, 1)
'1,112,345,673,489'
>>> def localeconv():
... d = _real_localeconv()
... d['grouping'] = [4, 4, 0]
... return d
...
>>> _real_localeconv = locale.localeconv
>>> locale.localeconv = localeconv
>>> locale.format("%d", 1112345673489, 1)
'1,1123,4567,3489'
>>>

This works only because locale.format internally calls
locale.localeconv at a Python level -- any dependence on
internals, such as this one, is too fragile, liable to
break on any upgrade to the module involved. Therefore
it cannot be recommended. Unfortunately, copy-and-paste
and reinvent-the-wheel are the two recommended (sigh)
ways to "reuse" the digit-grouping functionality that's
buried (ahem) in module locale.


Alex

Emile van Sebille

unread,
Jul 12, 2002, 7:54:30 AM7/12/02
to
Alex Martelli

> Emile van Sebille wrote:
> >
> > It's buried in locale...
>
> "buried" is a loaded word...:-)

Yes... sorry ;-) That's just what it felt like the first time I needed
it. And reflects in part a more recent run-in with locale.

[clarifying and expounding remarks snipped]

The other reason for my use of "buried" is my recent discovery of the
source of a problem I experienced after writing a simple repeatable
obfustication function to scramble text like customer names and
addresses, and that initially depended on string.letters.

ActivePython 2.1, build 210 ActiveState)
based on Python 2.1 (#15, Apr 19 2001, 10:28:27) [MSC 32 bit (Intel)] on
win32
Type "help", "copyright", "credits" or "license" for more information.
>>> import string
>>> string.letters
'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'


>>> import locale
>>> locale.setlocale(locale.LC_ALL, "")
'English_United States.1252'

>>> string.letters
'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz
\x83\x8a\x8c\x8e\x9a\x9c\x9e\x9f\xc0\xc1\xc2\xc3\xc4
\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1
\xd2\xd3\xd4\xd5\xd6\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf
\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec
\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf8\xf9\xfa\xfb
\xfc\xfd\xfe\xff'
>>>

As this ran within zope, it was first imported as part of a product
before setlocale happened elsewhere within zope. Only once zope was up
and running the problem surfaced. The cvs version of zope shows that
the errant module has been reworked and no longer uses locale.

Then again, this may be common knowlege I'm only now finding out. But
at least I'm forewarned for the next time I need to know my abc's _and_
put comma's in numbers. ;-)

0 new messages