2017-05-11 9 views
13

Mam słownika takiego:Pythonic Way to Convert Dictionary to namedtuple lub Another Hashable dict-like?

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

które chciałbym przekonwertować na namedtuple. My Obecnie stosowane podejście z następującego kodu

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

który wytwarza

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

działa w porządku dla mnie (chyba), ale jestem brakuje wbudowany w taki ...

nt = namedtuple.from_dict() ? 

UPDATE: jak di przeklęty w komentarzach, mój powód, dla którego chciałbym przekonwertować mój słownik na nazwanątakę, jest taki, że staje się nieosiągalna, ale wciąż ogólnie użyteczna jak dyktafon.

+0

Zgodnie z dokumentacją nie jest krótsza droga. Można jednak rozszerzyć klasę i zaimplementować tę metodę. – Blas

+0

Z namedtuples, powinieneś utworzyć typ namedtuple raz i używać go wielokrotnie, nie generując nowego typu namedtuple za każdym razem. Generowanie nowego, pokrewnego typu za każdym razem jest powolne i pokonuje wszelkie korzyści związane z przestrzenią. – user2357112

+0

@ użytkownik2357112 prawdopodobnie użytkownik ma wiele dykt przy użyciu tych samych kluczy. – wim

Odpowiedz

9

Aby utworzyć podklasę, mogą po prostu przekazać klucze dict bezpośrednio:

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

Teraz do utworzenia instancji z tego dict, lub innych dicts z pasującymi kluczami:

my_tuple = MyTuple(**d) 

Uwaga: kostki o nazwach porównawczych na wartości tylko wartości (zamówione). Są one zaprojektowane jako zamiennik zwykłych krotek, a nazwany dostęp do atrybutów jako dodatkowa funkcja. Nazwy pól nie będą brane pod uwagę podczas porównywania równości. Różni się to od porównań równości dict, które uwzględniają klucze i może nie być to, czego oczekujesz od typu namedtuple!

Jeśli dysponujesz tylko jednym dyktatem, a nie mnóstwem dyktatur, które dzielą ten sam zestaw kluczy, nie ma sensu tworzyć tej nazwanej litery w pierwszej kolejności. Należy po prostu użyć obiektu namespace Zamiast:

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

Dla hashable "attrdict" jak receptury, sprawdź zamarzniętego box:

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

Do jednolinijkowego potrzebujesz: MyNamedTuple = namedtuple ("MyNamedTuple", d.keys()) (** d) – FLab

+0

Interesujące jest to, że Namespace ma się nieczytelnie? To był mój pierwotny powód, dla którego chciałbym przekonwertować dicta na nazwanątakę –

+1

@MaxPower: Czy wiesz, że nazwane krotki, które konstruujesz z '{'a': 1}' i '{'b': 1}' będą równe i mają równe kody skrótu? Coś w rodzaju 'tuple (posortowane (d.items()))' lub 'frozenset (d.items())' może być bardziej odpowiednie. Będą również obsługiwać klucze, które nie są poprawnymi identyfikatorami Pythona, takie jak '' for'' lub '3'. – user2357112

0

to sprawdzić:

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

Wyjątki dla niepoprawnych nazw lub niepoprawnych argumentów jest obsługiwana przez __init__ z namedtuple.

test z 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)."