python: ¿cuál es la mejor manera de verificar que existen varias claves en un diccionario?

mi dict parece

d = { 'name': 'name', 'date': 'date', 'amount': 'amount', ... } 

Quiero probar si el name y la amount existen, entonces haré

 if not `name` in d and not `amount` in d: raise ValueError # for example 

Supongamos que obtengo los datos de una api y quiero probar si existen 10 campos en el diccionario o no.

¿Sigue siendo la mejor manera de buscar?

Puedes usar set intersecciones:

 if not d.viewkeys() & {'amount', 'name'}: raise ValueError 

En Python 3, eso sería:

 if not d.keys() & {'amount', 'name'}: raise ValueError 

Porque .keys() devuelve una vista de dict de forma predeterminada. Los objetos de vista de diccionario como los devueltos por .viewkeys() (y .keys() en Python 3) actúan como conjuntos y las pruebas de intersección son muy eficientes.

Demo en 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 

Tenga en cuenta que esto prueba True solo si faltan ambas claves. Si necesita que su examen pase si falta alguno , use:

 if not d.viewkeys() >= {'amount', 'name'}: raise ValueError 

lo cual es falso solo si ambas claves están presentes:

 >>> d = { ... 'name': 'name', ... 'date': 'date', ... 'amount': 'amount', ... } >>> not d.viewkeys() >= {'amount', 'name'} False >>> del d['amount'] >>> not d.viewkeys() >= {'amount', 'name'}) True 

Para una comparación estricta (permitiendo solo las dos claves, ni más, ni menos), en Python 2, compare la vista del diccionario con un conjunto:

 if d.viewkeys() != {'amount', 'name'}: raise ValueError 

(Entonces, en Python 3 sería if d.keys() != {'amount', 'name'} ).

 if all(k not in d for k in ('name', 'amount')): raise ValueError 

o

 if all(k in d for k in ('name', 'amount')): # do stuff 

También podrías usar set como:

 >>> d = { 'name': 'name', 'date': 'date', 'amount': 'amount', } >>> test = set(['name','date']) >>> test.issubset(set(d.keys())) True 

Me gusta esta forma:

 >>> 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 "", line 2, in  ValueError 

Funciona en Py 2 o Py 3

Para lograr la máxima eficiencia, debe evitar la construcción de set temporales innecesarios (que requieren todos los operadores binarios que no son de comparación). Puedes hacer esto por:

 if name not in d and amount not in d: 

con:

 if d.keys().isdisjoint(("amount", "name")): 

pero es probable que sea la lógica incorrecta (en ambos casos), ya que solo ingresa el cuerpo if (y genera la excepción) cuando faltan ambas claves, y es probable que desee elevar la excepción si falta alguna de las claves.

Para la lógica más probable de rechazar d menos que contenga ambas claves, querría esto:

 if name not in d or amount not in d: 

lo que se puede hacer con operaciones de conjunto de esta manera:

Primero, predefinir (fuera de la función para evitar la construcción repetida de un conjunto):

 required_keys = frozenset({"amount", "name"}) 

entonces hazlo:

 if not d.keys() >= required_keys: 

Ambas soluciones ( isdisjoint y >= ) evitan la construcción de conjuntos temporales por prueba, y deben funcionar lo más eficientemente posible (cortocircuitar tan pronto como se encuentre que falta una sola clave, y solo se necesitan un par de búsquedas O(1) cuando ambas claves están presentes). Personalmente, por solo dos claves, me quedo con if name not in d or amount not in d: pero si el número de teclas aumenta, claro, utilice operaciones de tipo set.