Sprawdź, czy zmienna jest listą lub krotką

234

W Pythonie, jaki jest najlepszy sposób sprawdzenia, czy zmienna zawiera listę lub krotkę? (tj. kolekcja)

Czy jest isinstance()tak złe, jak tutaj sugerowano? http://www.canonical.org/~kragen/isinstance/

Aktualizacja: najczęstszym powodem, dla którego chcę odróżnić listę od łańcucha jest to, że mam nieskończenie głęboko zagnieżdżone drzewo / strukturę danych list list list łańcuchów itp., Które badam za pomocą algorytmu rekurencyjnego i potrzebuję wiedzieć, kiedy uderzę w węzły „liścia”.

interstar
źródło
72
Zasadniczo odrzucanie sprawdzania typu jako zła jest nieco łatwe. To część języka. Jeśli jest tak zły, ktoś powinien napisać PEP, aby go usunąć.
Adam Crossland
4
@Adam Crossland: „To część języka”. Podobnie jak dzielenie przez zero. Można tego uniknąć. W takim przypadku bez dodatkowych informacji jest to prawdopodobnie całkowicie niepotrzebne. Większość sprawdzania typów w Pythonie jest niepotrzebna. Ponieważ nie wszystko jest niepotrzebne, należy sprawdzić niektóre typy. Ale to nie znaczy, że jest to pomocne, cenne, a nawet dobry pomysł.
S.Lott,
11
Mówisz więc, że potrzebne jest sprawdzenie typu, ale mimo to jest bezużyteczne, bezwartościowe i zły pomysł. Przepraszam, to po prostu nie ma sensu.
Glenn Maynard
18
„XXX jest zły” to źle pomyślana, wprowadzająca w błąd nazwa „sposób, w jaki prosisz o wykonanie XXX, sugeruje, że nie rozumiesz, kiedy należy go użyć, i prawie na pewno chcesz czegoś innego”. Najprawdopodobniej tak jest w tym przypadku.
Glenn Maynard
2
Zasadniczo nie odrzuciłem tego jako zła. Napisałem krótki esej o tym, kiedy jest zły i kiedy jest uzasadniony. Ten esej może składać się z wielu rzeczy - słusznych, złych, jasnych, niejasnych, przyjemnych, nudnych - ale jedną rzeczą nie jest ogólne odrzucenie tej techniki.
Kragen Javier Sitaker

Odpowiedzi:

101

Śmiało i użyj, isinstancejeśli potrzebujesz. Jest to trochę złe, ponieważ wyklucza niestandardowe sekwencje, iteratory i inne rzeczy, których możesz potrzebować. Czasami jednak musisz zachowywać się inaczej, jeśli ktoś, na przykład, przejdzie przez łańcuch. Moją preferencją byłoby jawne sprawdzenie strlub unicodepolubienie:

import types
isinstance(var, types.StringTypes)

NB Nie myl się types.StringTypez types.StringTypes. Ten ostatni zawiera stri unicodeobiekty.

typesModuł jest uważany przez wielu za przestarzały na rzecz tylko sprawdzam bezpośrednio przed typu obiektu, więc jeśli nie chcesz korzystać z powyższym, można alternatywnie sprawdzić jawnie przeciw stri unicode, podobnie jak to:

isinstance(var, (str, unicode)):

Edytować:

Jeszcze lepiej jest:

isinstance(var, basestring)

Zakończ edycję

Po którymkolwiek z nich możesz wrócić do zachowywania się, jakbyś otrzymywał normalną sekwencję, pozwalając, by niesekwencje generowały odpowiednie wyjątki.

Zobacz, że „zło” w sprawdzaniu typów nie polega na tym, że możesz chcieć zachowywać się inaczej w odniesieniu do określonego typu obiektu, lecz na tym, że sztucznie ograniczasz swoją funkcję do wykonywania właściwych rzeczy z nieoczekiwanymi typami obiektów, które w przeciwnym razie mogłyby zrobić właściwą rzecz. Jeśli masz ostatnią awarię, która nie jest sprawdzona pod względem typu, usuwasz to ograniczenie. Należy zauważyć, że zbyt wiele sprawdzania typu to zapach kodu wskazujący, że możesz chcieć trochę refaktoryzować, ale to niekoniecznie oznacza, że ​​powinieneś unikać go z getgo.

jcdyer
źródło
2
Moduł typów jest trochę historycznym artefaktem. Jak wspomniano na docs.python.org/dev/library/types.html#module-types, jeśli naprawdę musisz sprawdzić strtyp, powinieneś użyć go bezpośrednio, zamiast używać, types.StringTypektóry jest tylko aliasem. Ale nie sądzę, że ta odpowiedź odpowiada na zadane pytanie, ponieważ chodziło o „kolekcję”. Chyba, że ​​używasz wystarczająco nowego Pythona, aby mieć abcmoduł, który nie jest czymś, czego można użyć isinstancedo sprawdzenia, a nawet wtedy polecam unikanie sprawdzania, jeśli to w ogóle możliwe.
mzz
1
assert isinstance(u'abc', str) == False. Zgadzam się, że lepiej jest sprawdzić przed typu bezpośrednio, a nie za pomocą typesmodułu, ale types.StringTypesrobi coś, co strnie: zwraca True stri unicodeobiekty. Zmienię swoją odpowiedź, aby zaoferować podwójne sprawdzenie jako alternatywę.
jcdyer
1
Zdaję sobie sprawę, że nie odpowiedziałem bezpośrednio na pytanie, czy sprawdzić kolekcje, ale faktycznie zadane pytanie brzmiało: „Czy isinstancezło?” Dałem też kontrprzykład, który (1) jest nie-złym zastosowaniem isinstance, ponieważ awaria oznacza, że ​​nie psuje się, a (2) jest dobrym rozwiązaniem dla bardzo powszechnej motywacji, którą ludzie chcą sprawdzić, czy coś jest a listlub tuple(tj. aby odróżnić je od ciągów).
jcdyer
Zgadzam się, z zastrzeżeniem, że często przydatne jest, aby niestandardowe typy zachowywały się również jak łańcuchy. Ale OO Pythona idzie tylko do tej pory ...
Kragen Javier Sitaker
Czy class Foo(str): passrobisz co chcesz?
jcdyer
567
if type(x) is list:
    print 'a list'
elif type(x) is tuple:
    print 'a tuple'
else:
    print 'neither a tuple or a list'
wall-e
źródło
1
Nie działa: typ ([]) ==> lista; type ([]) is list ===> False
sten
3
W Python 2.7.5: type([]) is listzwracaTrue
David Geiger
54
type(x) in [list,tuple]jest krótszy.
Alex Holcombe,
jeśli x i typ (x) to lista: aby uniknąć niedopasowania []
Kenichi Shibata
Tak bardzo musiałem przewinąć w dół. : D
Rithwik,
38

Nie ma nic złego w korzystaniu, isinstanceo ile nie jest to zbędne. Jeśli zmienna powinna być tylko listą / krotką, to udokumentuj interfejs i po prostu użyj go jako takiego. W przeciwnym razie kontrola jest całkowicie uzasadniona:

if isinstance(a, collections.Iterable):
    # use as a container
else:
    # not a container!

Ten rodzaj sprawdzania ma kilka dobrych przypadków użycia, na przykład ze standardowym ciągiem rozpoczynającym się / kończącym z metodami (chociaż dla dokładności są one implementowane w C w CPython za pomocą jawnego sprawdzenia, czy jest to krotka - istnieje więcej niż jeden sposób aby rozwiązać ten problem, jak wspomniano w artykule, do którego prowadzi link).

Wyraźne sprawdzenie jest często lepsze niż próba użycia obiektu jako kontenera i obsługi wyjątku - może to spowodować różnego rodzaju problemy z częściowym lub niepotrzebnym uruchomieniem kodu.

Scott Griffiths
źródło
15
To dobry sposób na sprawdzenie, czy zmienna jest iterowalna. Jednak prawdopodobnie nie zadziała na potrzeby tego pytania. Należy pamiętać, że łańcuch również jest iterowalny i prawdopodobnie stworzyłby fałszywy wynik dodatni.
Corey O.
setObiekt jest również iterable, co oznacza, że jednocześnie można na pewno pop elementy z nim, ale nie gwarantuje pewien porządek, który jest bardzo niebezpieczna dla niektórych algorytmów. W przypadkach, gdy kolejność elementów ma znaczenie, algorytm korzystający z tego fragmentu kodu może potencjalnie generować różne wyniki dla różnych przebiegów!
koo
14

Udokumentuj argument, że musi to być sekwencja, i użyj go jako sekwencji. Nie sprawdzaj typu.

Ignacio Vazquez-Abrams
źródło
12

Co powiesz na hasattr(a, "__iter__"):?

Informuje, czy zwrócony obiekt może być iterowany jako generator. Domyślnie krotki i listy mogą, ale nie typy ciągów.

użytkownik1914881
źródło
uważam to za bardzo przydatne.
javadba
8
Również wyniki Prawda dla ciągów (przynajmniej w Pythonie 3).
SzieberthAdam
2
To zła odpowiedź. Ponieważ typ „str” ma również metodę „ iter ”. @SzieberthAdam miał rację. Typ „set” również jest iterowalny, ale nie można go zamawiać.
PADYMKO,
Również dykta mają __iter__.
thet
Jeśli będziesz kontynuować, dowolny niestandardowy typ może mieć __iter__z jakiegoś powodu ...
Alexander Irbis
10

W przypadku type(list) is listzwrotów w języku Python 2.8 false
sugerowałbym porównanie tego typu w ten okropny sposób:

if type(a) == type([]) :
  print "variable a is a list"

(przynajmniej w moim systemie, używając anakondy w Mac OS X Yosemite)

Xanderite
źródło
1
Czy typ (a), czy lista również ma wartość false?
Dmitriy Sintsov
4
Miałeś na myśli Python 2.7.8? python.org/dev/peps/pep-0404/#official-pronouncement
Carl
Cześć, jestem ciekawy: dlaczego uważasz swój przykład za „okropny”?
Seth Connell,
type(list) is listpowraca Falsebo type(list)to typenie list. type(list()) is listlub z każdym innym wystąpieniem listy powróci True.
Michael Greene
8

Python używa „Kaczego pisania”, tzn. Jeśli zmienna kwaks jak kaczka, musi to być kaczka. W twoim przypadku prawdopodobnie chcesz, aby był iterowalny lub chcesz uzyskać dostęp do elementu pod określonym indeksem. Powinieneś po prostu to zrobić: np. Użyć obiektu w bloku for var:lub var[idx]wewnątrz trybloku, a jeśli dostaniesz wyjątek, nie będzie to kaczka ...

Wim
źródło
7
Problem polega na tym, czy variteracja łańcucha wystąpi z prawdopodobnie nieoczekiwanymi wynikami.
Brian M. Hunt
Pomimo faktu stwierdzonego przez Briana M. Hunta, jego rozwiązanie jest dość pytoniczne, jeśli chodzi o prośbę o wybaczenie, a nie o pozwolenie.
Géza Török
6
>>> l = []
>>> l.__class__.__name__ in ('list', 'tuple')
True
tetra5
źródło
3

Jeśli potrzebujesz tylko wiedzieć, czy możesz użyć foo[123]notacji ze zmienną, możesz sprawdzić istnienie __getitem__atrybutu (który wywołuje Python podczas dostępu przez indeks) za pomocąhasattr(foo, '__getitem__')

Geoff Reedy
źródło
2

Musi być bardziej złożonym testem, jeśli naprawdę chcesz obsłużyć prawie wszystko jako argument funkcji.

type(a) != type('') and hasattr(a, "__iter__")

Chociaż zwykle wystarczy przeliterować, że funkcja oczekuje iterowalności, a następnie tylko sprawdzić type(a) != type('').

Może się również zdarzyć, że dla łańcucha masz prostą ścieżkę przetwarzania lub będziesz miły i zrobisz podział itp., Więc nie chcesz krzyczeć na łańcuchy, a jeśli ktoś wyśle ​​ci coś dziwnego, pozwól mu mieć wyjątek.

ZXX
źródło
2

Innym łatwym sposobem sprawdzenia, czy zmienna jest listą, krotką lub ogólnie typem zmiennej, byłoby:

    def islist(obj):

        if ("list" in str(type(obj)) ): return True

        else : return False
Mahdi Omidiyan
źródło
1

Zasadniczo zgadzam się z Ignacio powyżej, ale możesz także użyć type, aby sprawdzić, czy coś jest krotką lub listą.

>>> a = (1,)
>>> type(a)
(type 'tuple')
>>> a = [1]
>>> type(a)
(type 'list')
Adam Crossland
źródło