3
\$\begingroup\$

I have a python print statement and inside the string I wish to print are four digits. I would like to apply the same formatting function to each param. I am not familiar with the latest and greatest features of python PEPs.

Is there a slick way to do this?

Code

statement = "Splitting up the {} file into {} chunks, with the filesystem block size of {}, causing {} extra space to be used"

print(statement.format(
    sizeof_fmt(input_file_size), 
    sizeof_fmt(chunk_size), 
    sizeof_fmt(block_size), 
    sizeof_fmt(surrendered_space)))

Format Function

def sizeof_fmt(num, suffix='B'):
    for unit in ['','Ki','Mi','Gi','Ti','Pi','Ei','Zi']:
        if abs(num) < 1024.0:
            return "%3.1f%s%s" % (num, unit, suffix)
        num /= 1024.0
    return "%.1f%s%s" % (num, 'Yi', suffix)
\$\endgroup\$
  • 1
    \$\begingroup\$ This is not really a code review type question, and would be better asked on StackOverflow. But the slick way you are looking for is the map() function. \$\endgroup\$ – AJNeufeld Jun 17 at 16:29
  • \$\begingroup\$ @AJNeufeld - I have been told something here - codereview.stackexchange.com/questions/222292/…. Is it true? \$\endgroup\$ – Justin Jun 18 at 2:33
  • 3
    \$\begingroup\$ @Justin Ah. PEP 279 ... written in 2002, contains no mention of deprecating map, but a comment by GvR on the PEP suggested it should die. A discussion in 2005 suggested it might be gone by Python 3.0 ... which was released in December 2008. Now it is 11 years later, and I still see no signs of map being deprecated. True: you can use list comprehension instead of map, and in some cases it might be faster and clearer, but in others it can be slower and/or less clear. YMMV, but I don’t expect map will ever go away. \$\endgroup\$ – AJNeufeld Jun 18 at 4:19
2
\$\begingroup\$

Time will tell if your question is considered a worthy Code Review question, but till then I'ld like you to give a short review on your code nevertheless.

Format function

You could reduce the code duplication in the format function and make use of .format or f-strings (from Python 3.6 onwards).

def sizeof_fmt_rev(num, suffix='B'):
    for unit in ['', 'Ki', 'Mi', 'Gi', 'Ti', 'Pi', 'Ei', 'Zi']:
        if abs(num) < 1024.0:
            break
        num /= 1024.0
    else:
        # this part is only executed if the loop was not left with a break
        unit = 'Yi'
    return f"{num:.1f}{unit}{suffix}"

This uses for ... else, one of the less well-known features of Python and only has a single line where the format expression has to be written. I see a chance to build something using math.log instead of that loop, but I will leave that as an exercise to you. You can even build something that works without a loop, but at least the version I came up with (found below) is actually slower than the original implementation.

def sizeof_fmt_rev_log(num, suffix='B'):
    exponent = min(int(math.log(abs(num), 1024)), 8)
    num /= 1024**exponent
    unit = ('', 'Ki', 'Mi', 'Gi', 'Ti', 'Pi', 'Ei', 'Zi', 'Yi')[exponent]
    return f"{num:.1f}{unit}{suffix}"

I used

for i in range(10):
    num = 3.8 * 1024**i
    print(sizeof_fmt_rev(num))
    assert sizeof_fmt(num) == sizeof_fmt_rev(num)
    assert sizeof_fmt(-num) == sizeof_fmt_rev(-num)

to test the revised version.

Code

As @AJNeufeld mentions in his comment, you could use map to save yourself some typing

print(
    statement.format(*map(sizeof_fmt, (input_file_size, chunk_size, block_size, surrendered_space)))
)

which is functionally equivalent to using a list comprehension:

print(
    statement.format(*[
        sizeof_fmt(i)
        for i in (input_file_size, chunk_size, block_size, surrendered_space)
    ])
)

Both build upon a technique called tuple unpacking, but as you can see it can also be used with lists, other sequences, and maybe also iterables (if it is a generator, it will be consumed - thanks @Graipher, who confirmed it/pointed it out in a comment).

\$\endgroup\$
  • \$\begingroup\$ Tuple unpacking can indeed be used with any iterable. It will be consumed by it if it is a generator. \$\endgroup\$ – Graipher Jun 19 at 14:53

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.