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

时间:2016-06-18 22:49:23

标签: 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”,则保证顺序。我想知道的是,如果退货声明保证这一点;基本上,它是否是线程安全的?

以下“adict”构造对于多线程还是不需要更安全?

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

1 个答案:

答案 0 :(得分:1)

我一直在研究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_FAST将对adict的引用推送到堆栈。然后我们用该参数调用dict。我们不知道的是dict()函数是否是原子的。

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

我读到的一切都说内部类型是线程安全的。所以我相信单个函数调用字典会在内部保持一致。即,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()稍快一些。)