如何迭代字典 - 每次n个键值对

7
我有一个含有成千上万元素的大型字典。我需要以此字典作为参数执行一个函数。现在,我想要将这个函数按照批次执行,每次传入 x 个键值对给函数。
我的做法如下:
mydict = ##some large hash
x = ##batch size
def some_func(data):
    ##do something on data
temp = {}
for key,value in mydict.iteritems():
        if len(temp) != 0 and len(temp)%x == 0:
                some_func(temp)
                temp = {}
                temp[key] = value
        else:
                temp[key] = value
if temp != {}:
        some_func(temp)

这对我来说看起来很粗糙。我想知道是否有更优雅/更好的方法来完成这个任务。

你可以尝试使用这个(字典的子字典)或者这个(分割生成器) - tobias_k
3个回答

16

我经常使用这个小工具:

import itertools

def chunked(it, size):
    it = iter(it)
    while True:
        p = tuple(itertools.islice(it, size))
        if not p:
            break
        yield p

针对您的使用情况:

for chunk in chunked(big_dict.iteritems(), batch_size):
    func(chunk)

嗨,乔治。感谢你的回答。请问你能解释一下“chunked”方法的性能吗?它比我分享的解决方案更好吗? - nish
@nish:我猜它应该是高效的。itertools 是用 C 写的,比 Python 快得多。 - georg
1
在Python3中,应该使用big_dict.items()而不是big_dict.iteritems() - Cherona

1
这里有两个解决方案,是我早先回答的问题所改编的。你可以从字典中获取items列表,并从该列表的切片创建新的dict。然而,这并不是最优的解决方案,因为它会复制大量的字典内容。
def chunks(dictionary, size):
    items = dictionary.items()
    return (dict(items[i:i+size]) for i in range(0, len(items), size))

或者,您可以使用一些itertools模块的函数在循环时生成新的子字典。这类似于@georg的答案,只是使用了for循环。

from itertools import chain, islice
def chunks(dictionary, size):
    iterator = dictionary.iteritems()
    for first in iterator:
        yield dict(chain([first], islice(iterator, size - 1)))

示例用法。对于这两种情况:

mydict = {i+1: chr(i+65) for i in range(26)}
for sub_d in chunks2(mydict, 10):
    some_func(sub_d)

0

来自more-itertools

def chunked(iterable, n):
    """Break an iterable into lists of a given length::
        >>> list(chunked([1, 2, 3, 4, 5, 6, 7], 3))
        [[1, 2, 3], [4, 5, 6], [7]]
    If the length of ``iterable`` is not evenly divisible by ``n``, the last
    returned list will be shorter.
    This is useful for splitting up a computation on a large number of keys
    into batches, to be pickled and sent off to worker processes. One example
    is operations on rows in MySQL, which does not implement server-side
    cursors properly and would otherwise load the entire dataset into RAM on
    the client.
    """
    # Doesn't seem to run into any number-of-args limits.
    for group in (list(g) for g in izip_longest(*[iter(iterable)] * n,
                                                fillvalue=_marker)):
        if group[-1] is _marker:
            # If this is the last group, shuck off the padding:
            del group[group.index(_marker):]
        yield group

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接