Вопрос по python – Pythonic способ доступа к произвольному элементу из словаря

23

У меня есть словарь, полный предметов. Я хочу посмотреть на один, произвольный элемент:

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 interator. An improvement.
    for item in dictionary.itervalues():
        return item

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

Я нахожусь в состоянии, когда производительность не настолько критична, что это имеет значение (пока), поэтому я могу быть обвинен в преждевременной оптимизации, но я пытаюсь улучшить свой стиль кодирования Python, поэтому, если есть легко понятный вариант было бы хорошо принять его.

@ sgerg Я собирался представить это, но ты продолжаешь. : D jamylak
если хочешь заглянутьпредме (в отличие от значения) вы должны использоватьiteritems неitervalues / nitpick / moooeeeep
Как насчетdictionary.itervalues().next()? Это было бы лучше, чем ваш второйarb функция. srgerg
Никакой дополнительной работы, когда вы знаете, что вам это не нужно, не является преждевременной оптимизацией. Это эффективность. Chris Morgan
Должны ли они быть разными предметами во время разговора? Все это вернет один и тот же элемент ... the wolf

Ваш Ответ

4   ответа
27

но, на мой взгляд, немного более очевидно:

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

Это работает как в Python 2, так и в Python 3, но в Python 2 более эффективно сделать это следующим образом:

return next(dictionary.itervalues())
Следует отметить, что это вызывает StopIteration, если dict пуст. yak
Но если вы хотите избежатьStopIteration, вы можете указать значение по умолчанию, например,next(dictionary.itervalues(), None). Chris Morgan
Я не уверен, что вижу это как «гораздо более очевидное», но определенные моменты для подбора в одном выражении. Oddthinking
Я обновил ваш ответ для современного Python 3 раза, потому что это все еще популярный вопрос. Если вы недовольны внесенными мною изменениями, не стесняйтесь откатываться или редактировать по своему вкусу. Aran-Fey
@ Нечетное мышление: если тыявляютс просто хочу один элемент от итератора,next это путь. Нет смысла вfor a in b: return c или имеющий безусловныйbreak оператор в первой итерации цикла. Chris Morgan
10

Избегая всегоvalues/itervalues/viewvalues беспорядок, это одинаково хорошо работает в Python2 или Python3

dictionary[next(iter(dictionary))]

Альтернативно, если вы предпочитаете выражения генератора

next(dictionary[x] for x in dictionary)
ты мог бы просто сделатьnext(iter(dictionary.values())) Ev. Kounis
@ Ev.Kounis, но в python2 это создает дополнительный список. John La Rooy
@ user2357112, ах да, ты прав. Я удалю это, поскольку некоторые люди все еще пытаются поддерживать 2 и John La Rooy
map также создает список в Python 2. user2357112
0

Почему бы не использоватьrandom?

import random

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

Это очень ясно показывает, что результат должен быть чисто произвольным, а не побочным эффектом реализации. До тех пор, пока производительность не станет актуальной проблемой, всегда идите с ясностью по скорости.

Жаль, что dict_values не поддерживает индексацию, было бы неплохо вместо этого передать представление значений.

Update: так как все так одержимы производительностью, приведенной выше функции требуется <120 мс, чтобы вернуть случайное значение из разряда в 1 миллион предметов. Доверие к чистому коду - это не удивительный удар по производительност

Где было указано, что выбранный элемент не должен быть случайным, это пустая трата времени. Строка документа (и имя!) Вполне достаточна для таких наблюдений. Chris Morgan
Если имя «произвольное» и действие состоит в том, чтобы перебирать ключи, это имя мне не будет понятно, поэтому да, необходима строка документации. Если вам нужно написать строку документации, чтобы объяснить, почему ваш код делает что-то другое, чем кажется, возможно, ответ - написать более четкий код. Matthew Trevor
120 мс совершенно неприемлемо для такой операции. Это должно закончиться в течение микросекунды. Вы положили это туда, где вы ранее использовалиd[next(iter(d))] и ваше приложение может работать буквально в миллион раз медленнее. user2357112
2

что на этот вопрос был получен значительный ответ, но, надеюсь, это сравнение позволит пролить некоторый свет на чистый код и время компромисс

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 раз быстрее, чем создание списка и выбор случайного объекта из него.

Похожие вопросы