如何在Python中迭代dict代理?
我使用Python的如何在Python中迭代dict代理?,python,dictionary,multiprocessing,shared-memory,Python,Dictionary,Multiprocessing,Shared Memory,我使用Python的multiprocessing.Manager共享对一个进程将生成的数据集的访问,其他进程将查看该数据集。但是,我遇到了一个问题,即manager.dict()返回的dict代理不支持iteritems() 我可以迭代items(),但这意味着要构造dict中所有项的新元组,这是一个很大的数字。有没有一种方法可以不构建中间列表/元组,从而只使用恒定数量的额外内存 注意:如果解决方案要求生成过程暂停以进行迭代,则可以。iteritems()用于列表dict。您可以使用for循环
multiprocessing.Manager
共享对一个进程将生成的数据集的访问,其他进程将查看该数据集。但是,我遇到了一个问题,即manager.dict()
返回的dict代理不支持iteritems()
我可以迭代items()
,但这意味着要构造dict中所有项的新元组,这是一个很大的数字。有没有一种方法可以不构建中间列表/元组,从而只使用恒定数量的额外内存
注意:如果解决方案要求生成过程暂停以进行迭代,则可以。iteritems()
用于列表dict。您可以使用for循环。或者您可以说sorted()
,它将返回排序列表中的键,然后迭代该列表并执行dict[key]
。希望有帮助。如果有更好的办法。一定要和我分享。我很想知道。您可以使用该类注册自己的类型。然后,您可以在该类型上实现方法,例如,从dict中仅获取有限数量的项
下面是一个让您开始学习的示例:
import multiprocessing
from multiprocessing import managers
class TakerDict(dict):
"""Like a dict, but allows taking a limited number of items."""
def take(self, items=1):
"""Take the first `items` items."""
return [item for _, item in zip(range(items), self.items())]
# NOTE: add other dict methods to the tuple if you need them.
TakerProxy = managers.MakeProxyType('TakerProxy', ('take',))
managers.SyncManager.register('taker', TakerDict, TakerProxy)
if __name__ == '__main__':
manager = multiprocessing.Manager()
taker = manager.taker()
# in other processes, use e.g. taker.take(5)
因此,为了限制内存使用,您必须反复调用管理器进程以获取下一批元素
然而,要做到这一点,您的dict必须支持索引(以便您可以从特定偏移量恢复)。由于您无法访问dict中元素的基本顺序,因此最好使用列表(例如
manager.list()
)。然后在您的子流程中,请求列表的len()
,并按片索引以获得适当大小的批-您不需要为此注册任何代理类型。您可以迭代键()
,以减少内存占用。您必须防止密钥被删除
否则,这里有一个示例,它有两种不同的方式,可以让您迭代dict中的项。本示例中的iteritems()
方法仅适用于创建管理器对象的进程和管理器对象创建的子进程。这是因为创建新代理需要manager对象,而其他进程无权访问它。iteritems2()
方法在其他进程中工作,因为它不依赖于在这些进程中创建新代理
import multiprocessing as mp
import multiprocessing.managers
class mydict(dict):
def __init__(self, *args, **kwargs):
dict.__init__(self, *args, **kwargs)
self.iters = {}
def iteritems(self):
print "iteritems", mp.current_process()
return dict.iteritems(self)
def _iteritems_start(self):
print "_iteritems_start", mp.current_process()
i = dict.iteritems(self)
self.iters[id(i)] = i
return id(i)
def _iteritems_next(self, iter_id):
try:
return self.iters[iter_id].next()
except StopIteration:
del self.iters[iter_id]
return None
class mydict_proxy(mp.managers.DictProxy):
def iteritems(self):
print "iteritems proxy", mp.current_process()
return self._callmethod("iteritems")
def iteritems2(self):
print "iteritems2 proxy", mp.current_process()
iter_id = self._callmethod("_iteritems_start")
def generator():
while True:
a = self._callmethod("_iteritems_next",
(iter_id,))
if a == None:
return
yield a
return generator()
_method_to_typeid_ = { "iteritems": "Iterator" }
_exposed_ = mp.managers.DictProxy._exposed_
_exposed_ += ("iteritems", "_iteritems_start", "_iteritems_next")
class mymanager(mp.managers.BaseManager):
pass
mymanager.register("mydict", mydict, mydict_proxy)
mymanager.register("Iterator", proxytype = mp.managers.IteratorProxy,
create_method = False)
def other(d):
for k, v in d.iteritems2():
d[k] = v.lower()
for k, v in d.iteritems():
d[k] = ord(v)
def main():
manager = mymanager()
manager.start()
d = manager.mydict(list(enumerate("ABCDEFGHIJKLMNOP")))
for (k, v) in d.iteritems():
print k, v
proc = mp.Process(target = other, args = (d,))
proc.start()
proc.join()
for (k, v) in d.iteritems():
print k, v
if __name__ == "__main__":
main()
请注意,虽然这段代码的内存效率可能更高,但速度可能会慢得多。您是否考虑过使用
SyncManager
并在那里注册自己的代理,使用iteritems
exposed?@oleg您不能简单地公开iteritems,因为它返回的dict迭代器是不可pickle的。这就是为什么默认的dict代理不公开它,这就是问题所在。我没有说“简单地”公开。:)我们能否使用IteratorProxy
来公开iteritems
?@oleg如果我的回答似乎不屑一顾,那么很抱歉。我确信某种代理是一种解决方案,但我不知道如何构造一种。您基本上不是在实施我在问题中提到的“转换为列表”解决方案,而是以一种有点复杂的方式?这根本不能解决(由于也需要一个列表而导致的内存使用)问题。好的,这最终会将数据转换为列表,因此会带来内存开销。它只是分块执行,这样你就不会有那么多开销。我不认为它的性能比IteratorProxy
方法差,但我没有衡量任何东西。除了它实际上不做块:“但是,要做到这一点,您的dict必须支持索引(这样您就可以从特定偏移量恢复)。”您是对的。我现在明白了这实际上并不能解决您的问题(当然,除非您将数据类型从dict更改为list,这可能并不理想)我应该删除这个答案吗?还是它仍然有用?我认为它可以帮助一些人找到真正的解决方案。