Python: что лучший способ проверить несколько ключей в словаре?
мой dict выглядит как
d = {
'name': 'name',
'date': 'date',
'amount': 'amount',
...
}
Я хочу проверить, существуют ли name
и amount
, поэтому я сделаю
if not `name` in d and not `amount` in d:
raise ValueError # for example
Предположим, что я получаю данные из api, и я хочу проверить, существует ли 10
для этих полей в словаре или нет.
Разве это лучший способ поиска?
Ответы
Ответ 1
Вы можете использовать set
пересечения:
if not d.viewkeys() & {'amount', 'name'}:
raise ValueError
В Python 3 это будет:
if not d.keys() & {'amount', 'name'}:
raise ValueError
потому что .keys()
возвращает dict view по умолчанию. Объекты словарного представления, такие как возвращаемые .viewkeys()
(и .keys()
в Python 3), действуют как множества, и тестирование пересечений очень эффективно.
Демо в Python 2.7:
>>> d = {
... 'name': 'name',
... 'date': 'date',
... 'amount': 'amount',
... }
>>> not d.viewkeys() & {'amount', 'name'}
False
>>> del d['name']
>>> not d.viewkeys() & {'amount', 'name'}
False
>>> del d['amount']
>>> not d.viewkeys() & {'amount', 'name'}
True
Обратите внимание, что это проверяет истину, только если оба ключа отсутствуют. Если вам нужно пройти тест, если один из них отсутствует, используйте:
if not d.viewkeys() >= {'amount', 'name'}:
raise ValueError
что ложно, только если присутствуют оба ключа:
>>> d = {
... 'name': 'name',
... 'date': 'date',
... 'amount': 'amount',
... }
>>> not d.viewkeys() >= {'amount', 'name'}
False
>>> del d['amount']
>>> not d.viewkeys() >= {'amount', 'name'})
True
Для строгого сравнения (допускающего только два ключа, не больше, не меньше) в Python 2 сравните представление словаря с набором:
if d.viewkeys() != {'amount', 'name'}:
raise ValueError
(Так в Python 3 это было бы, if d.keys() != {'amount', 'name'}
).
Ответ 2
if all(k not in d for k in ('name', 'amount')):
raise ValueError
или
if all(k in d for k in ('name', 'amount')):
# do stuff
Ответ 3
Вы также можете использовать set как:
>>> d = {
'name': 'name',
'date': 'date',
'amount': 'amount',
}
>>> test = set(['name','date'])
>>> test.issubset(set(d.keys()))
True
Ответ 4
Мне нравится эта форма:
>>> d = {
... 'name': 'name',
... 'date': 'date',
... 'amount': 'amount'
... }
>>> tests={'name','date'}
>>> if any(test not in d for test in tests):
... raise ValueError
>>> # no error...
>>> del d['name']
>>> if any(test not in d for test in tests):
... raise ValueError
...
Traceback (most recent call last):
File "<stdin>", line 2, in <module>
ValueError
Работает на Py 2 или Py 3
Ответ 5
Для максимальной эффективности вы хотите избежать создания ненужного временного set
(который требуется для всех не бинарных операторов сравнения). Вы можете сделать это для:
if name not in d and amount not in d:
с:
if d.keys().isdisjoint(("amount", "name")):
но это, вероятно, неправильная логика (в обоих случаях), поскольку она вводится в тело if
(и вызывает исключение), когда оба ключа отсутствуют, и вы, вероятно, захотите вызвать исключение, если отсутствует какой-либо из ключей.
Для более вероятной логики отклонения d
если он не содержит оба ключа, вы хотели бы это:
if name not in d or amount not in d:
что можно сделать с помощью операций над множествами следующим образом:
Во-первых, вы должны заранее определить (вне функции, чтобы избежать повторного построения набора):
required_keys = frozenset({"amount", "name"})
затем сделайте:
if not d.keys() >= required_keys:
Оба решения (isdisjoint
и >=
) избегают построения наборов временных тестов и должны работать максимально эффективно (короткое замыкание, как только обнаруживается, что отсутствует один ключ, и требуется только пара поисков O(1)
когда оба ключа присутствуют). Лично я бы придерживался только двух ключей, if name not in d or amount not in d:
но если количество ключей растет, конечно, используйте операции, подобные множеству.