Получить ключ по значению в словаре
У меня есть функция, которая ищет возраст в словаре 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 ответ(ов)
В вашем коде есть несколько моментов, которые стоит поправить. В частности, вы используете 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
в данном контексте может быть не самым оптимальным, если вы хотите часто искать по возрасту. Рассмотрите возможность использования специальной структуры данных, если это потребуется в вашем проекте.
Если вам нужно как имя, так и возраст, следует использовать метод .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 в своём ответе, так как это не требует создания копии списка.
Я подумал, что было бы интересно указать, какие методы работают быстрее и в каких ситуациях:
Вот некоторые тесты, которые я провел (на 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()
) и хранит их в памяти.
Одна строка: p = dict(zip(i.values(), i.keys()))
Объяснение: i.keys()
и i.values()
возвращают два списка с ключами и значениями словаря соответственно. Функция zip
объединяет эти списки, чтобы создать новый словарь.
Предупреждение: Это будет работать только в том случае, если значения являются хэшируемыми и уникальными.
В вашем коде на Python вы используете словарь и хотите найти ключ, соответствующий определенному значению. Вот пояснение по обоим вашим примерам.
- В первом примере:
a = {'a': 1, 'b': 2, 'c': 3}
{v: k for k, v in a.items()}[1]
Это создание нового словаря, где значения и ключи поменяны местами. Затем вы сразу обращаетесь к значению по ключу 1
. В результате получите 'a'
, так как 1 — это значение ключа 'a' в оригинальном словаре.
- Во втором примере:
{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
, если такого значения нет.
Итерация по словарям с использованием циклов 'for'
Создание словаря (dict) из отдельных списков ключей и значений
Преобразование строки, представляющей словарь, в сам словарь
Преобразование списка словарей в DataFrame pandas
Почему использовать dict.get(key) вместо dict[key]?