web-dev-qa-db-de.com

Pythonic-Methode zum Konvertieren des Wörterbuchs in namedtuple oder ein anderes Hash-fähiges Diktiergerät?

Ich habe ein Wörterbuch wie:

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

welches ich in ein namedtuple umwandeln möchte ..__ Mein aktueller Ansatz ist mit dem folgenden Code

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

was produziert

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

Das funktioniert gut für mich (denke ich), aber ich vermisse ein eingebautes wie ... 

nt = namedtuple.from_dict() ?

UPDATE: Wie in den Kommentaren besprochen, ist mein Grund, warum ich mein Wörterbuch in ein namedtuple konvertieren möchte, so, dass es hashable wird, aber im Allgemeinen wie ein Diktat verwendet werden kann.

25
Max Power

Um die Unterklasse zu erstellen, können Sie die Schlüssel eines Diktats direkt übergeben:

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

Erstellen Sie nun Instanzen aus diesem oder anderen Diktaten mit übereinstimmenden Schlüsseln:

my_Tuple = MyTuple(**d)

Achtung: namedtuples vergleichen auf nur Werte (geordnet). Sie sind als Drop-In-Ersatz für reguläre Tupel konzipiert, wobei der Zugriff auf benannte Attribute als zusätzliches Feature gilt. Die Feldnamen werden beim Vergleich von Gleichheit nicht berücksichtigt. Dies unterscheidet sich von dict-Gleichheitsvergleichen, bei denen die Schlüssel berücksichtigt werden, und es ist möglicherweise nicht das, was Sie vom namedtuple-Typ erwartet oder erwartet haben!

Wenn Sie nur ein Diktat haben und nicht mehrere Diktate, die denselben Schlüsselsatz verwenden, ist es nicht sinnvoll, dieses Namedtuple zu erstellen. Sie sollten stattdessen nur ein Namespace-Objekt verwenden:

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

Für ein hashbares "attrdict" wie ein Rezept können Sie eine gefrorene box :

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

Mit dieser Funktion können Sie verschachtelte Wörterbücher behandeln:

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
1
fuggy_yama

Überprüfen Sie dies heraus:

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))

Ausnahmen für falsche Namen oder ungültige Argumentanzahl werden von __init__ von namedtuple behandelt.

Testen Sie mit 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)."
0
from collections import namedtuple
nt = namedtuple('x', d.keys())(*d.values())
0
Mitendra
def toNametuple(dict_data):
    return namedtuple(
        "X", dict_data.keys()
    )(*Tuple(map(lambda x: x if not isinstance(x, dict) else toNametuple(x), dict_data.values())))

d = {
    'id': 1,
    'name': {'firstName': 'Ritesh', 'lastName':'Dubey'},
    'list_data': [1, 2],
}

obj = toNametuple(d)

Zugriff als obj.name.firstName, obj.id

Dies funktioniert für verschachtelte Wörterbücher mit beliebigen Datentypen.

0
Ritesh Dubey

Obwohl ich die Antwort von @fuggy_yama mag, habe ich vor dem Lesen meine eigene Funktion erhalten, also lasse ich sie hier, um einen anderen Ansatz zu zeigen. Es behandelt auch verschachtelte namedtuples

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
0