【问题标题】:Splitting Dictionary on Bytes按字节拆分字典
【发布时间】:2022-01-20 20:01:04
【问题描述】:

我有一些 python 代码:

  1. 从不同端点提取各种指标
  2. 使用一些标准化的键/值将它们加入到一个通用字典中
  3. 将字典上传到另一个工具进行分析

虽然这通常有效,但当字典变得太大时会出现问题,这会导致各个方面的性能问题。

我已经看到使用 itertools 根据键的范围进行拆分,根据键的数量进行平均拆分的示例。但是,我想尝试根据字节大小对其进行拆分,因为某些指标比其他指标大得多。

可以根据字节大小将字典动态拆分为字典列表吗?

【问题讨论】:

  • 那本词典是如何“上传”的?当您说“太大”时,您是指在目标机器上传输数据还是处理数据?
  • 你能给个样品吗?例如键是字符串吗?
  • @a_guest - 字典被 POST 到 API 网关,将其保存为文件。然后该文件被另一个平台摄取。虽然 API 网关的默认限制为 10MB 是我可以通过其他方式解决的一个问题,但我们注意到平台中的性能问题,上传到分析平台的 5-6MB 上传。
  • @MadPhysicist - 键/值对各不相同。键名都是字符串,值可以是整数、字符串或嵌套的字典/字典列表。它是嵌套的字典列表,有时可以任意增长。
  • @stranjer。我已经更新了我的答案。只要你有足够的物品可以在顶层拆分,它就可以工作。

标签: python python-3.x list dictionary byte


【解决方案1】:

假设键和值都是可以以有意义的方式调用 sys.getsizeof 的合理类型以及所有不同的对象,您可以使用该信息将字典拆分为相等的块。

如果您希望最大块作为其除数,请首先计算总大小。如果您的最大尺寸在外部是固定的,则可以跳过此步骤:

total_size = sum(getsizeof(k) + getsizeof(v) for k, v in my_dict.items())

现在您可以迭代字典,假设整个字典的大小大致随机分布,在超过 max_size 阈值之前剪切一个新字典:

from sys import getsizeof

def split_dict(d, max_size):
    result = []
    current_size = 0
    current_dict = {}
    while d:
        k, v = d.popitem()
        increment = getsizeof(k) + getsizeof(v)
        if increment + current_size > max_size:
            result.append(current_dict)
            if current_size:
                current_dict = {k: v}
                current_size = increment
            else:
                current_dict[k] = v  # going to list
                current_dict = {}
                current_size = 0
        else:
            current_dict[k] = v
            current_size += increment
    if current_dict:
        result.append(current_dict)
    return result

请记住,dict.popitem 具有描述性:您实际上是从my_dict 中删除所有内容以填充较小的版本。

这是一个高度简化的例子:

>>> from string import ascii_letters
>>> d = {s: i for i, s in enumerate(ascii_letters)}
>>> total_size = sum(getsizeof(k) + getsizeof(v) for k, v in d.items())
>>> split_dict(d, total_size // 5)
[{'Z': 51, 'Y': 50, 'X': 49, 'W': 48, 'V': 47, 'U': 46, 'T': 45, 'S': 44, 'R': 43, 'Q': 42},
 {'P': 41, 'O': 40, 'N': 39, 'M': 38, 'L': 37, 'K': 36, 'J': 35, 'I': 34, 'H': 33, 'G': 32},
 {'F': 31, 'E': 30, 'D': 29, 'C': 28, 'B': 27, 'A': 26, 'z': 25, 'y': 24, 'x': 23, 'w': 22},
 {'v': 21, 'u': 20, 't': 19, 's': 18, 'r': 17, 'q': 16, 'p': 15, 'o': 14, 'n': 13, 'm': 12},
 {'l': 11, 'k': 10, 'j': 9, 'i': 8, 'h': 7, 'g': 6, 'f': 5, 'e': 4, 'd': 3, 'c': 2},
 {'b': 1, 'a': 0}]

如您所见,拆分在分布方面不一定是最佳的,但它确保没有块大于max_size,除非单个条目需要更多字节。

更新不健全的价值观

如果您有任意大的嵌套值,您仍然可以在顶层拆分,但是,您必须将 getsizeof(v) 替换为更健壮的东西。例如:

from collections.abc import Mapping, Iterable

def xgetsizeof(x):
    if isinstance(x, Mapping):
        return getsizeof(x) + sum(xgetsizeof(k) + xgetsizeof(v) for k, v in x.items())
    if isinstance(x, Iterable) and not isintance(x, str):
        return getsizeof(x) + sum(xgetizeof(e) for e in x)
    return getsizeof(x)

现在您还可以通过一次调用计算total_size

total_size = xgetsizeof(d)

请注意,这比您之前看到的值要大。较早的结果是

xgetsizeof(d) - getsizeof(d)

要使解决方案真正健壮,您需要添加实例跟踪以避免循环引用和重复计算。

我继续为我的库haggis 编写了这样一个函数,称为haggis.objects.getsizeof。它的行为很像上面的xgetsizeof,但更加健壮。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-10-26
    • 1970-01-01
    • 1970-01-01
    • 2021-09-09
    • 2021-11-17
    • 1970-01-01
    相关资源
    最近更新 更多