Cortar un diccionario

Tengo un diccionario y me gustaría pasar una parte de él a una función, esa parte está dada por una lista (o tupla) de teclas. Al igual que:

# the dictionary d = {1:2, 3:4, 5:6, 7:8} # the subset of keys I'm interested in l = (1,5) 

Ahora, idealmente me gustaría poder hacer esto:

 >>> d[l] {1:2, 5:6} 

… pero eso no funciona, ya que buscará una clave llamada (1,5) . Y d[1,5] ni siquiera es válido para Python (aunque parece que sería útil).

Sé que puedo hacerlo:

 >>> dict([(key, value) for key,value in d.iteritems() if key in l]) {1: 2, 5: 6} 

o esto:

 >>> dict([(key, d[key]) for key in l]) 

que es más compacto … pero creo que debe haber una “mejor” forma de hacerlo. ¿Me falta una solución más elegante?

(Estoy usando Python 2.7)

Debería estar recorriendo la tupla y verificando si la clave está en el dictado y no al revés, si no verifica si la clave existe y no está en el dictado, obtendrá un error de clave:

 print({k:d[k] for k in l if k in d}) 

Algunos tiempos:

  {k:d[k] for k in set(d).intersection(l)} In [22]: %%timeit l = xrange(100000) {k:d[k] for k in l} ....: 100 loops, best of 3: 11.5 ms per loop In [23]: %%timeit l = xrange(100000) {k:d[k] for k in set(d).intersection(l)} ....: 10 loops, best of 3: 20.4 ms per loop In [24]: %%timeit l = xrange(100000) l = set(l) {key: d[key] for key in d.viewkeys() & l} ....: 10 loops, best of 3: 24.7 ms per In [25]: %%timeit l = xrange(100000) {k:d[k] for k in l if k in d} ....: 100 loops, best of 3: 17.9 ms per loop 

No veo cómo {k:d[k] for k in l} no es legible o elegante y si todos los elementos están en d, entonces es bastante eficiente.

Use un conjunto para cruzar en la vista de diccionario dict.viewkeys() :

 l = {1, 5} {key: d[key] for key in d.viewkeys() & l} 

Esta es la syntax de Python 2, en Python 3 use d.keys() .

Esto todavía utiliza un bucle, pero al menos la comprensión del diccionario es mucho más legible. Usar conjuntos de intersecciones es muy eficiente, incluso si d o l es grande.

Manifestación:

 >>> d = {1:2, 3:4, 5:6, 7:8} >>> l = {1, 5} >>> {key: d[key] for key in d.viewkeys() & l} {1: 2, 5: 6} 

En Python 3 puedes usar la barra de islice para cortar el iterador dict.items()

 import itertools d = {1: 2, 3: 4, 5: 6} dict(itertools.islice(d.items(), 2)) {1: 2, 3: 4} 

Nota: esta solución no tiene en cuenta claves específicas. Se corta por orden interno de d , que en Python 3.7+ está garantizado para ser ordenado por inserción.

Escriba una subclase dict que acepte una lista de claves como un “elemento” y devuelva una “porción” del diccionario:

 class SliceableDict(dict): default = None def __getitem__(self, key): if isinstance(key, list): # use one return statement below # uses default value if a key does not exist return {k: self.get(k, self.default) for k in key} # raises KeyError if a key does not exist return {k: self[k] for k in key} # omits key if it does not exist return {k: self[k] for k in key if k in self} return dict.get(self, key) 

Uso:

 d = SliceableDict({1:2, 3:4, 5:6, 7:8}) d[[1, 5]] # {1: 2, 5: 6} 

O si desea usar un método separado para este tipo de acceso, puede usar * para aceptar cualquier número de argumentos:

 class SliceableDict(dict): def slice(self, *keys): return {k: self[k] for k in keys} # or one of the others from the first example d = SliceableDict({1:2, 3:4, 5:6, 7:8}) d.slice(1, 5) # {1: 2, 5: 6} keys = 1, 5 d.slice(*keys) # same 

set intersection y la dict comprehension se pueden utilizar aquí

 # the dictionary d = {1:2, 3:4, 5:6, 7:8} # the subset of keys I'm interested in l = (1,5) >>>{key:d[key] for key in set(l) & set(d)} {1: 2, 5: 6}