format_bytes(123456789) returns '117.7 MiB' with the one-liner above. It divides by 1,024 until the number would print below 1024.0, then formats it with one decimal. It works on any Python 3.6 or later, and on the int that os.path.getsize() or Path.stat().st_size gives you.
def format_bytes(n, i=0): return format_bytes(n / 1024, i + 1) if n >= 1023.95 and i < 5 else f"{n:.1f} " + ("B", "KiB", "MiB", "GiB", "TiB", "PiB")[i]
print(format_bytes(123456789), format_bytes(1536), format_bytes(500))
117.7 MiB 1.5 KiB 500.0 B
The tool below starts on 123456789 B: exactly 117.73756885528564453125 MiB, or 123.456789 MB.
A readable version, binary or decimal
def format_bytes(n, binary=True):
k = 1024 if binary else 1000
units = ['B', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB'] if binary else ['B', 'kB', 'MB', 'GB', 'TB', 'PB']
for unit in units[:-1]:
if round(n, 1) < k:
return f'{n} B' if unit == 'B' else f'{n:.1f} {unit}'
n /= k
return f'{n:.1f} {units[-1]}'
print(format_bytes(123456789))
print(format_bytes(123456789, binary=False))
print(format_bytes(1048575), format_bytes(500), format_bytes(0))
117.7 MiB
123.5 MB
1.0 MiB 500 B 0 B
Use binary=False to match macOS Finder and disk labels, and the 1,024 default to match ls -lh and du -h. Dividing by 1,024 and writing MB, as Windows does, is the mix that confuses people.
The 1024.0 KiB bug
The function most people copy checks the size before it rounds:
def sizeof_fmt(num, suffix='B'):
for unit in ('', 'Ki', 'Mi', 'Gi', 'Ti', 'Pi', 'Ei', 'Zi'):
if abs(num) < 1024.0:
return f'{num:3.1f}{unit}{suffix}'
num /= 1024.0
return f'{num:.1f}Yi{suffix}'
print(sizeof_fmt(1048575)) # 1024.0KiB
1,048,575 bytes is 1023.999 KiB, which is below 1,024, so it stays in KiB, and then :.1f rounds it up to 1024.0. Comparing round(n, 1) instead, or n >= 1023.95 as the one-liner does, moves it to 1.0 MiB.
f-string formats
:.1f fixes one decimal and :, groups thousands, handy for the exact count next to the short one:
n = 123456789
print(f'{format_bytes(n)} ({n:,} bytes)') # 117.7 MiB (123,456,789 bytes)
print(f'{n / 1000**2:.2f} MB') # 123.46 MB
humanize
pip install humanize adds naturalsize, which defaults to decimal units:
import humanize
humanize.naturalsize(123456789) # '123.5 MB'
humanize.naturalsize(123456789, binary=True) # '117.7 MiB'
humanize.naturalsize(123456789, gnu=True) # '117.7M'
humanize.naturalsize(1048575, binary=True) # '1.0 MiB'
humanize.naturalsize(500) # '500 Bytes'
gnu=True uses 1,024 with the single letters ls -h prints. That’s humanize 4.16.0 on Python 3.14.