9

Получить ключ по значению в словаре

12

У меня есть функция, которая ищет возраст в словаре Dictionary и выводит соответствующее имя. Вот код:

dictionary = {'george' : 16, 'amber' : 19}
search_age = raw_input("Введите возраст: ")
for age in dictionary.values():
    if age == search_age:
        name = dictionary[age]
        print(name)

Я знаю, как сравнивать возраст и находить его, но не знаю, как вывести имя человека. Кроме того, у меня возникает ошибка KeyError на пятой строке. Я понимаю, что это неправильно, но не могу разобраться, как сделать поиск в обратном порядке. Подскажите, как исправить эту проблему?

5 ответ(ов)

7

В вашем коде есть несколько моментов, которые стоит поправить. В частности, вы используете input() для получения значения возраста, однако оно будет возвращать строку. Это может привести к тому, что сравнение с age (который является числом) не сработает. Кроме того, метод dict.iteritems() больше не поддерживается в Python 3.x, так что мы просто используем dictionary.items().

Вот исправленный вариант вашего кода:

dictionary = {'george': 16, 'amber': 19}
search_age = int(input("Provide age: "))  # Преобразуем ввод в целое число
for name, age in dictionary.items():
    if age == search_age:
        print(name)

Таким образом, программа будет корректно сравнивать возраст, и вывод имени будет работать. Обратите внимание, что использование dict в данном контексте может быть не самым оптимальным, если вы хотите часто искать по возрасту. Рассмотрите возможность использования специальной структуры данных, если это потребуется в вашем проекте.

3

Если вам нужно как имя, так и возраст, следует использовать метод .items(), который возвращает кортежи вида (ключ, значение):

for name, age in mydict.items():
    if age == search_age:
        print(name)

Вы можете распаковать кортеж в две отдельные переменные прямо в цикле for и затем сравнить возраст.

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

{16: 'george', 19: 'amber'}

Это позволит вам находить имя по возрасту, просто обращаясь к словарю:

mydict[search_age]

Я называю его mydict, а не list, потому что list - это имя встроенного типа, и не стоит использовать его для чего-то другого.

Вы даже можете получить список всех людей с заданным возрастом за одну строку:

[name for name, age in mydict.items() if age == search_age]

Или, если у вас только по одному человеку с каждым возрастом:

next((name for name, age in mydict.items() if age == search_age), None)

Этот код вернёт None, если никого с таким возрастом не окажется.

Наконец, если ваш словарь длинный и вы используете Python 2, стоит рассмотреть использование .iteritems() вместо .items(), как это сделал Cat Plus Plus в своём ответе, так как это не требует создания копии списка.

1

Я подумал, что было бы интересно указать, какие методы работают быстрее и в каких ситуациях:

Вот некоторые тесты, которые я провел (на MacBook Pro 2012 года):

def method1(dict, search_age):
    for name, age in dict.iteritems():
        if age == search_age:
            return name

def method2(dict, search_age):
    return [name for name, age in dict.iteritems() if age == search_age]

def method3(dict, search_age):
    return dict.keys()[dict.values().index(search_age)]

Результаты из profile.run() для каждого метода, тестируемого 100,000 раз:

Метод 1:

>>> profile.run("for i in range(0,100000): method1(dict, 16)")
     200004 вызовов функций за 1.173 секунды

Метод 2:

>>> profile.run("for i in range(0,100000): method2(dict, 16)")
     200004 вызовов функций за 1.222 секунды

Метод 3:

>>> profile.run("for i in range(0,100000): method3(dict, 16)")
     400004 вызовов функций за 2.125 секунды

Из этого видно, что для небольшого словаря метод 1 работает быстрее. Это, скорее всего, связано с тем, что он возвращает первое совпадение, в отличие от метода 2, который возвращает все совпадения (см. примечание ниже).


Интересно, что при проведении тех же тестов на словаре с 2700 записями, результаты получаются совершенно другими (тестировал 10,000 раз):

Метод 1:

>>> profile.run("for i in range(0,10000): method1(UIC_CRS,'7088380')")
     20004 вызова функций за 2.928 секунды

Метод 2:

>>> profile.run("for i in range(0,10000): method2(UIC_CRS,'7088380')")
     20004 вызова функций за 3.872 секунды

Метод 3:

>>> profile.run("for i in range(0,10000): method3(UIC_CRS,'7088380')")
     40004 вызова функций за 1.176 секунды

Здесь метод 3 оказывается гораздо быстрее. Это показывает, что размер вашего словаря влияет на выбор метода.

Примечания:

  • Метод 2 возвращает список всех имен, в то время как методы 1 и 3 возвращают только первое совпадение.
  • Я не рассматривал использование памяти. Неясно, создает ли метод 3 два списка (keys() и values()) и хранит их в памяти.
0

Одна строка: p = dict(zip(i.values(), i.keys()))

Объяснение: i.keys() и i.values() возвращают два списка с ключами и значениями словаря соответственно. Функция zip объединяет эти списки, чтобы создать новый словарь.

Предупреждение: Это будет работать только в том случае, если значения являются хэшируемыми и уникальными.

0

В вашем коде на Python вы используете словарь и хотите найти ключ, соответствующий определенному значению. Вот пояснение по обоим вашим примерам.

  1. В первом примере:
a = {'a': 1, 'b': 2, 'c': 3}
{v: k for k, v in a.items()}[1]

Это создание нового словаря, где значения и ключи поменяны местами. Затем вы сразу обращаетесь к значению по ключу 1. В результате получите 'a', так как 1 — это значение ключа 'a' в оригинальном словаре.

  1. Во втором примере:
{k: v for k, v in a.items() if v == 1}

Здесь вы создаете новый словарь, включающий только те пары ключ-значение из исходного словаря a, где значение равно 1. Результатом будет: {'a': 1}.

Если вам нужно просто получить ключ по значению, более привычным будет:

key = next((k for k, v in a.items() if v == 1), None)

Эта строка вернет ключ, соответствующий значению 1, или None, если такого значения нет.

Чтобы ответить на вопрос, пожалуйста, войдите или зарегистрируйтесь