Tak zwykle robię, aby upewnić się, że dane wejściowe to list
/ tuple
- ale nie str
. Ponieważ wiele razy natknąłem się na błędy, w których funkcja str
omyłkowo mija obiekt, a funkcja celu for x in lst
przyjmuje, że lst
tak naprawdę jest to list
lub tuple
.
assert isinstance(lst, (list, tuple))
Moje pytanie brzmi: czy jest lepszy sposób na osiągnięcie tego?
Odpowiedzi:
Tylko w python 2 (nie python 3):
Jest właściwie tym, czego chcesz, w przeciwnym razie przegapisz wiele rzeczy, które działają jak listy, ale nie są podklasami
list
lubtuple
.źródło
basestring
nie ma, a ty po prostu sprawdźisinstance(lst, str)
.set
Wyrażenia generatora, iteratory. Są rzeczy egzotycznemmap
, mniej egzotyczne, takiearray
jak listy, i prawdopodobnie wiele więcej zapomniałem.lst
to iterowalności, podczas gdy oryginał zrobił (np. Int przeszedłby tę kontrolę)assert isinstance(lst, (list, tuple)) and assert not isinstance(lst, basestring)
Pamiętaj, że w Pythonie chcemy używać „pisania kaczego”. Tak więc wszystko, co działa jak lista, może być traktowane jako lista. Nie sprawdzaj więc typu listy, po prostu sprawdź, czy działa ona jak lista.
Ale łańcuchy znaków również działają jak lista i często nie tego chcemy. Są chwile, kiedy jest to nawet problem! Sprawdź więc wyraźnie, czy nie ma łańcucha, ale następnie użyj wpisywania kaczki.
Oto funkcja, którą napisałem dla zabawy. Jest to specjalna wersja,
repr()
która drukuje dowolną sekwencję w nawiasach kątowych („<”, „>”).Ogólnie rzecz biorąc, jest to czyste i eleganckie. Ale co
isinstance()
tam robi ta kontrola? To rodzaj włamania. Ale to jest niezbędne.Ta funkcja wywołuje się rekurencyjnie do wszystkiego, co działa jak lista. Gdybyśmy nie traktowali łańcucha specjalnie, wówczas byłby traktowany jak lista i dzieliłby po jednym znaku na raz. Ale wtedy wywołanie rekurencyjne spróbuje traktować każdą postać jak listę - i zadziała! Nawet ciąg jednoznakowy działa jako lista! Funkcja będzie się ciągle wywoływać rekurencyjnie, aż do przepełnienia stosu.
Funkcje takie jak ta, które zależą od każdego wywołania rekurencyjnego rozkładającego pracę do wykonania, wymagają ciągów znaków specjalnych - ponieważ nie można rozbić ciągu poniżej poziomu ciągu jednego znaku, a nawet jednego -znak znaków działa jak lista.
Uwaga:
try
/except
jest najczystszym sposobem wyrażenia naszych intencji. Ale jeśli ten kod byłby w jakiś sposób krytyczny czasowo, moglibyśmy chcieć zastąpić go jakimś testem, aby sprawdzić, czyarg
jest to sekwencja. Zamiast testować typ, powinniśmy prawdopodobnie przetestować zachowania. Jeśli ma.strip()
metodę, jest to ciąg znaków, więc nie traktuj jej jako sekwencji; w przeciwnym razie, jeśli jest indeksowalny lub iterowalny, jest to sekwencja:EDYCJA: Pierwotnie napisałem powyższe z zaznaczeniem,
__getslice__()
ale zauważyłem, że wcollections
dokumentacji modułu ciekawą metodą jest__getitem__()
; ma to sens, tak indeksujesz obiekt. Wydaje się to bardziej fundamentalne,__getslice__()
więc zmieniłem powyższe.źródło
srepr
to bardzo interesujący pomysł. Ale ja mam inne zdanie niż ty, czy to wymaga specjalnego przypadkustr
. Tak,str
jest zdecydowanie najbardziej oczywistą i powszechną iteracją, która spowodowałaby nieskończoną rekurencjęsrepr
. Ale mogę łatwo wyobrazić sobie iteratory zdefiniowane przez użytkownika, które zachowują się w ten sam sposób (z lub bez uzasadnionego powodu). Zamiast specjalnego przypadkustr
, powinniśmy przyznać, że takie podejście może spotkać się z nieskończoną rekurencją i zgodzić się na jakiś sposób radzenia sobie z tym. Zamieszczę moją sugestię w odpowiedzi.srepr()
jest w porządku. Potrzebujemy funkcji rekurencyjnej do obsługi rzeczy takich jak lista zagnieżdżona na innej liście; ale w przypadku łańcuchów wolelibyśmy je wydrukować jako"foo"
niż jako<'f', 'o', 'o'>
. Dlatego wyraźne sprawdzenie łańcucha ma tutaj sens. Poza tym naprawdę nie ma innych przykładów typów danych, w których iteracja zawsze zwraca iterowalność, a rekurencja zawsze powoduje przepełnienie stosu, więc nie potrzebujemy specjalnej właściwości do testowania tego („Praktyczność pokonuje czystość”).__iter__()
metodę w Pythonie 3, ale nie w Pythonie 2. Brakuje nawiasówis_sequence()
, powinien on brzmieć:return (not hasattr(arg, "strip") and (hasattr(arg, "__getitem__") or hasattr(arg, "__iter__")))
źródło
if isinstance( H, (list, tuple) ): ...
jest ona krótsza i jaśniejsza.if type(H) in [list, tuple]:
W przypadku Python 3:
W przypadku Python 2:
źródło
collections.Sequence
ale przetestowałem to i widzę, że tak. Tak też jestxrange
. Co więcej, ten test wykluczadict
, który ma zarówno__getitem__
i__iter__
.inspect.getmro(list)
nie obejmujeSequence
? Jak mamy dowiedzieć się, co możemy zrobić,isinstance
gdygetmro
nie wszystko pokazuje?Sequence
jest klasą abstrakcyjną.Python o smaku PHP:
źródło
__getitem__
. Również nazwa wprowadza w błąd, ponieważ istnieje również moduł tablicy. A var może być również numpy.ndarray lub dowolnym innym typem, który ma__getitem__
. Zobacz stackoverflow.com/a/1835259/470560 dla poprawnej odpowiedzi.str
również__getitem__
Twój czek nie wykluczastr
__getitem__
jest to zła rada tutaj.Ogólnie rzecz biorąc, fakt, że funkcja, która iteruje obiekt, działa zarówno na ciągach, jak i krotkach i listach, jest bardziej cechą niż błędem. Na pewno możesz użyć
isinstance
klawisza lub klawisza, aby sprawdzić argument, ale dlaczego?To brzmi jak pytanie retoryczne, ale tak nie jest. Odpowiedź na „dlaczego powinienem sprawdzić typ argumentu?” prawdopodobnie zaproponuje rozwiązanie prawdziwego problemu, a nie postrzeganego problemu. Dlaczego błąd jest przekazywany do funkcji? Ponadto: jeśli jest to błąd, gdy ciąg znaków jest przekazywany do tej funkcji, to czy jest to również błąd, jeśli przekazywana jest do niego inna iterowalna lista / krotka? Dlaczego lub dlaczego nie?
Myślę, że najczęstszą odpowiedzią na to pytanie może być to, że programiści, którzy piszą,
f("abc")
oczekują, że funkcja będzie zachowywać się tak, jak gdyby napisalif(["abc"])
. Prawdopodobnie istnieją sytuacje, w których bardziej sensowne jest chronienie programistów przed sobą, niż wspieranie przypadku iteracji znaków w ciągu. Ale najpierw długo się nad tym zastanowię.źródło
Wypróbuj to dla czytelności i najlepszych praktyk:
Python2
Python3
Mam nadzieję, że to pomoże.
źródło
AttributeError: module 'types' has no attribute 'ListType'
from typing import List
->isinstance([1, 2, 3], List
=True
iisinstance("asd", List)
=False
str
Obiekt nie ma__iter__
atrybutuabyś mógł sprawdzić
i to również podniesie ładność
AssertionError
dla każdego innego obiektu, który nie będzie iterowalny.Edytować: Jak Tim wspomina w komentarzach, będzie to działać tylko w Pythonie 2.x, a nie 3.x
źródło
hasattr('','__iter__')
powracaTrue
. Ma to oczywiście sens, ponieważ można iterować po łańcuchu.To nie ma na celu bezpośredniej odpowiedzi na PO, ale chciałem podzielić się kilkoma powiązanymi pomysłami.
Byłem bardzo zainteresowany powyższą odpowiedzią @steveha, która zdawała się dawać przykład, w którym pisanie kaczek wydaje się łamać. Jednak przy drugim przemyśleniu jego przykład sugeruje, że pisanie kaczek jest trudne do dostosowania, ale nie sugeruje, że
str
zasługuje na specjalne podejście.W końcu inny niż
str
typ (np. Typ zdefiniowany przez użytkownika, który utrzymuje pewne skomplikowane struktury rekurencyjne) może spowodować, żesrepr
funkcja @steveha spowoduje nieskończoną rekurencję. Chociaż jest to raczej mało prawdopodobne, nie możemy zignorować tej możliwości. Dlatego zamiast specjalnej obudowystr
wsrepr
, powinniśmy wyjaśnić, co chcemysrepr
zrobić, gdy nieskończonej wyniki rekursji.Może się wydawać, że jednym rozsądnym podejściem jest po prostu przerwanie rekurencji w
srepr
danym momencielist(arg) == [arg]
. To całkowicie rozwiązałoby problemstr
bez żadnegoisinstance
.Jednak naprawdę skomplikowana struktura rekurencyjna może powodować nieskończoną pętlę, w której
list(arg) == [arg]
nigdy się nie zdarza. Dlatego chociaż powyższa kontrola jest przydatna, nie jest wystarczająca. Potrzebujemy czegoś w rodzaju twardego ograniczenia głębokości rekurencji.Chodzi mi o to, że jeśli planujesz obsługiwać dowolne typy argumentów, obsługa
str
za pomocą pisania kaczego jest o wiele łatwiejsza niż obsługa bardziej ogólnych typów, które możesz (teoretycznie) napotkać. Jeśli więc czujesz potrzebę wykluczeniastr
instancji, powinieneś zamiast tego zażądać, aby argument był instancją jednego z niewielu typów, które wyraźnie określiłeś.źródło
str
, który obsługuje kod specjalnego przypadku. Ale może powinna istnieć nowa standardowa właściwość, którą kod może sprawdzać,.__atomic__
powiedzmy, która sygnalizuje, że czegoś nie da się dalej zepsuć. Prawdopodobnie jest już za późno, aby dodać kolejną wbudowaną funkcjęatomic()
do Pythona, ale może możemy to dodaćfrom collections import atomic
lub coś takiego.W funkcji tensorflow znajduję taką funkcję o nazwie is_sequence .
I zweryfikowałem, że spełnia twoje potrzeby.
źródło
Robię to w moich testach.
Nieprzetestowane na generatorach, myślę, że pozostaniesz przy następnej „wydajności”, jeśli zostaniesz przekazany do generatora, co może zepsuć wszystko w dół rzeki. Ale z drugiej strony jest to „niestosowne”
źródło
Co powiesz na „pisanie kaczką”?
lub
odpowiednio. Pozwala to uniknąć
isinstance
/hasattr
introspekcji.Możesz także sprawdzić na odwrót:
Wszystkie warianty tak naprawdę nie zmieniają zawartości zmiennej, ale sugerują zmianę przypisania. Nie jestem pewien, czy w pewnych okolicznościach może to być niepożądane.
Co ciekawe, przy przypisaniu „na miejscu”
+=
nieTypeError
pojawi się żadna lista, jeślilst
jest to lista (a nie krotka ). Dlatego zadanie jest wykonywane w ten sposób. Może ktoś może wyjaśnić, dlaczego tak jest.źródło
najprostszy sposób ... używając
any
iisinstance
źródło
Inna wersja pisma kaczego, która pomaga odróżnić obiekty łańcuchowe od innych obiektów podobnych do sekwencji.
Reprezentacja ciągu obiektów podobnych do łańcucha jest samym łańcuchem, dzięki czemu można sprawdzić, czy otrzymano od
str
konstruktora równy obiekt :Powinno to działać dla wszystkich obiektów zgodnych z
str
i dla wszystkich rodzajów obiektów iterowalnych.źródło
Python 3 ma to:
Aby sprawdzić zarówno Listy, jak i Tuple, byłoby to:
źródło
źródło
Po prostu to zrób
źródło
w python> 3.6
źródło
str
, a nie ciągu. Spróbuj,isinstance('my_string', collections.abc.Container)
a zobaczysz, że wróciTrue
. Wynika to z tego, żeabc.Container
dostarcza__contains__
metodę, a łańcuchy ją mają, oczywiście.Zwykle to robię (jeśli naprawdę, naprawdę musiałem):
źródło
some_var
instancją klasy, która jest podklasąlist()
? Twój kod nie będzie miał pojęcia, co z nim zrobić, nawet jeśli będzie działał idealnie pod kodem „zrób coś z listą”. I rzadko trzeba dbać o różnicę między listą a krotką. Przepraszam, -1.type(tuple())
-tuple
zrobi. To samo dla listy. Ponadto zarównostr
iunicode
rozszerzbasestring
, który jest prawdziwym typem łańcucha, więc zamiast tego chcesz to sprawdzić.type(i) is list
. Ponadto,type(list())
jest po prostulist
sam… Wreszcie, nie działa to z gracją z podklasami. Jeślii
w rzeczywistości jest to OragedDict, lub jakiś nametuple, ten kod będzie traktowany jako ciąg znaków.