Let's raise the bar a bit here.
Function Declaration
def convert(s):
This function converts ... something. Inches to feet? Celsius to Fahrenheit? The function name doesn't give us any clue. The function argument s
is similarly non-descriptive.
What does the function do? It truncates a string 3 characters after the first period character, if one is present. We could name the function that:
def truncate_3_characters_after_period(s):
A wee bit too verbose, and it doesn't quite match what appears to be the intended usage. From the doc-string, the function seems to be intended to apply to strings representing floating-point quantities, so maybe:
def truncate_to_3_decimal_places(s):
That is better, but no longer implies the function is expecting a string, or returns a string. Type-hints go a long way here.
def truncate_to_3_decimal_places(s: str) -> str:
The type-hints don't actually do anything; they are ignored by the Python interpreter. They are read by programs like mypy
, which can use this static type information to spot possible errors in your program. They are also read and used by some documentation generating programs, which is very useful.
Doc-string
""" Convert 1234.12345678 to 1234.123"""
I love doc-strings. But this one is a little lacking in detail. It looks like it says it converts a floating point value to another floating point value with fewer significant digits. But the function doesn't do that at all; the function expects and returns strings.
"""Convert '1234.12345678' to '1234.123'"""
That is slightly clearer. The astute reader will see the single quotation marks, and realize it is not a numerical transformation, but a textual one.
But, many questions are left unanswered. Does '123.4567'
get rounded to '123.457'
, or truncated to '123.456'
?
""" Truncate the string representation of a float after 3 decimal places. >>> convert('1234.12345678') '1234.123' >>> convert('123.45678') '123.456' >>> convert('123') '123' """
That is better doc-string. It describes, in words, what the function is doing, and then goes on to provide three examples, the second showing the truncation, the third showing what happens when the string doesn't have a decimal point.
As a bonus, the format of the examples in the doc-string allow the doc-string to do double duty. You can run doctest.testmod()
on the module, and the examples become test-cases, and the output is compared with the documented output, and deviations are flagged.
Exceptional cases
The function does a few bizarre things.
len(vec) == 2
If and only if the string has 2 parts, will the length of the second part be tested.
>>> convert('192.168.0.1') '192.168.0.1'
Should the function have returned that value? Would 192.168
be a more expected result? Perhaps raising a ValueError
would be even better?
Exponential Notation
>>> convert(str(1234567890123456.0)) '1.234' >>> convert(str(0.000000001234)) '1.234'
Whoa! Those results are unexpected! Clearly, it does not handle the floating point values in exponential representation.
Is this a bug? Or is this a limitation, which should be spelt out in the docstring?
Alternate representation
>>> convert("123.4") '123.4' >>> convert("123.400321") '123.400'
The returned values would both represent the same floating point values, but their representations are different. Should the function add zeros to pad to 3 decimal places? Should this difference be mentioned in the doc-string?
Quality-of-Life
How is this function used? Do you already have the string representation of the floating point value, or are you converting the float-point value into a string for passing to the function?
A function which takes a float, and converts it to a string, truncated to the desired precision might make handling special cases easier.
Internationalization
Some regions do not use the period (.
) as the decimal indicator. A comma (,
) may be used to separate the integral part from the fractional. For instance, an approximation to pi might appear as 3,14159
. Since you've hard-coded your function to split the string at the period character, the application would break in France.
Using built-in formatting would take care of internationalization concerns. For instance, f"{value:.3f}"
would convert value
to a fixed-point string with 3 digits after the decimal point, even if the decimal point appears as the comma (,
) character. However, it would round based on the digit that comes next, instead of truncating the value, which does not appear to match what you've written.
You could explicitly truncate the value to the required resolution
value = math.trunc(value * 1000) / 1000
and then convert the result to a string using f"{value:.3f}"
.
def convert(s): return f'{float(s):.3f}'
\$\endgroup\$