python dictionary namedtuple

python - ¿Una forma pitónica de convertir el diccionario en un objeto con nombre, u otro tipo de dictado hashable?



dictionary namedtuple (4)

Tengo un diccionario como:

d = {''a'': 1, ''b'': 2, ''c'': 3, ''d'': 4}

que me gustaría convertir a un nombre de tupla. Mi enfoque actual es con el siguiente código

namedTupleConstructor = namedtuple(''myNamedTuple'', '' ''.join(sorted(d.keys()))) nt= namedTupleConstructor(**d)

que produce

myNamedTuple (a = 1, b = 2, c = 3, d = 4)

Esto funciona bien para mí (creo), pero me falta una incorporada como ...

nt = namedtuple.from_dict() ?

ACTUALIZACIÓN: como se comentó en los comentarios, mi razón para querer convertir mi diccionario en una tupla con nombre es para que sea hashable, pero en general se puede usar como un dict.


Aunque me gusta la respuesta de @fuggy_yama, antes de leerlo tengo mi propia función, así que la dejo aquí solo para mostrar un enfoque diferente. También maneja las namedtuples anidadas

def dict2namedtuple(thedict, name): thenametuple = namedtuple(name, []) for key, val in thedict.items(): if not isinstance(key, str): msg = ''dict keys must be strings not {}'' raise ValueError(msg.format(key.__class__)) if not isinstance(val, dict): setattr(thenametuple, key, val) else: newname = dict2namedtuple(val, key) setattr(thenametuple, key, newname) return thenametuple


Mira esto:

def fill_tuple(NamedTupleType, container): if container is None: args = [None] * len(NamedTupleType._fields) return NamedTupleType(*args) if isinstance(container, (list, tuple)): return NamedTupleType(*container) elif isinstance(container, dict): return NamedTupleType(**container) else: raise TypeError("Cannot create ''{}'' tuple out of {} ({}).".format(NamedTupleType.__name__, type(container).__name__, container))

Las excepciones para los nombres incorrectos o el conteo de argumentos no válidos se manejan por __init__ de namedtuple .

Prueba con py.test:

def test_fill_tuple(): A = namedtuple("A", "aa, bb, cc") assert fill_tuple(A, None) == A(aa=None, bb=None, cc=None) assert fill_tuple(A, [None, None, None]) == A(aa=None, bb=None, cc=None) assert fill_tuple(A, [1, 2, 3]) == A(aa=1, bb=2, cc=3) assert fill_tuple(A, dict(aa=1, bb=2, cc=3)) == A(aa=1, bb=2, cc=3) with pytest.raises(TypeError) as e: fill_tuple(A, 2) assert e.value.message == "Cannot create ''A'' tuple out of int (2)."


Para crear la subclase, puede simplemente pasar las claves de un dict directamente:

MyTuple = namedtuple(''MyTuple'', sorted(d))

Ahora para crear instancias de este dict, u otros dictados con claves coincidentes:

my_tuple = MyTuple(**d)

Cuidado: namedtuples solo compara valores (ordenados). Están diseñados para ser un reemplazo directo de las tuplas normales, con el acceso al atributo nombrado como una característica adicional. Los nombres de los campos no serán considerados al hacer comparaciones de igualdad . Esto difiere de las comparaciones de igualdad de dict , que toman en cuenta las claves, ¡y puede que no sea lo que usted desea ni se espera del tipo de namedtuple !

Si solo tiene un dict, en lugar de un grupo de dicts que comparten el mismo conjunto de claves, entonces no tiene sentido crear este primer grupo con nombre en primer lugar. Solo debes usar un objeto de espacio de nombres en su lugar:

>>> from types import SimpleNamespace >>> SimpleNamespace(**d) namespace(a=1, b=2, c=3, d=4)

Para obtener una receta como "atributo de hashable", echa un vistazo a una box congelada:

>>> from box import Box >>> b = Box(d, frozen_box=True) >>> hash(b) 7686694140185755210 >>> b.a 1 >>> b[''a''] 1


Puede usar esta función para manejar diccionarios anidados:

def create_namedtuple_from_dict(obj): if isinstance(obj, dict): fields = sorted(obj.keys()) namedtuple_type = namedtuple( typename=''GenericObject'', field_names=fields, rename=True, ) field_value_pairs = OrderedDict( (str(field), create_namedtuple_from_dict(obj[field])) for field in fields ) try: return namedtuple_type(**field_value_pairs) except TypeError: # Cannot create namedtuple instance so fallback to dict (invalid attribute names) return dict(**field_value_pairs) elif isinstance(obj, (list, set, tuple, frozenset)): return [create_namedtuple_from_dict(item) for item in obj] else: return obj