it-swarm.com.de

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

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

import types

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

Mit isinstance():

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

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

Normalerweise möchten Sie in Python, dass Ihr Code die Vererbung unterstützt (da die Vererbung so praktisch ist, wäre es schlecht, den Code nicht zu verwenden!). Daher ist isinstance weniger schlecht als die Überprüfung der Identität von types, da die Vererbung nahtlos unterstützt wird.

Es ist nicht so, dass isinstance gut ist , wohlgemerkt - es ist nur weniger schlecht als die Überprüfung der Typengleichheit. Die normale, pythonische, bevorzugte Lösung ist fast immer "duck typing": Verwenden Sie das Argument so, als ob es von einem bestimmten gewünschten Typ wäre, und tun Sie es in einem try/except - Anweisung, die alle Ausnahmen abfängt, die auftreten könnten, wenn das Argument nicht von diesem Typ ist (oder von einem anderen Typ, der es gut imitiert ;-), und in der except - Klausel etwas anderes versuchen (unter Verwendung des Arguments "als ob" es von einem anderen Typ war).

basestring ist jedoch ein ganz besonderer Fall - ein eingebauter Typ, der nur existiert , damit Sie isinstance verwenden können (sowohl str als auch unicode Unterklasse basestring). Strings sind Sequenzen (Sie könnten sie durchlaufen, indizieren, in Scheiben schneiden, ...), aber Sie möchten sie im Allgemeinen als "skalare" Typen behandeln - es ist etwas ungewöhnlich (aber ein recht häufiger Anwendungsfall), alle Arten von Strings zu behandeln Zeichenfolgen (und möglicherweise auch andere skalare Typen, dh solche, auf die Sie keine Schleife durchführen können), alle Container (Listen, Mengen, Dikte usw.) auf andere Weise und basestring plus isinstance hilft Ihnen dabei - die Gesamtstruktur dieser Redewendung sieht ungefähr so ​​aus:

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

Man könnte sagen, dass basestring eine abstrakte Basisklasse ("ABC") ist - sie bietet Unterklassen keine konkrete Funktionalität, sondern existiert hauptsächlich als "Marker" zur Verwendung mit isinstance. In Python ist das Konzept offensichtlich auf dem Vormarsch, da PEP 3119 , das eine Verallgemeinerung davon einführt, akzeptiert und ab Python 2.6 und 3.0 implementiert wurde.

Das PEP macht deutlich, dass ABCs zwar oft das Typisieren von Enten ersetzen können, dass jedoch im Allgemeinen kein großer Druck besteht (siehe hier ). ABCs, wie sie in neueren Python - Versionen implementiert wurden, bieten jedoch zusätzliche Extras: isinstance (und issubclass) können jetzt mehr bedeuten als nur "[eine Instanz] einer abgeleiteten Klasse" (in Insbesondere kann jede Klasse bei einem ABC "registriert" werden, so dass sie als Unterklasse und ihre Instanzen als Instanzen des ABC angezeigt werden. und ABCs können auch auf sehr natürliche Weise zusätzlichen Komfort für tatsächliche Unterklassen bieten, indem sie Musteranwendungen für die Vorlagenmethode verwenden (siehe hier und hier [[Teil II]] für weitere Informationen zu TM DP im Allgemeinen und speziell in Python (unabhängig von ABCs).

Informationen zur Funktionsweise der ABC-Unterstützung, wie sie in Python 2.6 angeboten wird, finden Sie unter hier ; Für ihre 3.1-Version, sehr ähnlich, siehe hier . In beiden Versionen bietet das Standardbibliotheksmodul Sammlungen (das ist die 3.1-Version - für die sehr ähnliche 2.6-Version siehe hier ) mehrere nützliche ABCs.

Für die Beantwortung dieser Frage ist das Wichtigste, was Sie über ABCs wissen sollten (jenseits einer wahrscheinlich natürlicheren Platzierung der TM DP-Funktionalität im Vergleich zur klassischen Python Alternative von Mixin-Klassen wie serDict.DictMixin ) ist, dass sie isinstance (und issubclass) viel attraktiver und durchdringender machen (in Python 2.6 und später) als früher (in 2.5 und früher) ) und machen daher die Überprüfung der Gleichheit von Schriftarten in neueren Python -Versionen noch schlimmer als früher.

1171
Alex Martelli

Hier ist ein Beispiel, in dem isinstance etwas erreicht, das type nicht kann:

class Vehicle:
    pass

class Truck(Vehicle):
    pass

in diesem Fall ist ein LKW-Objekt ein Fahrzeug. Sie erhalten jedoch 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 vergleiche ich den Typ eines Objekts in Python?

312
Peter

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

Typprüfung mit

isinstance(obj, Base)

erlaubt Instanzen von Unterklassen und mehrere mögliche Basen:

isinstance(obj, (Base1, Base2))

während die Typprüfung mit

type(obj) is Base

unterstützt nur den angegebenen Typ.


Als Randnotiz ist is wahrscheinlich angemessener als

type(obj) == Base

weil Klassen Singletons sind.

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

In Python möchten Sie in der Regel einen beliebigen Typ für Ihre Argumente zulassen, ihn wie erwartet behandeln und einen entsprechenden Fehler auslösen, wenn sich das Objekt nicht wie erwartet verhält. Dies ist als Polymorphismus, auch als Ententypisierung bekannt.

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

Wenn der obige Code funktioniert, können wir davon ausgehen, dass unser Argument eine Ente ist. Somit können wir in anderen Dingen tatsächliche Unterarten von Enten 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 Typprüfung wünschenswert ist. Vielleicht haben Sie vernünftige Dinge mit verschiedenen Objekttypen zu tun. Beispielsweise kann das Dataframe-Objekt Pandas aus Diktat- oder Datensätzen erstellt werden. In einem solchen Fall muss Ihr Code wissen, welche Art von Argument er erhält, damit er ordnungsgemäß damit umgehen kann.

Also, um die Frage zu beantworten:

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

Lassen Sie mich den Unterschied 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 einem Typ wie diesem suchen:

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

Wenn wir versuchen, ein Diktat weiterzugeben, das eine Unterklasse von dict ist (wie wir es können sollten, wenn wir erwarten, dass unser Code dem Prinzip von Liskov-Substitution folgt), werden diese Untertypen kann durch Typen ersetzt werden) 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 isinstance verwenden, können wir die 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')]))

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

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. Wenn wir in unserem Fall nur das Protokoll Mapping erwarten, 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 ist zu beachten, dass der Typ verwendet werden kann, um mit type(obj) in (A, B, C) gegen mehrere Klassen zu prüfen.

Ja, Sie können auf Gleichheit der Typen testen. Verwenden Sie jedoch anstelle der oben genannten die mehreren Basen für den Kontrollfluss, es sei denn, Sie lassen ausdrücklich nur die folgenden Typen zu:

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

Der Unterschied besteht wiederum darin, dass isinstance Unterklassen unterstützt, die durch die übergeordneten Klassen ersetzt werden können, ohne dass das Programm sonst beschädigt wird. Diese Eigenschaft wird als Liskov-Substitution bezeichnet.

Noch besser ist es jedoch, die Abhängigkeiten zu invertieren und überhaupt nicht nach bestimmten Typen zu suchen.

Fazit

Da wir das Ersetzen von Unterklassen unterstützen möchten, möchten wir in den meisten Fällen die Typprüfung mit type vermeiden und die Typprüfung mit isinstance bevorzugen - es sei denn, Sie müssen die genaue Klasse einer Instanz wirklich kennen .

87
Aaron Hall

Letzteres wird bevorzugt, da es Unterklassen ordnungsgemäß behandelt. Tatsächlich kann Ihr Beispiel noch einfacher geschrieben werden, da der zweite Parameter von isinstance() ein Tupel sein kann:

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

oder mit der abstrakten Klasse basestring:

if isinstance(b, basestring):
    do_something_else()
61
John Millikin

Laut python Dokumentation ist hier eine Aussage:

8.15. Types - Namen für integrierte Typen

Ab Python 2.2 sind integrierte Factory-Funktionen wie int() und str() auch Namen für die entsprechenden Typen.

Also sollte isinstance()type() vorgezogen werden.

13
Xinus

Ein praktischer Unterschied ist, wie sie mit booleans umgehen:

True und False sind nur Schlüsselwörter, die in Python 1 und 0 bedeuten. Somit,

isinstance(True, int)

und

isinstance(False, int)

beide geben True zurück. Beide Booleschen Werte sind eine Instanz einer Ganzzahl. type() ist jedoch schlauer:

type(True) == int

gibt False zurück.

1
Alec Alameddine

Die wirklichen Unterschiede können wir in code finden, aber ich kann das Implement des Standardverhaltens von isinstance() nicht finden.

Wir können jedoch die ähnliche erhalten abc .__ instancecheck __ 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 habe diese Schlussfolgerung für type:

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

Für 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)})

Übrigens: Verwenden Sie besser nicht relative and absolutely import, sondern absolutely import aus project_dir (hinzugefügt von sys.path).

0
Cheney