Jak sprawdzić, czy zmienna jest słownikiem w Pythonie?


214

Jak sprawdziłbyś, czy zmienna jest słownikiem w Pythonie?

Na przykład chciałbym, aby przeglądał wartości w słowniku, dopóki nie znajdzie słownika. Następnie przejdź przez ten, który znajdzie:

dict = {'abc': 'abc', 'def': {'ghi': 'ghi', 'jkl': 'jkl'}}
for k, v in dict.iteritems():
    if ###check if v is a dictionary:
        for k, v in v.iteritems():
            print(k, ' ', v)
    else:
        print(k, ' ', v)

Również stackoverflow.com/questions/378927/… (który jest oznaczony jako duplikat powyższego).
NPE,


40
Nie, to nie to samo pytanie. Odpowiedź na to pytanie i na inne pytania tutaj wymienione zawierają zasadniczo te same informacje. Ale odpowiedź na „Jak sprawdzić, czy zmienna jest słownikiem w pythonie” brzmi „Użyj type () lub isinstance ()”, co następnie prowadzi do nowego pytania, jaka jest różnica między typem () a isinstance () . Ale osoba zadająca pierwsze pytanie nie może wiedzieć, dopóki nie odpowie na pierwsze pytanie. Ergo, różne pytania, które mają znaczenie, gdy szukasz pytania na stronie.

13
Zgadzam się z @mbakeranalecta Przybyłem tutaj, szukając odpowiedzi na pytanie „Jak sprawdzić, czy zmienna jest słownikiem w Pythonie?” i nigdy nie pomyślałbym, aby spojrzeć na moją odpowiedź w „Różnice między isinstance () a type () w python”.
lodebari

5
Aby sprawdzić, czy zmienna jest w szczególności słownikiem, prawdopodobnie powinieneś użyć isinstance(v, collections.abc.Mapping). Innymi słowy, nie jest to dokładna kopia „Różnic między isinstance () a type ()”.
Josh Kelley

Odpowiedzi:


279

Możesz użyć if type(ele) is dictlub użyć, isinstance(ele, dict)który działałby, gdybyś podklasował dict:

d = {'abc':'abc','def':{'ghi':'ghi','jkl':'jkl'}}
for ele in d.values():
    if isinstance(ele,dict):
       for k, v in ele.items():
           print(k,' ',v)

70
I dół głosowali tę odpowiedź, ponieważ prawidłowa odpowiedź na ogólne pytanie brzmi: isinstance(ele, collections.Mapping). To działa na dict(), collections.OrderedDict(), i collections.UserDict(). Przykład w pytaniu jest wystarczająco konkretny, aby odpowiedź Padriaca zadziałała, ale nie jest wystarczająca w przypadku ogólnym.
Alexander Ryzhov

3
Odebrałem tę odpowiedź, ponieważ jeśli zamierzasz wywołać, ele.items()dlaczego sprawdzasz typ? EAFP / kaczego wpisując działa, po prostu zawinąć for k,v in ele.items()w try...except (AttributeError, TypeError). Jeśli powstanie wyjątek, wiesz ele, itemsże nie ma takiego, który daje
iterowalność

@Padraic Cunningham Twój hipotetyczny przypadek krawędzi wymagałby, aby twoja klasa niestandardowa „100% not dict” 1. posiadała items()metodę 2. która właśnie tak się wydarzyła, umożliwiając iterację i 3. gdzie każdy element tej iteracji może być reprezentowany jako 2 -tuple. W tym momencie Twój obiekt niestandardowy już zaimplementował już połowę nagrania. Dla celów wymienionego kodu dbaliśmy tylko o warunkowe rozwinięcie zagnieżdżonego elementu, a Twój obiekt niestandardowy już to implementuje. (Trochę ironiczne jest to, że krytykujesz komentarz Aleksandra Ryżowa za to, że jest zbyt ogólny, ale teraz
poruszasz

1
@PadraicCunningham Wolałbym od początku nie uczyć ludzi, którzy nie są zbytnio obeznani z Pythonem. W Pythonie jest bardzo niewiele przypadków użycia, które wymagają jawnego sprawdzania typu - większość wynika z dziedziczenia złej implementacji na początku („obiekt boski, przesłanianie standardowych konstrukcji biblioteki / języka itp.) Pierwotne pytanie samo w sobie jest problemem XY. Dlaczego PO musi sprawdzić typ? Ponieważ zgodnie z ich kodem tak naprawdę chcą zrobić, aby sprawdzić, czy element w ich kolekcji zachowuje się jak kolekcja (implementacje, items()które dają zagnieżdżoną iterowalność).
cowbert

4
@AlexanderRyzhov Dlaczego nie opublikować takiego podejścia jako odpowiedzi? BTW, jak Josh Kelley skomentował powyżej sugerując collections.abc.Mapping, uwaga może być odpowiednia, że ​​są takie same, ale collections.abcnie jest dostępna przed Pythonem 3.3. collections.Mappingalias jest nadal dostępny w Pythonie 3.6, ale nie jest udokumentowany, więc prawdopodobnie należy go preferować collections.abc.Maping.
Yushin Washio

5

Jak sprawdziłbyś, czy zmienna jest słownikiem w Pythonie?

To jest doskonałe pytanie, ale jest to niefortunne, że większość upvoted prowadzi odpowiedź z ubogiej rekomendacji type(obj) is dict.

(Należy pamiętać, że nie należy również używać dictjako nazwy zmiennej - jest to nazwa wbudowanego obiektu).

Jeśli piszesz kod, który zostanie zaimportowany i użyty przez innych, nie zakładaj, że będą oni korzystać bezpośrednio z wbudowanego dict - dzięki temu domniemaniu twój kod jest bardziej nieelastyczny iw tym przypadku twórz łatwo ukryte błędy, które nie spowodowałyby błędu programu .

Zdecydowanie sugeruję, dla celów poprawności, łatwości konserwacji i elastyczności dla przyszłych użytkowników, aby nigdy nie mieli mniej elastycznych, jednoznacznych wyrażeń w kodzie, gdy są bardziej elastyczne, idiomatyczne wyrażenia.

isjest testem tożsamości obiektu . Nie obsługuje dziedziczenia, nie obsługuje żadnej abstrakcji i nie obsługuje interfejsu.

Podam więc kilka opcji, które to robią.

Wspieranie dziedziczenia:

Jest to pierwsza rekomendacja chciałbym zrobić, ponieważ pozwala użytkownikom dostarczać własne podklasy dict, albo ich OrderedDict, defaultdictalbo Counterz modułu zbiory:

if isinstance(any_object, dict):

Ale są jeszcze bardziej elastyczne opcje.

Obsługiwane abstrakcje:

from collections.abc import Mapping

if isinstance(any_object, Mapping):

Dzięki temu użytkownik Twojego kodu może użyć własnej niestandardowej implementacji abstrakcyjnego odwzorowania, która obejmuje również dowolną podklasę dicti nadal uzyskać prawidłowe zachowanie.

Użyj interfejsu

Często słyszysz poradę OOP „program do interfejsu”.

Ta strategia wykorzystuje polimorfizm lub pisanie kaczek w Pythonie.

Więc po prostu spróbuj uzyskać dostęp do interfejsu, wychwytując określone oczekiwane błędy ( AttributeErrorw przypadku, gdy nie ma, .itemsa TypeErrorjeśli itemsnie można wywołać) z rozsądną rezerwą - a teraz każda klasa, która implementuje ten interfejs, da ci swoje elementy (uwaga .iteritems()zniknęła w Pythonie 3):

try:
    items = any_object.items()
except (AttributeError, TypeError):
    non_items_behavior(any_object)
else: # no exception raised
    for item in items: ...

Być może pomyślisz, że takie pisanie kaczy posuwa się zbyt daleko, by pozwolić na zbyt wiele fałszywych trafień, i może być tak, w zależności od celów tego kodu.

Wniosek

Nie używaj isdo sprawdzania typów dla standardowego przepływu sterowania. Użyj isinstance, rozważ abstrakcje takie jak Mappinglub MutableMappingi całkowicie unikaj sprawdzania typu, używając bezpośrednio interfejsu.


3

OP nie wykluczył zmiennej początkowej, więc dla kompletności tutaj jest, jak poradzić sobie z ogólnym przypadkiem przetwarzania domniemanego słownika, który może zawierać elementy jako słowniki.

Również postępując zgodnie z zalecanym przez Pythona (3.8) sposobem testowania słownika w powyższych komentarzach.

from collections.abc import Mapping

dict = {'abc': 'abc', 'def': {'ghi': 'ghi', 'jkl': 'jkl'}}

def parse_dict(in_dict): 
    if isinstance(in_dict, Mapping):
        for k, v in in_dict.items():
            if isinstance(v, Mapping):
                for k, v in v.items():
                    print(k, v)
            else:
                print(k, v)

parse_dict(dict)

dict.iteritems()nie istnieje w Pythonie 3, powinieneś użyć dict.items()zamiast tego.
Arkelis

@Arkelis Ups, właśnie skopiowałem / wkleiłem tę część, dziękuję za zwrócenie jej uwagi - teraz poprawione.
CodeMantle
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.