web-dev-qa-db-fra.com

Comment vérifier que plusieurs clés sont dans un dict en un seul passage?

Je veux faire quelque chose comme:

foo = {'foo':1,'Zip':2,'zam':3,'bar':4}

if ("foo","bar") in foo:
    #do stuff

Comment vérifier si "foo" et "bar" sont tous les deux dans dict foo?

181
user131465

Eh bien, vous pouvez faire ceci:

>>> if all (k in foo for k in ("foo","bar")):
...     print "They're there!"
...
They're there!
307
hughdbrown
if set(("foo", "bar")) <= set(myDict): ...
108
Alex Martelli

Banc d'essai simple pour 3 des alternatives.

Mettez vos propres valeurs pour D et Q


>>> from timeit import Timer
>>> setup='''from random import randint as R;d=dict((str(R(0,1000000)),R(0,1000000)) for i in range(D));q=dict((str(R(0,1000000)),R(0,1000000)) for i in range(Q));print("looking for %s items in %s"%(len(q),len(d)))'''

>>> Timer('set(q) <= set(d)','D=1000000;Q=100;'+setup).timeit(1)
looking for 100 items in 632499
0.28672504425048828

#This one only works for Python3
>>> Timer('set(q) <= d.keys()','D=1000000;Q=100;'+setup).timeit(1)
looking for 100 items in 632084
2.5987625122070312e-05

>>> Timer('all(k in d for k in q)','D=1000000;Q=100;'+setup).timeit(1)
looking for 100 items in 632219
1.1920928955078125e-05
35
John La Rooy

Vous n'avez pas à envelopper le côté gauche dans un ensemble. Vous pouvez simplement faire ceci:

if {'foo', 'bar'} <= set(some_dict):
    pass

Cela fonctionne également mieux que la solution all(k in d...).

27
claytonk

Utiliser sets:

if set(("foo", "bar")).issubset(foo):
    #do stuff

Alternativement:

if set(("foo", "bar")) <= set(foo):
    #do stuff
22
Karl Voigtland

Que dis-tu de ça:

if all(key in foo for key in ["foo","bar"]):
    # do stuff
    pass
8
Greg

Bien que j'aime la réponse d'Alex Martelli, elle ne me semble pas pythonique. C’est-à-dire que j’ai pensé qu’un aspect important d’être Pythonic est d’être facilement compréhensible. Avec cet objectif, <= N'est pas facile à comprendre.

Bien que ce soit plus de caractères, utiliser issubset() comme suggéré par la réponse de Karl Voigtland est plus compréhensible. Puisque cette méthode peut utiliser un dictionnaire comme argument, une solution courte et compréhensible est la suivante:

foo = {'foo': 1, 'Zip': 2, 'zam': 3, 'bar': 4}

if set(('foo', 'bar')).issubset(foo):
    #do stuff

J'aimerais utiliser {'foo', 'bar'} À la place de set(('foo', 'bar')), parce que c'est plus court. Cependant, ce n'est pas très compréhensible et je pense que les accolades sont trop facilement confondues avec le sens d'un dictionnaire.

7
L S

La solution d'Alex Martelli set(queries) <= set(my_dict) est le code le plus court mais peut ne pas être le plus rapide. Supposons que Q = len (requêtes) et D = len (my_dict).

Cela prend O(Q) + O(D) pour faire les deux ensembles, puis (on espère!) Seulement seulement O (min (Q, D )) faire le test de sous-ensemble - en supposant bien sûr que Python est O(1) - c'est le cas le plus défavorable (lorsque la réponse est vraie).

La solution génératrice de hughdbrown (et al?) all(k in my_dict for k in queries) est le cas le plus défavorable O (Q).

Facteurs compliquant:
(1) Les boucles du gadget basé sur un ensemble sont toutes exécutées à la vitesse du C, alors que le gadget quelconque est en boucle sur bytecode.
(2) L’appelant du gadget quelconque peut être en mesure d’utiliser toute connaissance de probabilité d’échec pour ordonner les éléments de la requête en conséquence, alors que le gadget basé sur un ensemble ne permet pas un tel contrôle.

Comme toujours, si la vitesse est importante, l’étalonnage en conditions opérationnelles est une bonne idée.

4
John Machin

Je pense que c'est le plus intelligent et pithonique.

{'key1','key2'} <= my_dict.keys()
3
Shota Tamura

Que diriez-vous d'utiliser lambda?

 if reduce( (lambda x, y: x and foo.has_key(y) ), [ True, "foo", "bar"] ): # do stuff
1
rein

Si vous voulez:

  • également obtenir les valeurs pour les clés
  • vérifier plus d'un dictonary

ensuite:

from operator import itemgetter
foo = {'foo':1,'Zip':2,'zam':3,'bar':4}
keys = ("foo","bar") 
getter = itemgetter(*keys) # returns all values
try:
    values = getter(foo)
except KeyError:
    # not both keys exist
    pass
1
Jochen Ritzel

Cela ne veut pas dire que ce n'est pas quelque chose auquel vous n'avez pas pensé, mais je trouve que la chose la plus simple est généralement la meilleure:

if ("foo" in foo) and ("bar" in foo):
    # do stuff
1
Jason Baker
>>> if 'foo' in foo and 'bar' in foo:
...     print 'yes'
... 
yes

Jason, () n'est pas nécessaire en Python.

1
Juanjo Conti

Vous pouvez aussi utiliser . Issubset ()

>>> {"key1", "key2"}.issubset({"key1":1, "key2":2, "key3": 3})
True
>>> {"key4", "key2"}.issubset({"key1":1, "key2":2, "key3": 3})
False
>>>
0
Sinan Çetinkaya

À mon sens, il existe deux méthodes faciles à comprendre parmi toutes les options proposées. Mon critère principal est donc d'avoir un code très lisible, pas un code exceptionnellement rapide. Pour que le code soit compréhensible, je préfère donner les possibilités suivantes:

  • var <= var2.keys ()
  • var.issubset (var2)

Le fait que "var <= var2.keys ()" s’exécute plus rapidement dans mes tests ci-dessous, je préfère celui-ci.

import timeit

timeit.timeit('var <= var2.keys()', setup='var={"managed_ip", "hostname", "fqdn"}; var2= {"zone": "test-domain1.var23.com", "hostname": "bakje", "api_client_ip": "127.0.0.1", "request_data": "", "request_method": "GET", "request_url": "hvar2p://127.0.0.1:5000/test-domain1.var23.com/bakje", "utc_datetime": "04-Apr-2019 07:01:10", "fqdn": "bakje.test-domain1.var23.com"}; var={"managed_ip", "hostname", "fqdn"}')
0.1745898080000643

timeit.timeit('var.issubset(var2)', setup='var={"managed_ip", "hostname", "fqdn"}; var2= {"zone": "test-domain1.var23.com", "hostname": "bakje", "api_client_ip": "127.0.0.1", "request_data": "", "request_method": "GET", "request_url": "hvar2p://127.0.0.1:5000/test-domain1.var23.com/bakje", "utc_datetime": "04-Apr-2019 07:01:10", "fqdn": "bakje.test-domain1.var23.com"}; var={"managed_ip", "hostname", "fqdn"};')
0.2644960229999924
0
PietjePuk