Подтвердить что ты не робот

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 для этих полей в словаре или нет.

Разве это лучший способ поиска?

4b9b3361

Ответ 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: но если количество ключей растет, конечно, используйте операции, подобные множеству.