Python >> Tutoriel Python >  >> Python

Manière pythonique de convertir un dictionnaire en namedtuple ou en un autre type de dict hachable?

Pour créer la sous-classe, vous pouvez simplement passer directement les clés d'un dict :

MyTuple = namedtuple('MyTuple', d)

Maintenant, pour créer des instances de tuple à partir de ce dict, ou de tout autre dict avec des clés correspondantes :

my_tuple = MyTuple(**d)

Attention : les tuples nommés comparent sur les valeurs uniquement (commandé). Ils sont conçus pour remplacer les tuples réguliers, avec un accès aux attributs nommés en tant que fonctionnalité supplémentaire. Les noms de champ ne seront pas pris en compte lors des comparaisons d'égalité . Ce n'est peut-être pas ce que vous vouliez ni ce que vous attendiez du namedtuple taper! Cela diffère de dict comparaisons d'égalité, qui prennent en compte les clés et comparent également l'ordre agnostique.

Pour les lecteurs qui n'ont pas vraiment besoin d'un type qui est une sous-classe de tuple , il n'y a probablement pas grand intérêt à utiliser un tuple nommé en premier lieu. Si vous souhaitez simplement utiliser la syntaxe d'accès aux attributs sur les champs, il serait plus simple et plus facile de créer des objets d'espace de noms à la place :

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

ma raison de vouloir convertir mon dictionnaire en un namedtuple est qu'il devienne hachable, mais toujours généralement utilisable comme un dict

Pour une recette de type "attrdict" hachable, consultez une boîte congelée :

>>> from box import Box
>>> b = Box(d, frozen_box=True)
>>> hash(b)
7686694140185755210
>>> b.a
1
>>> b["a"]
1
>>> b["a"] = 2
BoxError: Box is frozen

Il peut également y avoir un type de mappage gelé dans une version ultérieure de Python, regardez ce brouillon de PEP pour acceptation ou rejet :

PEP 603 -- Ajout d'un type frozenmap aux collections


from collections import namedtuple
nt = namedtuple('x', d.keys())(*d.values())

Vous pouvez utiliser cette fonction pour gérer les dictionnaires imbriqués :

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

Post précédent