Jaki jest kanoniczny sposób sprawdzania typu w Pythonie?

1276

Jaki jest najlepszy sposób sprawdzenia, czy dany obiekt jest określonego typu? Co powiesz na sprawdzenie, czy obiekt dziedziczy po danym typie?

Powiedzmy, że mam przedmiot o. Jak sprawdzić, czy to jest str?

Herge
źródło
7
Cóż, kanoniczne podejście w Pythonie polega na tym, aby w ogóle nie sprawdzać typu (chyba że debugujesz). Zwykle po prostu próbujesz użyć go jako łańcucha (np. Połączyć z innymi łańcuchami, drukować na konsoli itp.); jeśli uważasz, że to może się nie powieść, użyj try / else lub hasattr. To powiedziawszy, zaakceptowana odpowiedź jest kanonicznym sposobem robienia tego, czego na ogół „nie powinieneś” robić w świecie Pythona. Aby uzyskać więcej informacji, wejdź na Google „ Pisanie kaczek w języku Python” lub przeczytaj je: voidspace.org.uk/python/articles/duck_typing.shtml stackoverflow.com/questions/610883/...
Jon Coombs
9
Myślę, że pan Coombs przeoczy przykłady takie jak klasy szeregowalne inne niż JSON. Jeśli wstawiając dużą część danych przez funkcję (na kod, na którą nie można wpływać), można chcieć przekonwertować niektóre fragmenty tych danych, na przykład na <str> przed ich przekazaniem. Przynajmniej tak trafiłem na tę stronę ...
John Carrell,
2
Wydaje się, że najczęstszym powodem tego pytania jest to, że chce się rozróżnić ciągi znaków i iterowalne ciągi znaków. To trudne pytanie, ponieważ łańcuchy iterowalnymi ciągami - łańcuch jednoznakowy jest nawet sekwencją samego siebie (ostatnim razem, gdy sprawdzałem - prawdopodobnie nie należy na nim polegać). Ale czy ktokolwiek kiedykolwiek użyłby czegoś podobnego do sznurka? Tak . Zatem odpowiedź na „Co powinienem zrobić, aby odróżnić ciągi znaków od innych iterowalnych ciągów znaków?” jest właściwie: „To zależy od tego, co próbujesz zrobić”. :-D
clacke
2
Adnotacje typu Python są teraz rzeczą. Spójrz na mypy
Sheena

Odpowiedzi:

1522

Aby sprawdzić, czy ojest instancją strlub jakąkolwiek podklasą str, użyj isinstance (byłby to sposób „kanoniczny”):

if isinstance(o, str):

Aby sprawdzić, czy typ ojest dokładnie str(wyklucz podklasy):

if type(o) is str:

Następujące działania również działają i mogą być przydatne w niektórych przypadkach:

if issubclass(type(o), str):

Zobacz funkcje wbudowane w Python Library Reference istotnych informacji.

Jeszcze jedna uwaga: w tym przypadku, jeśli używasz Python 2, możesz chcieć użyć:

if isinstance(o, basestring):

ponieważ będzie to również złapać ciągi Unicode ( unicodenie jest podklasą str; oba stri unicodesą podklasy basestring). Zauważ, że basestringnie istnieje już w Pythonie 3, gdzie istnieje ścisłe oddzielenie ciągów ( str) i danych binarnych ( bytes).

Alternatywnie, isinstanceakceptuje krotkę klas. Zwróci to, Truejeśli ojest instancją dowolnej podklasy dowolnej z (str, unicode):

if isinstance(o, (str, unicode)):
Fredrik Johansson
źródło
31
str .__ podklasy __ () zwraca tylko bezpośrednie podklasy str i nie robi tego samego, co issubclass () lub isinstance (). (Aby to zrobić, musisz rekurencyjnie wywoływać .__ podklasy __ ().
Thomas Wouters
15
To dobra odpowiedź, ale myślę, że naprawdę powinna zacząć od ostrzeżenia, że ​​zwykle nie powinieneś robić tego w Pythonie. W tej chwili wydaje się to potwierdzać założenie, że jest to „kanoniczna rzecz do zrobienia w Pythonie”, a tak nie jest.
Jon Coombs,
4
To są odpowiedzi na python2. Na przykład w python3 nie ma łańcucha bazowego.
dfrankow
4
Jaka jest różnica między instancją a „dokładnie”? Jeśli type(a) is Objectto nie jest prawdą, że tak isinstance(a, Object). Jeśli jednak type(a) is SubClassOfObjectto type(a) is Object == False, ale isinstance(a, Object) == True. Dobrze?
mavavilj
1
@mavavilj - a is boznacza, że ​​aib są dokładnie tym samym, tzn. odniesienia do tego samego bytu w pamięci. Więc ai bmusiałaby być dokładnie taka sama klasa, a nie podklasy, jak w przypadku isinstance(). Zobacz na przykład stackoverflow.com/a/133024/1072212
Terry Brown
196

Najbardziej pythonowy sposób sprawdzić typ obiektu jest ... nie to sprawdzić.

Ponieważ Python zachęca do pisania kaczego , powinieneś po prostu try...exceptużywać metod obiektu tak, jak chcesz. Więc jeśli twoja funkcja szuka zapisywalnego obiektu pliku, nie sprawdzaj, czy jest to podklasa file, po prostu spróbuj użyć jego .write()metody!

Oczywiście czasami te ładne abstrakcje rozpadają się i isinstance(obj, cls)są tym, czego potrzebujesz. Ale używaj oszczędnie.

Dan Lenski
źródło
75
IMHO, najbardziej pythonicznym sposobem jest radzenie sobie z każdym podanym argumentem. W moim kodzie często nie wiem, czy otrzymuję obiekt, czy tablicę obiektów, i wewnętrznie używam sprawdzania typu, aby przekonwertować pojedynczy obiekt na listę jednoelementową.
sastanin
14
Zamiast próbować użyć metody zapisu, są chwile, kiedy chcesz to zrobić bez powodowania wyjątku. W takim przypadku możesz zrobić ... if hasattr(ob, "write") and callable(ob.write): lub zapisać dostęp do func = getattr(ob, "write", None) if callable(func): ...
nagrań
142
Wpisywanie kaczki polega na korzystaniu z biblioteki. Sprawdzanie typu polega na pisaniu biblioteki. Domena nie jest tym samym problemem.
RickyA,
16
@RickyA, nie zgadzam się. Wpisywanie kaczych polega na interakcji z obiektami za pomocą interfejsów o znanej semantyce. Może to dotyczyć zarówno kodu biblioteki, jak i kodu korzystającego z takiej biblioteki.
Dan Lenski
6
@ nyuszika7h, W Python3 tłumi hasattrtylko błąd AttributeError - patrz: docs.python.org/3.4/library/functions.html#hasattr
ideasman42
57

isinstance(o, str)zwróci, Truejeśli ojest strlub jest typu, który dziedziczy str.

type(o) is strwróci, Truejeśli i tylko jeśli ojest str. Zwróci, Falsejeśli ojest typu, który dziedziczy str.

Herge
źródło
6
Oczywiście to się nie powiedzie, jeśli obiekt nie jest instancją „str”, lecz czegoś w rodzaju łańcucha. Jak unicode, mmap, UserString lub dowolny inny typ zdefiniowany przez użytkownika. Typowe podejście w Pythonie nie polega na sprawdzaniu typów.
Thomas Wouters
6
Nie musisz przepraszać, możesz odpowiedzieć na własne pytanie. SO dotyczy odpowiedzi, a nie karmy.
Eli Bendersky
2
To jest bardzo pomocne. Ponieważ różnica między isinstancei type(var) == type('')nie jest wyraźna.
sastanin
30

Po zadaniu pytania i udzieleniu odpowiedzi do Pythona dodano wskazówki typu . Wskazówki dotyczące typów w Pythonie umożliwiają sprawdzanie typów, ale w zupełnie inny sposób niż w przypadku języków typowanych statycznie. Wskazówki dotyczące typów w Pythonie kojarzą oczekiwane typy argumentów z funkcjami jako dane dostępne w czasie wykonywania powiązane z funkcjami, co pozwala na sprawdzenie typów. Przykład składni podpowiedzi typu:

def foo(i: int):
    return i

foo(5)
foo('oops')

W tym przypadku chcemy, aby wystąpił błąd, foo('oops')ponieważ typ argumentu z adnotacją to int. Dodana wskazówka typu nie powoduje wystąpienia błędu, gdy skrypt jest uruchamiany normalnie. Dodaje jednak atrybuty do funkcji opisującej oczekiwane typy, które inne programy mogą wyszukiwać i wykorzystywać do sprawdzania błędów typów.

Jednym z tych innych programów, za pomocą których można znaleźć błąd typu, jest mypy:

mypy script.py
script.py:12: error: Argument 1 to "foo" has incompatible type "str"; expected "int"

(Może być konieczne zainstalowanie mypyz poziomu menedżera pakietów. Nie wydaje mi się, aby zawierał CPython, ale wydaje się, że ma pewien poziom „oficjalności”).

Sprawdzanie typów w ten sposób różni się od sprawdzania typów w statycznie skompilowanych językach kompilowanych. Ponieważ typy są dynamiczne w Pythonie, sprawdzanie typów musi być wykonywane w czasie wykonywania, co nakłada koszty - nawet na prawidłowe programy - jeśli nalegamy, aby zdarzyło się to przy każdej okazji. Jawne sprawdzanie typu może być również bardziej restrykcyjne niż to konieczne i powodować niepotrzebne błędy (np. Czy argument naprawdę musi być dokładnie tego samego listtypu, czy może wystarczy iterowalne powtórzenie?).

Zaletą jawnego sprawdzania typów jest to, że może wcześniej wychwytywać błędy i dawać wyraźniejsze komunikaty o błędach niż pisanie kaczych znaków. Dokładne wymagania dla typu kaczki można wyrazić tylko za pomocą dokumentacji zewnętrznej (mam nadzieję, że jest to dokładne i dokładne), a błędy z niekompatybilnych typów mogą wystąpić daleko od miejsca, z którego pochodzą.

Wskazówki dotyczące typów w Pythonie mają na celu zaoferowanie kompromisu, w którym typy można określać i sprawdzać, ale nie ma dodatkowych kosztów podczas zwykłego wykonywania kodu.

Te typingzmienne oferty Rodzaj opakowania, które mogą być stosowane w podpowiedzi typu zachowań potrzebnych do wyrażenia bez potrzeby poszczególnych rodzajów. Na przykład zawiera zmienne takie jak Iterablei Callablepodpowiedzi, aby określić potrzebę dowolnego typu z tymi zachowaniami.

Chociaż wskazówki dotyczące typów są najbardziej Pythońskim sposobem sprawdzania typów, często jeszcze bardziej Pythonic w ogóle nie sprawdza typów i polega na pisaniu kaczek. Podpowiedzi do czcionek są stosunkowo nowe, a jury jest nadal aktywne, gdy są najbardziej pythonicznym rozwiązaniem. Względnie niekontrowersyjne, ale bardzo ogólne porównanie: Podpowiedzi dotyczące typów zapewniają formę dokumentacji, którą można egzekwować, pozwalają na wcześniejsze generowanie kodu i łatwiejsze do zrozumienia błędy, mogą wychwycić błędy, których nie można wpisać podczas pisania kaczki, i można je sprawdzić statycznie (w nietypowy sposób sens, ale wciąż jest poza środowiskiem uruchomieniowym). Z drugiej strony, pisanie kaczką było od dawna sposobem Pythona, nie narzuca poznawczego pisania statycznego, jest mniej gadatliwe i akceptuje wszystkie możliwe typy, a potem niektóre.

Prakseolityczny
źródło
2
-1: mypy określa się mianem „statycznego sprawdzania typu”, więc nie jestem pewien, skąd masz „sprawdzanie typu należy wykonać w czasie wykonywania”.
Kevin,
@Kevin Z perspektywy czasu była to niepotrzebna dygresja, ale aby uzyskać więcej informacji, wskazówki typu Pythona są przekształcane w dane środowiska wykonawczego i mypysą modułem Pythona używającym importlibdostępu do tych danych. To, czy jest to „statyczne sprawdzanie typu”, jest filozoficznym pytaniem, ale różni się od tego, czego większość by się spodziewała, ponieważ zaangażowany jest zwykły tłumacz języka i maszyny importujące.
Praxeolitic
4
To też nie jest prawda. To używa typed_ast, który sam w sobie jest po prostu klonem ast z dodatkowych funkcji. ast nie importuje modułów; analizuje je w abstrakcyjne drzewo składniowe.
Kevin,
18

Oto przykład, dlaczego pisanie kaczek jest złe, nie wiedząc, kiedy jest niebezpieczne. Na przykład: Oto kod Pythona (być może pomijający właściwe wcięcia), zauważ, że takiej sytuacji można uniknąć, zajmując się funkcjami isinstance i issclassof, aby upewnić się, że kiedy naprawdę potrzebujesz kaczki, nie dostaniesz bomby.

class Bomb:
    def __init__(self):
        ""

    def talk(self):
        self.explode()

    def explode(self):
        print "BOOM!, The bomb explodes."

class Duck:
    def __init__(self):
        ""
    def talk(self):
        print "I am a duck, I will not blow up if you ask me to talk."    

class Kid:
    kids_duck = None

    def __init__(self):
        print "Kid comes around a corner and asks you for money so he could buy a duck."

    def takeDuck(self, duck):
        self.kids_duck = duck
        print "The kid accepts the duck, and happily skips along"

    def doYourThing(self):
        print "The kid tries to get the duck to talk"
        self.kids_duck.talk()

myKid = Kid()
myBomb = Bomb()
myKid.takeDuck(myBomb)
myKid.doYourThing()
Dmitry
źródło
36
Nawet przy sprawdzaniu typu można utworzyć class EvilDuck(Duck)i zastąpić talk (). Lub bardziej prawdopodobne, class ChineseCancerDuck(Duck)z nieprzyjemnym efektem ubocznym, który pojawia się dopiero po latach. Lepiej byłoby po prostu nadzorować swoje dziecko (i dokładnie testować jej zabawki :)
Brett Thomas,
36
Bomby nie mówią. Nie dodawaj nonsensownych metod, a to się nie stanie.
prawej
7
@Dmitry, to jest powszechna krytyka Duck Typing: en.wikipedia.org/wiki/Duck_typing#Criticism ... po prostu mówisz, że każdy interfejs, dla którego semantyka nie jest egzekwowana przez język, jest zły. Wierzę, że jest to bardziej podejście Java. Chodzi o to, że pisanie kaczek w Pythonie polega na tym, że działa tylko wtedy, gdy istnieje powszechnie stosowana konwencja o tym, co oznaczają określone interfejsy. Na przykład możesz pokorkować dużo kodu Pythona, zastępując __file__atrybut (zwykle używany do identyfikowania obiektów podobnych do plików) i oznaczający coś innego.
Dan Lenski
2
Wszystko sprowadza się do starego żartu: „Doktorze, boli mnie, kiedy to robię”. ... „Więc nie rób tego.”. Niezadowalające dla kogoś, kto jest przyzwyczajony do „jeśli się kompiluje, to działa”, ale właśnie dlatego testowanie obsesji wyrosło z dynamicznego świata językowego.
clacke
1
@clacke w zasadzie wymuszanie typów w środowisku wykonawczym jest zbyt drogie, ponieważ WSZYSTKO musi być obiektem (aby możliwe było odwzorowanie łańcucha na dowolny możliwy typ), i zbyt wygodne, aby nie robić uników, ponieważ unikanie pozwala na naprawdę potężne techniki prototypowania, które pokonują rzeczy, które są zwykle bardzo trudne do wykonania ze sztywnymi interfejsami. Poza tym każdy język statyczny stoi w obliczu punktu, w którym musi tworzyć pisanie kaczek za pomocą dynamicznych bibliotek, oceny i strunizacji lub interfejsów, a te rzeczy z natury nie powodują, że jest zły, a jedynie bardzo potężny.
Dmitry
12
isinstance(o, str)

Link do dokumentów

Alexander Kojevnikov
źródło
1
Chociaż ten link może odpowiedzieć na pytanie, lepiej dołączyć tutaj istotne części odpowiedzi i podać link w celach informacyjnych. Odpowiedzi zawierające tylko łącze mogą stać się nieprawidłowe, jeśli połączona strona ulegnie zmianie.
EKons,
7

Myślę, że fajną rzeczą w używaniu dynamicznego języka takiego jak Python jest to, że naprawdę nie powinieneś sprawdzać czegoś takiego.

Po prostu wywołałbym wymagane metody na twoim obiekcie i złapał AttributeError. Później pozwoli ci to wywoływać swoje metody z innymi (pozornie niezwiązanymi) obiektami w celu wykonania różnych zadań, takich jak wyśmiewanie obiektu do testowania.

Używałem tego bardzo często podczas pobierania danych z Internetu, za pomocą urllib2.urlopen()których zwracany jest obiekt typu plik . To z kolei można przekazać do prawie każdej metody, która czyta z pliku, ponieważ implementuje tę samą read()metodę, co prawdziwy plik.

Ale jestem pewien, że jest czas i miejsce do korzystania isinstance(), w przeciwnym razie prawdopodobnie nie byłoby tam :)

Will Harding
źródło
Dobrym przykładem tego, kiedy należy go użyć, jest analizowanie dynamicznego obiektu Json. Nie wiadomo z góry, czy pole jest łańcuchem czy słownikiem.
Gray
6

W przypadku bardziej złożonych walidacji typów podoba mi się podejście walidatora typeguard oparte na adnotacjach typu python:

from typeguard import check_type
from typing import List

try:
    check_type('mylist', [1, 2], List[int])
except TypeError as e:
    print(e)

Możesz przeprowadzać bardzo złożone weryfikacje w bardzo czysty i czytelny sposób.

check_type('foo', [1, 3.14], List[Union[int, float]])
# vs
isinstance(foo, list) and all(isinstance(a, (int, float)) for a in foo) 
Granitozaur
źródło
6

Możesz sprawdzić typ zmiennej, używając __name__ typu.

Dawny:

>>> a = [1,2,3,4]  
>>> b = 1  
>>> type(a).__name__
'list'
>>> type(a).__name__ == 'list'
True
>>> type(b).__name__ == 'list'
False
>>> type(b).__name__
'int'
Yerramsetty Rohit
źródło
Dzięki, to jest tajny kod, którego chciałem, gdy wyświetlałem go jako informację zwrotną dla użytkownika. Zajęło mi to zbyt długo, aby to znaleźć ...
Aaron D. Marasco
5

Do Hugo:

Prawdopodobnie masz na myśli listraczej niż array, ale wskazuje to na cały problem ze sprawdzaniem typu - nie chcesz wiedzieć, czy przedmiotowy obiekt jest listą, chcesz wiedzieć, czy jest to jakaś sekwencja, czy pojedynczy obiekt. Spróbuj więc użyć go jak sekwencji.

Powiedz, że chcesz dodać obiekt do istniejącej sekwencji lub jeśli jest to sekwencja obiektów, dodaj je wszystkie

try:
   my_sequence.extend(o)
except TypeError:
  my_sequence.append(o)

Jedna sztuczka polega na tym, jeśli pracujesz z ciągami i / lub sekwencjami ciągów - to podchwytliwe, ponieważ ciąg jest często uważany za pojedynczy obiekt, ale jest to również ciąg znaków. Gorzej, ponieważ tak naprawdę jest to ciąg ciągów o jednej długości.

Zwykle wybieram takie zaprojektowanie interfejsu API, aby akceptował tylko jedną wartość lub sekwencję - to ułatwia. W [ ]razie potrzeby nie jest trudno obliczyć wartość pojedynczą, gdy ją przekażesz.

(Chociaż może to powodować błędy w łańcuchach, ponieważ wyglądają jak (są) sekwencjami.)

Chris Barker
źródło
0

Prostym sposobem sprawdzenia typu jest porównanie go z czymś, kogo znasz.

>>> a  = 1
>>> type(a) == type(1)
True
>>> b = 'abc'
>>> type(b) == type('')
True
Ultrablendz
źródło
-1

Myślę, że najlepszym sposobem jest dobre wpisanie zmiennych. Możesz to zrobić, korzystając z biblioteki „pisania”.

Przykład:

from typing import NewType UserId = NewType ('UserId', int) some_id = UserId (524313) `

Zobacz https://docs.python.org/3/library/typing.html

cherah30
źródło
-7

Możesz sprawdzić w poniższym wierszu, aby sprawdzić, jaki typ znaku jest podana wartość:

def chr_type(chrx):
    if chrx.isalpha()==True:
        return 'alpha'
    elif chrx.isdigit()==True:
        return 'numeric'
    else:
        return 'nothing'

chr_type("12)
Venkatesan
źródło
3
Na pewno nie chcesz usunąć tej odpowiedzi @Venkatesan?
Gray