【问题标题】:Pythonic way to access arbitrary element from dictionary [duplicate]从字典中访问任意元素的Pythonic方法[重复]
【发布时间】:2012-05-22 13:07:08
【问题描述】:

我有一本字典,里面有很多东西。我想偷看一个任意的项目:

print("Amongst our dictionary's items are such diverse elements as: %s" % arb(dictionary))

我不在乎哪个项目。它不需要是随机的

我可以想出很多方法来实现这一点,但它们似乎都很浪费。我想知道是否有任何 Python 中的首选习语,或者(甚至更好)如果我缺少一个。

def arb(dictionary):
# Creates an entire list in memory. Could take a while.
    return list(dictionary.values())[0]

def arb(dictionary):
# Creates an entire iterator. An improvement.
    for item in dictionary.values():
        return item

def arb(dictionary):
# No iterator, but writes to the dictionary! Twice!
    key, value = dictionary.popitem()
    dictionary[key] = value
    return value

我处于一个性能不够关键的位置,这很重要(还),所以我可能会被指责为过早优化,但我正在努力改进我的 Python 编码风格,所以如果有一个容易理解的变体,采用它会很好。

【问题讨论】:

  • dictionary.itervalues().next() 怎么样?这至少会比你的第二个arb 函数更好。
  • @sgerg 我本来打算提交的,但你继续。 :D
  • 在通话中它们是否需要是不同的项目?所有这些都将返回相同的项目...
  • 当你知道你不需要做额外的工作时不做不是过早的优化。这是效率。
  • 如果你想查看一个 item(与值相反)你应该使用iteritems 而不是itervalues /nitpick/

标签: python


【解决方案1】:

在我看来,类似于您的第二个解决方案,但更明显:

return next(iter(dictionary.values()))

这在 python 2 和 python 3 中都有效,但在 python 2 中这样做更有效:

return next(dictionary.itervalues())

【讨论】:

  • 需要注意的是,如果 dict 为空,这会引发 StopIteration。
  • 但是如果你想避免StopIteration,你可以指定一个默认值,例如next(dictionary.itervalues(), None).
  • 我不确定我是否认为它“更明显”,但适合单个表达式的明确要点。
  • @Oddthinking:如果你只想要一个迭代器中的单个元素,next 是要走的路。 for a in b: return c 或在循环的第一次迭代中有无条件的 break 语句没有意义。
  • 我已将您对现代 python 的答案更新了 3 次,因为这仍然是一个热门问题。如果您对我所做的更改不满意,请随时回滚或根据自己的喜好进行编辑。
【解决方案2】:

避免整个values/itervalues/viewvalues混乱,这在Python2或Python3中同样适用

dictionary[next(iter(dictionary))]

如果您更喜欢生成器表达式,也可以选择

next(dictionary[x] for x in dictionary)

【讨论】:

  • 你也可以简单地做next(iter(dictionary.values()))
  • @Ev.Kounis,但在 python2 中,这会创建一个额外的列表。
  • map 也在 Python 2 中创建一个列表。
  • @user2357112,是的,你是对的。我将删除那个,因为有些人仍在尝试支持 2 和 3
【解决方案3】:

我相信这个问题已经得到了显着的回答,但希望这个比较能够阐明干净代码与时间的权衡:

from timeit import timeit
from random import choice
A = {x:[y for y in range(100)] for x in range(1000)}
def test_pop():
    k, v= A.popitem()
    A[k] = v

def test_iter(): k = next(A.iterkeys())

def test_list(): k = choice(A.keys())

def test_insert(): A[0] = 0

if __name__ == '__main__':
    print('pop', timeit("test_pop()", setup="from __main__ import test_pop", number=10000))
    print('iter', timeit("test_iter()", setup="from __main__ import test_iter", number=10000))
    print('list', timeit("test_list()", setup="from __main__ import test_list", number=10000))
    print('insert', timeit("test_insert()", setup="from __main__ import test_insert", number=10000))

结果如下:

('pop', 0.0021750926971435547)
('iter', 0.002003908157348633)
('list', 0.047267913818359375)
('insert', 0.0010859966278076172)

似乎使用 iterkeys 只比弹出一个项目并重新插入快一点,但比创建列表并从中选择一个随机对象快 10 倍。

【讨论】:

    【解决方案4】:

    为什么不使用random

    import random
    
    def arb(dictionary):
        return random.choice(dictionary.values())
    

    这很清楚地表明结果是纯粹的任意而不是实现的副作用。在性能成为实际问题之前,始终保持清晰而不是速度。

    很遗憾 dict_values 不支持索引,如果能够传入 value 视图就好了。

    更新:由于每个人都对性能如此着迷,因此上述函数需要

    【讨论】:

    • 已经指定选择的元素不需要是随机的,这是浪费时间。一个文档字符串(和名称!)对于这样的观察是完全足够的。
    • 如果名称是“任意”并且操作是遍历键,那么我不会清楚该名称,所以是的,文档字符串是必要的。如果你必须编写一个文档字符串来解释为什么你的代码做了一些看起来不一样的事情,也许答案是编写更清晰的代码。
    • 120 毫秒对于这样的操作是完全不可接受的。这应该在一微秒内完成。你把它放在你以前使用d[next(iter(d))] 的地方,你的应用程序可能会慢一百万倍。
    猜你喜欢
    • 2011-03-07
    • 2014-09-13
    • 1970-01-01
    • 2019-05-03
    • 2013-03-22
    • 2015-08-19
    相关资源
    最近更新 更多