首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >从字典值中获得第一个值的最佳和最快的方法

从字典值中获得第一个值的最佳和最快的方法
EN

Stack Overflow用户
提问于 2021-12-26 21:27:17
回答 2查看 237关注 0票数 -1

从字典值中获得第一个值的最快和最好的方法是什么?有多种技术,但什么是最好的(内存和速度),为什么?

下面是一些例子。我也希望有其他技巧:

代码语言:javascript
复制
dictis = {
    1: "One"
    , 2: "Two"
    , 3: "Three"
    , 4: "Four"
}

# 1 technique:
#----------------

value = next(iter(dictis.values()))
print(value)

# 2 technique:
#----------------

value = list(dictis.values())[0]
print(value)

# 3 technique:
#----------------

value = [value for value in dictis.values()][0]
print(value)

# 4 technique:
#----------------

value = next(value for value in dictis.values())
print(value)
EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2021-12-26 21:40:16

TL;博士

用第一条路!对于大词典来说,它比第四快2.5倍,比第二(10000倍)和第三(20000倍)快得多,当词典变大时,增加得更多。第二和第三种方法创建完整列表并消耗O(n)内存

时间测量

用于小型词典:

代码语言:javascript
复制
size = 10
dictis = {i: str(i) for i in range(size)}
code1 = "value = next(iter(dictis.values()))"
code2 = "value = list(dictis.values())[0]"
code3 = "value = [value for value in dictis.values()][0]"
code4 = "value = next(value for value in dictis.values())"

kw = {"number": 1000000, "globals": globals()}
print("code1: ", timeit.timeit(code1, **kw)) # 0.15313154199999998
print("code2: ", timeit.timeit(code2, **kw)) # 0.25568722099999996
print("code3: ", timeit.timeit(code3, **kw)) # 0.49292356800000003
print("code4: ", timeit.timeit(code4, **kw)) # 0.43500832200000006

让我们检查一下size = 1_000_000。第二和第三种方法持续时间很长(number = 100)

代码语言:javascript
复制
code1:  1.7961999999982492e-05
code2:  1.607245366
code3:  3.3421025739999997
code4:  6.061599999984679e-05

对于number = 10_000_00

代码语言:javascript
复制
code1:  1.7192588249999998
code4:  4.457956223
票数 2
EN

Stack Overflow用户

发布于 2021-12-26 22:29:32

让我们谈谈几点。首先,Python字典并不总是按标准排序,它取决于版本。在实现方面,它们在Python3.5之前具有任意顺序,这意味着如果在中间时间对字典进行更改,那么在不同时间发出的相同list(dictis)调用可以返回不一致顺序列表。所以,不要在3.5或更低的时间内这样做。Python实际上有几种不同的实现方式,最常见的是CPython,而另一种流行的方法是PyPy。在这两种情况下,字典的实现方式都保持了插入顺序。从3.6开始,CPython就是这样的。记住:字典的顺序只是插入元素的顺序,它与元素的属性无关。

第二,"O(n)“和"O(1)”指的是大-O表示法,这是渐近表示法中的一个主题。维基百科上有关于这些的文章,但它们可以是相当技术性的。我建议阅读一本算法教科书,比如Cormen的。其主旨是:括号内的数字代表一个“乘数”,表示你希望你的算法能执行多少步。如果希望至少循环一次n项列表中的每个项,则算法是"O(n)“(昵称为”线性时间“)。如果您只希望看到1或2或任何数目的项目是固定的,永远不会改变,那么您是"O(1)“(昵称为”恒定时间“)。正如khelwood对你的问题所评论的那样,第一个选择是O(1)。这是因为iter函数向字典返回一个迭代器,而next方法一次只查看一个元素。我不知道字典迭代器在CPython (3.6-3.10)中的实际实现是O(1),但理论上应该是。技巧2和3将字典转换为列表,这意味着他们必须访问每个元素,因此必须采取更多的步骤。技术4使用生成器实现与技术1类似的效果,但考虑起来更复杂,灵活性更低。

不过,技术1和4使用函数values来获得字典值的“视图”。我不知道这是多少工作的引擎盖(这可能是O(n),就我所知),所以我认为我们应该切断中间的人。这应该是一个更好的解决方案:

代码语言:javascript
复制
key = next(iter(dictis)) # this doesn't ask to generate a view object
value = dictis[key]      # this is instant look-up

我做了一些测试,这个解决方案实际上与技术1相当,所以生成字典值的视图似乎不需要太多开销。使用与kosciej16 16的答案相同的测试,使用size = 1_000_000number = 10_000

代码语言:javascript
复制
code5 = '''
key = next(iter(dictis))
value = dictis[key]
'''

我得到了

代码语言:javascript
复制
>>> print("code1: ", timeit.timeit(code1, **kw))
code1:  0.002648300000146264
>>> print("code4: ", timeit.timeit(code4, **kw))
code4:  0.0054308000001128676
>>> print("code5: ", timeit.timeit(code5, **kw))
code5:  0.0028101000000333443

但我可以提供一个更好的解决方案,但这需要更多的工作吗?如果您想要跟踪元素被插入到集合中的顺序,那么使用队列会给您带来很多好处。你可以查阅有关算法教科书的细节。基本上,它是一种数据结构,可以跟踪插入的顺序。您可能需要将数据存储在对象中,这在内存方面更为昂贵。

代码语言:javascript
复制
class Objectis():
    def __init__(self, id, data):
        self.id = id
        self.data = data

然后制作一堆这样的东西:

代码语言:javascript
复制
item1 = Objectis(1, "One")
item2 = Objectis(2, "Two")
item3 = Objectis(3, "Three")
item4 = Objectis(4, "Four")

将它们添加到任何顺序的列表中:

代码语言:javascript
复制
queue = [] # you should start your queue empty
queue.append(item3)
queue.append(item1)
queue.append(item2)
queue.append(item4)

如果你想知道谁是第一个,你可以立即使用queue[0]。如果你想知道谁是最后一个,就做queue[-1]吧。如果要删除第一个元素,可以执行element = queue.pop(0)。如果要在第一个位置插入元素,可以执行queue.insert(0, element)

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/70489969

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档