从字节大小返回人类可读大小的函数:

>>> human_readable(2048)
'2 kilobytes'
>>>

如何做到这一点?


当前回答

你应该用humanize。

>>> humanize.naturalsize(1000000)
'1.0 MB'
>>> humanize.naturalsize(1000000, binary=True)
'976.6 KiB'
>>> humanize.naturalsize(1000000, gnu=True)
'976.6K'

参考: https://pypi.org/project/humanize/

其他回答

您将在下面发现的决不是已经发布的解决方案中性能最好或最短的解决方案。相反,它专注于一个许多其他答案都忽略的特定问题。

即输入如999_995时的情况:

Python 3.6.1 ...
...
>>> value = 999_995
>>> base = 1000
>>> math.log(value, base)
1.999999276174054

哪个,被截断为最近的整数,并应用回输入给出

>>> order = int(math.log(value, base))
>>> value/base**order
999.995

这似乎正是我们所期望的,直到我们被要求控制输出精度。这就是事情开始变得有点困难的时候。

将精度设置为2位,我们得到:

>>> round(value/base**order, 2)
1000 # K

而不是1M。

我们该如何应对呢?

当然,我们可以显式地检查它:

if round(value/base**order, 2) == base:
    order += 1

但我们能做得更好吗?在我们做最后一步之前,我们能知道订单应该怎么削减吗?

事实证明我们可以。

假设0.5十进制舍入规则,则上述if条件转化为:

导致

def abbreviate(value, base=1000, precision=2, suffixes=None):
    if suffixes is None:
        suffixes = ['', 'K', 'M', 'B', 'T']

    if value == 0:
        return f'{0}{suffixes[0]}'

    order_max = len(suffixes) - 1
    order = log(abs(value), base)
    order_corr = order - int(order) >= log(base - 0.5/10**precision, base)
    order = min(int(order) + order_corr, order_max)

    factored = round(value/base**order, precision)

    return f'{factored:,g}{suffixes[order]}'

>>> abbreviate(999_994)
'999.99K'
>>> abbreviate(999_995)
'1M'
>>> abbreviate(999_995, precision=3)
'999.995K'
>>> abbreviate(2042, base=1024)
'1.99K'
>>> abbreviate(2043, base=1024)
'2K'

使用1000或kibibytes的幂将更符合标准:

def sizeof_fmt(num, use_kibibyte=True):
    base, suffix = [(1000.,'B'),(1024.,'iB')][use_kibibyte]
    for x in ['B'] + map(lambda x: x+suffix, list('kMGTP')):
        if -base < num < base:
            return "%3.1f %s" % (num, x)
        num /= base
    return "%3.1f %s" % (num, x)

附注:永远不要相信一个以K(大写)后缀打印数千的库。

我最近提出了一个避免循环的版本,使用log2来确定大小顺序,作为后缀列表的移位和索引:

from math import log2

_suffixes = ['bytes', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB']

def file_size(size):
    # determine binary order in steps of size 10 
    # (coerce to int, // still returns a float)
    order = int(log2(size) / 10) if size else 0
    # format file size
    # (.4g results in rounded numbers for exact matches and max 3 decimals, 
    # should never resort to exponent values)
    return '{:.4g} {}'.format(size / (1 << (order * 10)), _suffixes[order])

不过,它的可读性很可能被认为是非python化的。

重复作为匆匆.filesize()替代方案提供的代码段,下面的代码段根据所使用的前缀给出不同的精度数字。它不像某些片段那样简洁,但我喜欢这样的结果。

def human_size(size_bytes):
    """
    format a size in bytes into a 'human' file size, e.g. bytes, KB, MB, GB, TB, PB
    Note that bytes/KB will be reported in whole numbers but MB and above will have greater precision
    e.g. 1 byte, 43 bytes, 443 KB, 4.3 MB, 4.43 GB, etc
    """
    if size_bytes == 1:
        # because I really hate unnecessary plurals
        return "1 byte"

    suffixes_table = [('bytes',0),('KB',0),('MB',1),('GB',2),('TB',2), ('PB',2)]

    num = float(size_bytes)
    for suffix, precision in suffixes_table:
        if num < 1024.0:
            break
        num /= 1024.0

    if precision == 0:
        formatted_size = "%d" % num
    else:
        formatted_size = str(round(num, ndigits=precision))

    return "%s %s" % (formatted_size, suffix)

现代Django有自模板标签filesizeformat:

将值格式化为人类可读的文件大小(即:“13 KB”,“4.1 MB”,“102字节”等)。

例如:

{{ value|filesizeformat }}

如果值是123456789,输出将是117.7 MB。

更多信息:https://docs.djangoproject.com/en/1.10/ref/templates/builtins/#filesizeformat