Frage Was sind die Unterschiede zwischen type () und isinstance ()?


Was sind die Unterschiede zwischen diesen beiden Codefragmenten? Verwenden type():

import types

if type(a) is types.DictType:
    do_something()
if type(b) in types.StringTypes:
    do_something_else()

Verwenden isinstance():

if isinstance(a, dict):
    do_something()
if isinstance(b, str) or isinstance(b, unicode):
    do_something_else()

933
2017-10-11 03:50


Ursprung


Antworten:


Um den Inhalt anderer (schon guter!) Antworten zusammenzufassen, isinstance sorgt für Vererbung (eine Instanz einer abgeleiteten Klasse ist ein Instanz einer Basisklasse), während auf Gleichheit von type nicht (es erfordert die Identität von Typen und lehnt Instanzen von Subtypen ab, Unterklassen von AKA).

Normalerweise in Python, wollen Sie Ihren Code Erbe zu unterstützen, natürlich (da Vererbung so handlich ist, wäre es schlecht sein, Code zu verwenden stoppen Sie verwendet es!), So isinstance ist weniger schlecht als die Identität von überprüfen types, weil es die Vererbung nahtlos unterstützt.

Es ist nicht das isinstance ist gut, wohlgemerkt - es ist nur Weniger schlecht als Überprüfung der Gleichheit der Typen. Die normale, pythonsche, bevorzugte Lösung ist fast immer "Ententipp": versuchen Sie es mit dem Argument als ob es war von einem bestimmten gewünschten Typ, mach es in einem try/except Anweisung, die alle Ausnahmen abfängt, die entstehen könnten, wenn das Argument nicht tatsächlich von diesem Typ wäre (oder irgendein anderer Typ, der es nett nachahmt ;-), und in der except Klausel, versuchen Sie etwas anderes (mit dem Argument "als ob" es war von einem anderen Typ).

basestring  istallerdings ein ganz besonderer Fall - ein eingebauter Typ, der existiert nur um dich benutzen zu lassen isinstance (beide str und unicode Unterklasse basestring). Strings sind Sequenzen (Sie könnten Schleife über sie, indizieren sie, so dass sie in Scheiben schneiden, ...), aber Sie wollen in der Regel, sie zu behandeln, als „Skalar“ Typen-it etwas incovenient ist (aber ein recht häufiger Anwendungsfall) alle Arten von behandeln Strings (und vielleicht andere skalare Typen, also solche, die man nicht anschleifen kann) in einer Richtung, alle Container (Listen, Sets, Dicts, ...) auf andere Weise und basestring Plus isinstance hilft Ihnen dabei - die Gesamtstruktur dieses Idioms ist etwas wie:

if isinstance(x, basestring)
  return treatasscalar(x)
try:
  return treatasiter(iter(x))
except TypeError:
  return treatasscalar(x)

Das könntest du sagen basestring ist ein Abstrakte Basisklasse ("ABC") - es bietet keine konkrete Funktionalität für Unterklassen, sondern existiert eher als "Marker", hauptsächlich zur Verwendung mit isinstance. Das Konzept ist offensichtlich ein wachsendes in Python, seit PEP 3119, die eine Verallgemeinerung davon einführt, wurde akzeptiert und wurde mit Python 2.6 und 3.0 implementiert.

Die PEP macht deutlich, dass ABC zwar oft die Art der Ente ersetzen kann, aber dafür gibt es im Allgemeinen keinen großen Druck (vgl Hier). ABCs, wie sie in neueren Python-Versionen implementiert wurden, bieten jedoch zusätzliche Extras: isinstance (und issubclass) Kann nun mehr als nur „[eine Instanz] eine abgeleiteten Klasse“ (insbesondere bedeutet, kann jede Klasse „registriert“ mit einem ABC werden, so dass es als eine Unterklasse zeigen, und seine Instanzen als Instanzen des ABC); und ABCs können den tatsächlichen Unterklassen auf eine sehr natürliche Art und Weise zusätzlichen Komfort bieten, indem Mustermusteranwendungsmuster verwendet werden (siehe Hier und Hier [[Teil II]] für mehr auf der TM DP, im Allgemeinen und speziell in Python, unabhängig von ABCs).

Für die zugrunde liegende Mechanik der ABC-Unterstützung, wie sie in Python 2.6 angeboten wird, siehe Hier; für ihre Version 3.1, sehr ähnlich, siehe Hier. In beiden Versionen Standard-Bibliotheksmodul Sammlungen (Das ist die Version 3.1 - für die sehr ähnliche Version 2.6, siehe Hier) bietet mehrere nützliche ABCs.

Für die Zwecke dieser Antwort ist das Wichtigste, was man über ABCs behalten sollte (jenseits einer wohl natürlicheren Platzierung für TM DP Funktionalität, verglichen mit der klassischen Python Alternative von Mixinklassen wie UserDict.DictMixin) ist, dass sie machen isinstance (und issubclass) viel attraktiver und durchdringender (in Python 2.6 und vorwärts) als früher (in 2.5 und davor), und daher machen die Überprüfung der Typgleichheit eine noch schlimmere Praxis in den letzten Python-Versionen als es schon einmal war .


988
2017-10-11 04:31



Hier ist der Grund isinstance ist besser als type:

class Vehicle:
    pass

class Truck(Vehicle):
    pass

In diesem Fall ist ein LKW-Objekt ein Fahrzeug, aber Sie erhalten Folgendes:

isinstance(Vehicle(), Vehicle)  # returns True
type(Vehicle()) == Vehicle      # returns True
isinstance(Truck(), Vehicle)    # returns True
type(Truck()) == Vehicle        # returns False, and this probably won't be what you want.

Mit anderen Worten, isinstance gilt auch für Unterklassen.

Siehe auch: Wie vergleicht man den Typ eines Objekts in Python?


260
2017-10-11 03:58



Unterschiede zwischen isinstance() und type() in Python?

Typprüfung mit

isinstance(obj, Base)

ermöglicht Instanzen von Unterklassen und mehrere mögliche Basen:

isinstance(obj, (Base1, Base2))

während Typprüfung mit

type(obj) is Base

unterstützt nur den referenzierten Typ.


Als Anmerkung, is ist wahrscheinlich passender als

type(obj) == Base

weil Klassen Singles sind.

Vermeiden Sie die Typprüfung - verwenden Sie Polymorphismus (Duck-Typisierung)

In Python möchten Sie normalerweise jeden Typ für Ihre Argumente zulassen, wie erwartet behandeln, und wenn sich das Objekt nicht wie erwartet verhält, wird ein entsprechender Fehler ausgelöst. Dies wird als Polymorphismus, auch bekannt als Duck-Typisierung, bezeichnet.

def function_of_duck(duck):
    duck.quack()
    duck.swim()

Wenn der obige Code funktioniert, können wir annehmen, dass unser Argument eine Ente ist. So können wir andere Dinge als tatsächliche Subtypen der Ente weitergeben:

function_of_duck(mallard)

oder das funktioniert wie eine Ente:

function_of_duck(object_that_quacks_and_swims_like_a_duck)

und unser Code funktioniert immer noch.

Es gibt jedoch einige Fälle, in denen eine explizite Typüberprüfung erwünscht ist. Vielleicht haben Sie sinnvolle Dinge mit verschiedenen Objekttypen zu tun. Zum Beispiel kann das Pandas-Dataframe-Objekt aus Dicts aufgebaut werden oder Aufzeichnungen. In diesem Fall muss der Code wissen, welche Art von Argument er erhält, damit er korrekt verarbeitet werden kann.

Also, um die Frage zu beantworten:

Unterschiede zwischen isinstance() und type() in Python?

Erlaube mir, den Unterschied zu demonstrieren:

type

Angenommen, Sie müssen ein bestimmtes Verhalten sicherstellen, wenn Ihre Funktion eine bestimmte Art von Argument erhält (ein häufiger Anwendungsfall für Konstruktoren). Wenn Sie nach Typ wie diesem suchen:

def foo(data):
    '''accepts a dict to construct something, string support in future'''
    if type(data) is dict:
        # we're only going to test for dicts for now
        raise ValueError('only dicts are supported for now')

Wenn wir versuchen, ein Diktat zu übergeben, das eine Unterklasse von ist dict (wie wir es können sollten, wenn wir erwarten, dass unser Code dem Prinzip von Liskov Substitution, dass Subtypen für Typen ersetzt werden können) unser Code bricht !:

from collections import OrderedDict

foo(OrderedDict([('foo', 'bar'), ('fizz', 'buzz')]))

wirft einen Fehler auf!

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 3, in foo
ValueError: argument must be a dict

isinstance

Aber wenn wir es benutzen isinstance, wir können Liskov Substitution unterstützen !:

def foo(a_dict):
    if not isinstance(a_dict, dict):
        raise ValueError('argument must be a dict')
    return a_dict

foo(OrderedDict([('foo', 'bar'), ('fizz', 'buzz')]))

kehrt zurück OrderedDict([('foo', 'bar'), ('fizz', 'buzz')])

Abstrakte Basisklassen

In der Tat können wir es noch besser machen. collections stellt abstrakte Basisklassen bereit, die minimale Protokolle für verschiedene Typen erzwingen. In unserem Fall, wenn wir nur das erwarten MappingProtokoll können wir Folgendes tun, und unser Code wird noch flexibler:

from collections import Mapping

def foo(a_dict):
    if not isinstance(a_dict, Mapping):
        raise ValueError('argument must be a dict')
    return a_dict

Antwort auf Kommentar:

Es sollte beachtet werden, dass der Typ verwendet werden kann, um mit mehreren Klassen zu überprüfen type(obj) in (A, B, C)

Ja, Sie können auf Gleichheit von Typen testen, aber anstelle des oben genannten verwenden Sie die mehreren Basen für den Steuerungsfluss, es sei denn, Sie erlauben ausdrücklich nur diese Typen:

isinstance(obj, (A, B, C))

Der Unterschied ist wiederum der isinstance unterstützt Unterklassen, die den Eltern ersetzen können, ohne das Programm anderweitig zu unterbrechen, eine Eigenschaft, die als Liskov-Substitution bekannt ist.

Noch besser, invertieren Sie Ihre Abhängigkeiten und prüfen Sie nicht auf bestimmte Typen überhaupt.

Fazit

Da wir die Substitution von Unterklassen unterstützen wollen, wollen wir in den meisten Fällen die Typüberprüfung mit vermeiden type und bevorzuge die Typprüfung mit isinstance - es sei denn, Sie müssen die genaue Klasse einer Instanz wirklich kennen.


60
2018-02-06 04:13



Letzteres ist bevorzugt, weil es Subklassen richtig behandelt. In der Tat kann Ihr Beispiel noch einfacher geschrieben werden, weil isinstance()Der zweite Parameter kann ein Tupel sein:

if isinstance(b, (str, unicode)):
    do_something_else()

oder unter Verwendung der basestring abstrakte Klasse:

if isinstance(b, basestring):
    do_something_else()

55
2017-10-11 03:54



Laut Python-Dokumentation gibt es hier eine Aussage:

8.15. Typen - Namen für integrierte Typen

Beginnend in Python 2.2, eingebaut   Fabrikfunktionen wie int() und    str() sind auch Namen für die   entsprechende Typen.

Damit isinstance() sollte vorgezogen werden type().


11
2017-10-11 03:59



Für die wirklichen Unterschiede können wir es finden code, aber ich kann nicht finden, die Umsetzung des Standardverhaltens der isinstance().

Jedoch können wir das ähnliche bekommen abc .__ instanzcheck__ gemäß __instancecheck__.

Von oben abc.__instancecheck__, nach dem Test unten:

# file tree
# /test/__init__.py
# /test/aaa/__init__.py
# /test/aaa/aa.py
class b():
pass

# /test/aaa/a.py
import sys
sys.path.append('/test')

from aaa.aa import b
from aa import b as c

d = b()

print(b, c, d.__class__)
for i in [b, c, object]:
    print(i, '__subclasses__',  i.__subclasses__())
    print(i, '__mro__', i.__mro__)
    print(i, '__subclasshook__', i.__subclasshook__(d.__class__))
    print(i, '__subclasshook__', i.__subclasshook__(type(d)))
print(isinstance(d, b))
print(isinstance(d, c))

<class 'aaa.aa.b'> <class 'aa.b'> <class 'aaa.aa.b'>
<class 'aaa.aa.b'> __subclasses__ []
<class 'aaa.aa.b'> __mro__ (<class 'aaa.aa.b'>, <class 'object'>)
<class 'aaa.aa.b'> __subclasshook__ NotImplemented
<class 'aaa.aa.b'> __subclasshook__ NotImplemented
<class 'aa.b'> __subclasses__ []
<class 'aa.b'> __mro__ (<class 'aa.b'>, <class 'object'>)
<class 'aa.b'> __subclasshook__ NotImplemented
<class 'aa.b'> __subclasshook__ NotImplemented
<class 'object'> __subclasses__ [..., <class 'aaa.aa.b'>, <class 'aa.b'>]
<class 'object'> __mro__ (<class 'object'>,)
<class 'object'> __subclasshook__ NotImplemented
<class 'object'> __subclasshook__ NotImplemented
True
False

Ich bekomme diese Schlussfolgerung, Zum type:

# according to `abc.__instancecheck__`, they are maybe different! I have not found negative one 
type(INSTANCE) ~= INSTANCE.__class__
type(CLASS) ~= CLASS.__class__

Zum isinstance:

# guess from `abc.__instancecheck__`
return any(c in cls.__mro__ or c in cls.__subclasses__ or cls.__subclasshook__(c) for c in {INSTANCE.__class__, type(INSTANCE)})

BTW: besser nicht zu mischen relative and absolutely import, benutzen absolutely import von project_dir (hinzugefügt von sys.path)


0
2018-05-10 08:00