Discussion:
Is there a better way? [combining f-string, thousands separator, right align]
(too old to reply)
Gilmeh Serda
2024-08-25 15:12:20 UTC
Permalink
Subject explains it, or ask.
s = "123456789" # arrives as str
f"{f'{int(s):,}': >20}"
' 123,456,789'
--
Gilmeh

I have a rock garden. Last week three of them died. -- Richard Diran
Stefan Ram
2024-08-25 15:42:24 UTC
Permalink
Post by Gilmeh Serda
Subject explains it, or ask.
Or, you could have asked this way:

|Please fill in the implementation (replacing "pass" below) for
|the following function definition!
|
|def format_number(number_str: str) -> str:
| """
| Format a string of digits as a right-aligned, comma-separated number.
|
| This function takes a string of digits and returns a formatted string where:
| - The number is right-aligned in a field width of 20 characters
| - Commas are inserted as thousand separators
| - Leading spaces are added for alignment
|
| Args:
| number_str (str): A string containing only digits (0-9)
|
| Returns:
| str: A formatted string with the number right-aligned and comma-separated
|
| Examples:
| >>> format_number("123456789")
| ' 123,456,789'
| >>> format_number("1000000")
| ' 1,000,000'
| >>> format_number("42")
| ' 42'
| >>> format_number("1234567890123456789")
| '1,234,567,890,123,456,789'
| """
| pass
Stefan Ram
2024-08-25 15:46:25 UTC
Permalink
Post by Stefan Ram
| >>> format_number("123456789")
| ' 123,456,789'
Yeah, you totally can combine the two format specifications into one:

f"{int(number):>20,}"

.
Gilmeh Serda
2024-08-27 20:48:27 UTC
Permalink
Post by Stefan Ram
f"{int(number):>20,}"
Great. Thanks. Do you have a link to where that's documented?

I did web search, found nothing.
--
Gilmeh

When a fellow says, "It ain't the money but the principle of the thing,"
it's the money. -- Kim Hubbard
Stefan Ram
2024-08-27 21:36:47 UTC
Permalink
Post by Gilmeh Serda
Post by Stefan Ram
f"{int(number):>20,}"
Great. Thanks. Do you have a link to where that's documented?
I did web search, found nothing.
Stoked to hear you're into it!

For docs, I usually snag the PDFs from "python.org," especially
"reference.pdf" (the Python Language Reference) and "library.pdf"
(the Python Library Reference), then I search them for keywords.

The f-string stuff is laid out in "The Python Language Reference"
since they're part of the language itself. A quick search for
"f-str" gets you to "2.4.3 Formatted string literals," which says,
"The result is then formatted using the format() protocol." So, you
got to check out "The Python Library Reference" too! Searching for
"format(" there zooms you right to "6.1.3 Format String Syntax"
(via "Format Specification Mini-Language").

But honestly, I usually just hit up a chatbot first. I'll drop
in my code and say, "How can I make this shorter?".
Gilmeh Serda
2024-08-30 05:25:40 UTC
Permalink
Post by Stefan Ram
For docs, I usually snag the PDFs from "python.org," especially
"reference.pdf" (the Python Language Reference) and "library.pdf"
(the Python Library Reference), then I search them for keywords.
Thanks, that's a good idea. I've usually gone for the web docs only.
Post by Stefan Ram
The f-string stuff is laid out in "The Python Language Reference"
since they're part of the language itself. A quick search for
[snip]

Great. Thanks. I'll try to remember that.
Post by Stefan Ram
But honestly, I usually just hit up a chatbot first. I'll drop
in my code and say, "How can I make this shorter?".
In other words, a shortcut. :)
--
Gilmeh

More people are flattered into virtue than bullied out of vice. -- R. S. Surtees
Grant Edwards
2024-08-27 21:39:21 UTC
Permalink
Post by Gilmeh Serda
Post by Stefan Ram
f"{int(number):>20,}"
Great. Thanks. Do you have a link to where that's documented?
I did web search, found nothing.
https://docs.python.org/3/library/string.html#formatspec
https://docs.python.org/3/reference/lexical_analysis.html#f-strings
Gilmeh Serda
2024-08-30 05:22:17 UTC
Permalink
Post by Grant Edwards
Post by Gilmeh Serda
Post by Stefan Ram
f"{int(number):>20,}"
Great. Thanks. Do you have a link to where that's documented?
I did web search, found nothing.
https://docs.python.org/3/library/string.html#formatspec
https://docs.python.org/3/reference/lexical_analysis.html#f-strings
Thanks. I'll check those as time allows.
--
Gilmeh

National security is in your hands - guard it well.
Gilmeh Serda
2024-08-31 05:31:05 UTC
Permalink
Post by Stefan Ram
f"{int(number):>20,}"
I can find "," (comma) and I can find "_" (underscore) but how about " "
(space)?

Or any other character, for that matter?

Any ideas?

Of course I can do f"{123456:>20_}".replace("_", " "), just thought there
might be something else my search mojo fails on.

Thanks.
--
Gilmeh

Diplomacy is to do and say, the nastiest thing in the nicest way. --
Balfour
Stefan Ram
2024-08-31 15:26:58 UTC
Permalink
Post by Gilmeh Serda
Of course I can do f"{123456:>20_}".replace("_", " "), just thought there
might be something else my search mojo fails on.
Looks like this "replace" deal is the go-to move, no two ways
about it. If there's some neck of the woods where it's SOP, you
might wanna roll with "locale.format_string". (Whipping up a custom
locale just for kicks is usually more trouble than it's worth.)
Stefan Ram
2024-08-31 17:00:11 UTC
Permalink
Post by Stefan Ram
Looks like this "replace" deal is the go-to move, no two ways
about it. If there's some neck of the woods where it's SOP, you
might wanna roll with "locale.format_string". (Whipping up a custom
locale just for kicks is usually more trouble than it's worth.)
The German DIN 5008 (their fancy-schmancy rules for writing
and formatting) now says you got to use /spaces/ to separate
thousands, and only recommends using periods for cold hard cash.

If you just chill for a hot minute, Python's German locale will
catch up, and you'll be golden.
Stefan Ram
2024-08-31 17:02:47 UTC
Permalink
Post by Stefan Ram
The German DIN 5008 (their fancy-schmancy rules for writing
and formatting) now says you got to use /spaces/ to separate
thousands, and only recommends using periods for cold hard cash.
And, reportedly, ISO 80000, too.
Lawrence D'Oliveiro
2024-08-31 22:56:01 UTC
Permalink
The German DIN 5008 (their fancy-schmancy rules for writing and
formatting) now says you got to use /spaces/ to separate thousands ...
That was taught to me as the international scientific standard when I was
in secondary school. Along with using a centred dot for the decimal point,
to avoid the confusion with region-specific rules involving full stops and
commas.
Gilmeh Serda
2024-09-03 16:16:46 UTC
Permalink
Post by Stefan Ram
Looks like this "replace" deal is the go-to move,
Yep, looks like it from the rest of the replies.

Thanks to all participants.
--
Gilmeh

"You're just the sort of person I imagined marrying, when I was little...
except, y'know, not green... and without all the patches of fungus." --
Swamp Thing
MRAB
2024-08-31 18:55:39 UTC
Permalink
Post by Gilmeh Serda
Post by Stefan Ram
f"{int(number):>20,}"
I can find "," (comma) and I can find "_" (underscore) but how about " "
(space)?
Or any other character, for that matter?
Any ideas?
Of course I can do f"{123456:>20_}".replace("_", " "), just thought there
might be something else my search mojo fails on.
The format is described here:

https://docs.python.org/3/library/string.html#formatspec

A space is counted as a fill character.
dn
2024-09-02 00:33:16 UTC
Permalink
Post by Grant Edwards
Post by Gilmeh Serda
Post by Stefan Ram
f"{int(number):>20,}"
I can find "," (comma) and I can find "_" (underscore) but how about " "
(space)?
Or any other character, for that matter?
Any ideas?
Of course I can do f"{123456:>20_}".replace("_", " "), just thought there
might be something else my search mojo fails on.
https://docs.python.org/3/library/string.html#formatspec
A space is counted as a fill character.
Rather than strict formatting, you may be venturing into
"internationalisation/localisation" thinking.

Different cultures/languages present numeric-amounts in their own ways.
For example, a decimal point may look like a dot or period to some
(there's two words for the same symbol from different English-language
cultures!), whereas in Europe the symbol others call a comma is used, eg
123.45 or 123,45 - and that's just one complication of convention...

For your reading pleasure, please review "locales"
(https://docs.python.org/3/library/locale.html)


Here's an example:

Country Integer Float
USA 123,456 123,456.78
France 123 456 123 456,78
Spain 123.456 123.456,78
Portugal 123456 123456,78
Poland 123 456 123 456,78


Here's some old code, filched from somewhere (above web.ref?) and
updated today to produce the above:-

""" PythonExperiments:locale_numbers.py
Demonstrate numeric-presentations in different cultures (locales).
"""

__author__ = "dn, IT&T Consultant"
__python__ = "3.12"
__created__ = "PyCharm, 02 Jan 2021"
__copyright__ = "Copyright © 2024~"
__license__ = "GNU General Public License v3.0"

# PSL
import locale


locales_to_compare = [
( "USA", "en_US", ),
( "France", "fr_FR", ),
( "Spain", "es_ES", ),
( "Portugal", "pt_PT", ),
( "Poland", "pl_PL", ),
]

print( "\n Country Integer Float" )
for country_name, locale_identifier in locales_to_compare:
locale.setlocale( locale.LC_ALL, locale_identifier, )
print( F"{country_name:>10}", end=" ", )
print(
locale.format_string("%10d", 123456, grouping=True, ),
end="",
)
print( locale.format_string("%15.2f", 123456.78, grouping=True, ) )
--
Regards =dn
--
Regards,
=dn
Pierre Fortin
2024-08-26 01:38:22 UTC
Permalink
Post by Gilmeh Serda
Subject explains it, or ask.
s = "123456789" # arrives as str
f"{f'{int(s):,}': >20}"
' 123,456,789'
Oops.. forgot comma

f"{int(s):>20,}"
MRAB
2024-08-26 01:55:03 UTC
Permalink
Post by Gilmeh Serda
Subject explains it, or ask.
s = "123456789" # arrives as str
f"{f'{int(s):,}': >20}"
' 123,456,789'
s = "123456789"
f'{int(s): >20,}'
' 123,456,789'
Post by Gilmeh Serda
f'{int(s):20,}'
' 123,456,789'
Pierre Fortin
2024-08-26 01:34:51 UTC
Permalink
Post by Gilmeh Serda
Subject explains it, or ask.
s = "123456789" # arrives as str
f"{f'{int(s):,}': >20}"
' 123,456,789'
f"{s:>20}"
dn
2024-08-26 08:42:32 UTC
Permalink
Post by Gilmeh Serda
Subject explains it, or ask.
s = "123456789" # arrives as str
f"{f'{int(s):,}': >20}"
' 123,456,789'
With recent improvements to the expressions within F-strings, we can
separate the string from the format required. (reminiscent of FORTRAN
which had both WRITE and FORMAT statements, or for that matter HTML
which states the 'what' and CSS the 'how')

Given that the int() instance-creation has a higher likelihood of
data-error, it is recommended that it be a separate operation for ease
of fault-finding - indeed some will want to wrap it with try...except.
Post by Gilmeh Serda
s = "123456789" # arrives as str
s_int = int( s ) # makes the transformation obvious and distinct
s_format = ">20," # define how the value should be presented
F"{s_int:{s_format}}"
' 123,456,789'
Post by Gilmeh Serda
S_FIELD_WIDTH = 20
s_format = F">{S_FIELD_WIDTH},"
RIGHT_JUSTIFIED = ">"
THOUSANDS_SEPARATOR = ","
s_format = F"{RIGHT_JUSTIFIED}{S_FIELD_WIDTH}{THOUSANDS_SEPARATOR}"
s_format = F"{S_FIELD_WIDTH}{THOUSANDS_SEPARATOR}"
To the extreme that if your user keeps fiddling with presentations (none
ever do, do they?), all settings to do with s_format could be added to a
config/environment file, and thus be even further separated from
program-logic!
--
Regards,
=dn
Stefan Ram
2024-08-26 11:30:44 UTC
Permalink
RIGHT_JUSTIFIED = ">"
THOUSANDS_SEPARATOR = ","
s_format = F"{RIGHT_JUSTIFIED}{S_FIELD_WIDTH}{THOUSANDS_SEPARATOR}"
def PROCEDURE_DEFINIION( NAME, BODY ):
return f'\ndef {NAME}():\n{" "*4}{BODY}\n'

def PRINT_STATEMENT( WHAT_TO_PRINT ):
return f'print( "{WHAT_TO_PRINT}" )'

def PROCEDURE_INVOCATION( WHAT_TO_INVOKE ):
return f'{WHAT_TO_INVOKE}()'

exec\
( PROCEDURE_DEFINIION( "f", PRINT_STATEMENT( "Hello, world!" ))+
PROCEDURE_INVOCATION( "f" ))
2***@potatochowder.com
2024-08-26 11:00:32 UTC
Permalink
On 2024-08-26 at 20:42:32 +1200,
Post by dn
RIGHT_JUSTIFIED = ">"
THOUSANDS_SEPARATOR = ","
s_format = F"{RIGHT_JUSTIFIED}{S_FIELD_WIDTH}{THOUSANDS_SEPARATOR}"
s_format = F"{S_FIELD_WIDTH}{THOUSANDS_SEPARATOR}"
To the extreme that if your user keeps fiddling with presentations (none
ever do, do they?), all settings to do with s_format could be added to a
config/environment file, and thus be even further separated from
program-logic!
And then you'll need a parser, many of whose Unique Challenges™ aren't
even apparent until you start parsing files from actual users, and
you'll still need some sort of fallback in the code anyway for the case
that s_format can't be parsed (for whatever reason).

Isn't a config file what just caused the global CrowdStrike outage? ;-)

That said, I understand that report generators are a thing, not to
mention RPG (https://en.wikipedia.org/wiki/IBM_RPG).

Okay, sorry; I'll just crawl back into the hole from whence I came.
dn
2024-08-26 20:27:11 UTC
Permalink
Post by 2***@potatochowder.com
On 2024-08-26 at 20:42:32 +1200,
Post by dn
RIGHT_JUSTIFIED = ">"
THOUSANDS_SEPARATOR = ","
s_format = F"{RIGHT_JUSTIFIED}{S_FIELD_WIDTH}{THOUSANDS_SEPARATOR}"
s_format = F"{S_FIELD_WIDTH}{THOUSANDS_SEPARATOR}"
To the extreme that if your user keeps fiddling with presentations (none
ever do, do they?), all settings to do with s_format could be added to a
config/environment file, and thus be even further separated from
program-logic!
And then you'll need a parser, many of whose Unique Challenges™ aren't
even apparent until you start parsing files from actual users, and
you'll still need some sort of fallback in the code anyway for the case
that s_format can't be parsed (for whatever reason).
Isn't a config file what just caused the global CrowdStrike outage? ;-)
That said, I understand that report generators are a thing, not to
mention RPG (https://en.wikipedia.org/wiki/IBM_RPG).
Okay, sorry; I'll just crawl back into the hole from whence I came.
Not at all. Please continue to question/ask/suggest!

This is a valid point. There are costs and benefits (trade-offs) to all
decisions!

That said, writing one's own parser would become a veritable can of
worms/rabbit hole. Here be dragons!

Similarly, explaining this takes longer than writing the example itself!


Older Windows users will know about .ini files, and Linux Admins are
familiar with .conf files. Many of us are already using JSON or YAML
formats. Any of these (and more) could be pressed into service, as
above. At the 'top end', there are also whole libraries devoted to
establishing application configuration or "environments": default
values, config files, command-line options, user-input...

Have switched to using Python-poetry, which replaces packaging methods
such as setuptools (as well as virtual-environment tools). It takes its
project configuration specifications from a pyproject.toml file. So, for
a few projects lately, I've been using .toml for application-config as
well. However, I have to say, this more from an attempt at consistency
than a decision of logic. (critique welcome)

That said, a setup.py configuration, took the form:

setup(
name='demo_project',
version='1.1.0',
packages=find_packages(),
install_requires=[
'requests',
'numpy',
...
],
entry_points={
...

Accordingly, it offers an example of the simplest format (for us), and
one which has a zero-learning pre-requisite. At execution-time, the
moment such a config is import-ed, a syntax-error will immediately bring
proceedings to a halt!


I have some stats-wonks as clients. They dabble in programming, but
(fortunately) realise their limitations. (usually!) The boss has had to
ban them from 'improving' my code ($paid to be an improvement on their
usual quality), but including a .py configuration/options file has
proven to be an honor-preserving compromise. Of course, they manage
their own runs, adjusting parameters as they go. So, any errors are
their own, and they can fix themselves (without anyone else knowing!).

Such would not work in many?most other environments - children: do not
try this at home!


An irritation for those of us who have to delve into projects after
they've been written, is a git-history full of the sorts of
user-tweaking changes vilified earlier. Putting user-config into a
separate file, even a separate sub-directory, makes it easy to spot
which updates to ignore, and thus, which to consider!


PS the reason why CrowdStrike was not the end of humanity as we know it,
(and only that of those who only know MSFT's eco-system) is because the
majority of the world's Internet servers run Linux - including Azure
(brings to mind the old saw: the package said "runs on Windows-95 or
better" so I installed it on Linux!)

Joking aside, we (virtuous ones) ALWAYS test BEFORE release. Correct?
--
Regards,
=dn
Loading...