用Python从字典中提取键和值(以线程安全的方式)

用Python从字典中提取键和值(以线程安全的方式),python,dictionary,thread-safety,Python,Dictionary,Thread Safety,我有一个从字典中提取键和值的简单函数 def separate_kv_fast(adict): '''Separates keys/values from a dictionary to corresponding arrays''' return adict.keys(), adict.values() 我知道如果在.keys()和.values()调用之间不修改字典“adict”,那么顺序是有保证的。我想知道的是,返回语句是否保证了这一点;基本上,它是线程安全的吗 以下“

我有一个从字典中提取键和值的简单函数

def separate_kv_fast(adict):
    '''Separates keys/values from a dictionary to corresponding arrays'''
    return adict.keys(), adict.values() 
我知道如果在.keys()和.values()调用之间不修改字典“adict”,那么顺序是有保证的。我想知道的是,返回语句是否保证了这一点;基本上,它是线程安全的吗

以下“adict”结构对于多线程是否更安全

def separate_kv_fast(adict):
    '''Separates keys/values from a dictionary to corresponding arrays'''
    bdict = dict(adict)
    return bdict.keys(), bdict.values() 

我一直在学习python反汇编,我相信这表明这两个调用不是原子的:

>>> dis.dis(separate_kv_fast)                                                                             
  2           0 LOAD_FAST                0 (adict)
              3 LOAD_ATTR                0 (keys)
              6 CALL_FUNCTION            0
              9 LOAD_FAST                0 (adict)
             12 LOAD_ATTR                1 (values)
             15 CALL_FUNCTION            0
             18 BUILD_TUPLE              2
             21 RETURN_VALUE        
>>> 
它跨多个操作码调用键和值,我相信这表明它不是原子的

让我们看看您的
bdict=dict(adict)
是如何工作的:

  2           0 LOAD_GLOBAL              0 (dict)
              3 LOAD_FAST                0 (adict)
              6 CALL_FUNCTION            1
              9 STORE_FAST               1 (bdict)
LOAD\u FAST
将对
adict
的引用推送到堆栈上。然后我们用这个参数调用
dict
。我们不知道的是
dict()
函数是否是原子函数

bdict=adict.copy()
给出了类似的反汇编
adict.copy
无法反汇编

我读到的所有东西都说内部类型是线程安全的。所以我相信对字典的单个函数调用在内部是一致的。i、 例如,
items()
copy()
values()
keys()
,等等。两个串行调用(
values()
后跟
keys()
不一定是安全的。迭代器也不一定是安全的

您不只是使用
items()
,有什么原因吗

我很好奇,所以继续进行基准测试:

#!/usr/bin/python
import timeit
import random

D = dict()
for x in xrange(0, 1000):
    D[x] = str(x)

def a():
    return D.keys(), D.values()

def b():
    keys = []
    values = []
    for k, v in D.items():
        keys.append(k)
        values.append(v)
    return keys, values

def c():
    d = D.copy()
    return d.keys(), d.values()

def d():
    return zip(*D.items())

print timeit.timeit("a()", 'from __main__ import a')
print timeit.timeit("b()", 'from __main__ import b')
print timeit.timeit("c()", 'from __main__ import c')
print timeit.timeit("d()", 'from __main__ import d')
结果:

6.56165385246
145.151810169
19.9027020931
65.4051799774

副本是最快的原子副本(可能比使用dict()稍快一些)。

反汇编版本无疑增加了洞察力。我的第一个版本generate_kv()使用.items()/.iteritems()这当然更安全,但需要对它们进行迭代,以提取键和值,但速度会有所下降。这并不多,但我很好奇,特别是因为我正在考虑使用多处理或多线程来生成字典。谢谢!
iteritems()
我希望它会有问题,因为它是可重入的。
items()
只返回一个键值对列表。我希望它和
copy()一样安全(而且速度慢)
您意识到如果使用
多处理
,对象不会在进程中共享?因此字典的并发性不是问题。您可能对我的回答感兴趣——这是通过
多处理
生成的一个大列表。