valor resueltos recorrer lista elementos ejercicios diccionarios diccionario dentro convertir buscar agregar actualizar python-2.7 python-3.x dictionary methods except

python 2.7 - resueltos - Método seguro de Python para obtener valor del diccionario anidado



lista dentro de un diccionario python (10)

Al combinar todas estas respuestas aquí y los pequeños cambios que hice, creo que esta función sería útil. es seguro, rápido y fácil de mantener.

def deep_get(dictionary, keys, default=None): return reduce(lambda d, key: d.get(key, default) if isinstance(d, dict) else default, keys.split("."), dictionary)

Ejemplo:

>>> from functools import reduce >>> def deep_get(dictionary, keys, default=None): ... return reduce(lambda d, key: d.get(key, default) if isinstance(d, dict) else default, keys.split("."), dictionary) ... >>> person = {''person'':{''name'':{''first'':''John''}}} >>> print (deep_get(person, "person.name.first")) John >>> print (deep_get(person, "person.name.lastname")) None >>> print (deep_get(person, "person.name.lastname", default="No lastname")) No lastname >>>

Tengo un diccionario anidado. ¿Hay una sola forma de obtener valores de forma segura?

try: example_dict[''key1''][''key2''] except KeyError: pass

¿O quizás Python tiene un método como get() para el diccionario anidado?


Aprovechando la respuesta de Yoav, un enfoque aún más seguro:

def deep_get(dictionary, *keys): return reduce(lambda d, key: d.get(key, None) if isinstance(d, dict) else None, keys, dictionary)


Después de ver this para obtener atributos profundos, hice lo siguiente para obtener de manera segura los valores dict anidados mediante la notación de puntos. Esto funciona para mí porque mis dicts son objetos MongoDB deserializados, así que sé que los nombres de las teclas no contienen . s. Además, en mi contexto, puedo especificar un valor falso ( None ) que no tengo en mis datos, por lo que puedo evitar el patrón try / except cuando llamo a la función.

from functools import reduce # Python 3 def deepgetitem(obj, item, fallback=None): """Steps through an item chain to get the ultimate value. If ultimate value or path to value does not exist, does not raise an exception and instead returns `fallback`. >>> d = {''snl_final'': {''about'': {''_icsd'': {''icsd_id'': 1}}}} >>> deepgetitem(d, ''snl_final.about._icsd.icsd_id'') 1 >>> deepgetitem(d, ''snl_final.about._sandbox.sbx_id'') >>> """ def getitem(obj, name): try: return obj[name] except (KeyError, TypeError): return fallback return reduce(getitem, item.split(''.''), obj)


Podrías usar get dos veces:

example_dict.get(''key1'', {}).get(''key2'')

Esto devolverá None si key2 o key2 no existen.

Tenga en cuenta que esto aún podría generar un AttributeError si example_dict[''key1''] existe, pero no es un dict (o un objeto similar a dict con un método get ). El código try..except que publicaste generaría un TypeError en su lugar si example_dict[''key1''] es susceptible de suscripción.

Otra diferencia es que el try...except cortocircuitos inmediatamente después de la primera clave faltante. La cadena de llamadas no lo hace.

Si desea conservar la sintaxis, example_dict[''key1''][''key2''] pero no desea que se suba KeyErrors, entonces podría usar la receta Hasher :

class Hasher(dict): # https://.com/a/3405143/190597 def __missing__(self, key): value = self[key] = type(self)() return value example_dict = Hasher() print(example_dict[''key1'']) # {} print(example_dict[''key1''][''key2'']) # {} print(type(example_dict[''key1''][''key2''])) # <class ''__main__.Hasher''>

Tenga en cuenta que esto devuelve un Hasher vacío cuando falta una clave.

Como Hasher es una subclase de dict , puedes usar un Hasher de la misma manera que podrías usar un dict . Todos los mismos métodos y sintaxis están disponibles, Hashers simplemente trata las claves que faltan de manera diferente.

Puede convertir un dict regular en un Hasher como este:

hasher = Hasher(example_dict)

y convierte un Hasher a un dict regular con la misma facilidad:

regular_dict = dict(hasher)

Otra alternativa es ocultar la fealdad en una función auxiliar:

def safeget(dct, *keys): for key in keys: try: dct = dct[key] except KeyError: return None return dct

Entonces, el resto de tu código puede mantenerse relativamente legible:

safeget(example_dict, ''key1'', ''key2'')


Si bien el enfoque de reducción es ordenado y breve, creo que un simple bucle es más fácil de asimilar. También he incluido un parámetro predeterminado.

def deep_get(_dict, keys, default=None): for key in keys: if isinstance(_dict, dict): _dict = _dict.get(key, default) else: return default return _dict

Como un ejercicio para entender cómo funcionaba la reducción de un trazador de líneas, hice lo siguiente. Pero, en última instancia, el enfoque de bucle me parece más intuitivo.

def deep_get(_dict, keys, default=None): def _reducer(d, key): if isinstance(d, dict): return d.get(key, default) return default return reduce(_reducer, keys, _dict)

Uso

nested = {''a'': {''b'': {''c'': 42}}} print deep_get(nested, [''a'', ''b'']) print deep_get(nested, [''a'', ''b'', ''z'', ''z''], default=''missing'')


También podrías usar python reduce :

def deep_get(dictionary, *keys): return reduce(lambda d, key: d.get(key) if d else None, keys, dictionary)


Una adaptación de la respuesta de unutbu que encontré útil en mi propio código:

example_dict.setdefaut(''key1'', {}).get(''key2'')

Genera una entrada de diccionario para key1 si ya no tiene esa clave para evitar el KeyError. Si desea terminar un diccionario anidado que incluya ese emparejamiento de clave de todos modos como lo hice, esta parece ser la solución más fácil.


Una clase simple que puede envolver un dict, y recuperar basado en una clave:

class FindKey(dict): def get(self, path, default=None): keys = path.split(".") val = None for key in keys: if val: if isinstance(val, list): val = [v.get(key, default) if v else None for v in val] else: val = val.get(key, default) else: val = dict.get(self, key, default) if not val: break return val

Por ejemplo:

person = {''person'':{''name'':{''first'':''John''}}} FindDict(person).get(''person.name.first'') # == ''John''

Si la clave no existe, devuelve None por defecto. Puede anular eso utilizando una clave default= en el contenedor FindDict - por ejemplo`:

FindDict(person, default='''').get(''person.name.last'') # == doesn''t exist, so ''''


Una solución recursiva. No es el más eficiente, pero me parece un poco más legible que los otros ejemplos y no se basa en functools.

def deep_get(d, keys): if not keys or d is None: return d return deep_get(d.get(keys[0]), keys[1:])

Ejemplo

d = {''meta'': {''status'': ''OK'', ''status_code'': 200}} deep_get(d, [''meta'', ''status_code'']) # => 200 deep_get(d, [''garbage'', ''status_code'']) # => None

Una versión más pulida

def deep_get(d, keys, default=None): """ Example: d = {''meta'': {''status'': ''OK'', ''status_code'': 200}} deep_get(d, [''meta'', ''status_code'']) # => 200 deep_get(d, [''garbage'', ''status_code'']) # => None deep_get(d, [''meta'', ''garbage''], default=''-'') # => ''-'' """ assert type(keys) is list if d is None: return default if not keys: return d return deep_get(d.get(keys[0]), keys[1:], default)


para recuperar claves de segundo nivel, puede hacer esto:

key2_value = (example_dict.get(''key1'') or {}).get(''key2'')