variable - python plot beschriftung




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

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 eine Nebenbemerkung is es wahrscheinlich angemessener 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 oder Records konstruiert werden. 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 üblicher 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, das eine Unterklasse von dict ist, weiterzuleiten (so wie wir es können sollten, wenn wir erwarten, dass unser Code dem Prinzip der Liskov-Substitution folgt, können diese Subtypen durch Typen ersetzt werden), bricht unser Code !:

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 , können wir 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')])

Abstrakte Basisklassen

In der Tat können wir es noch besser machen. collections bietet abstrakte Basisklassen, die minimale Protokolle für verschiedene Typen erzwingen. In unserem Fall, wenn wir nur das Mapping Protokoll 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

Fazit

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

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()

Verwenden von isinstance() :

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

Hier ist warum 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 ist auch für Unterklassen wahr.

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


Letzteres ist bevorzugt, weil es Subklassen richtig behandelt. In der Tat 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 basestring abstrakten Klasse:

if isinstance(b, basestring):
    do_something_else()

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 Gleichheit des type nicht erfolgt (sie erfordert die Identität von Typen und lehnt Instanzen von Subtypen ab, AKA-Unterklassen).

Normalerweise möchten Sie in Python natürlich, dass Ihr Code die Vererbung unterstützt (da die Vererbung so praktisch ist, wäre es schlecht, den Code zu stoppen, indem Sie Ihren benutzen!), isinstance ist es weniger schlimm, als die Identität des type s zu überprüfen unterstützt nahtlos die Vererbung.

Es ist nicht so, dass isinstance gut ist, isinstance - es ist weniger schlimm, als Gleichheit von Typen zu prüfen. Die normale, pythonsche, bevorzugte Lösung ist fast immer "duck typing": Versuchen Sie, das Argument so zu verwenden, als ob es von einem bestimmten gewünschten Typ wäre, in einer try / except Anweisung alle Ausnahmen zu erfassen, die entstehen könnten, wenn das Argument nicht tatsächlich wäre von diesem Typ (oder von einem anderen Typ, der es nett nachahmt ;-), und in der except Klausel, versuche etwas anderes (mit dem Argument "als ob" es von einem anderen Typ wäre).

basestring ist jedoch ein ganz besonderer Fall - ein eingebauter Typ, der nur dafür existiert, dass Sie isinstance ( isinstance und unicode Unterklassen- basestring ) verwenden können. Strings sind Sequenzen (Sie könnten sie überstreichen, indexieren, in Scheiben schneiden, ...), aber im Allgemeinen wollen Sie sie als "skalare" Typen behandeln - es ist etwas unpassend (aber ein ziemlich häufiger Anwendungsfall), um alle Arten von Strings (und vielleicht andere skalare Typen, dh solche, die man nicht basestring kann) in einer Richtung, alle Container (Listen, Mengen, Dicts, ...) auf andere Weise, und basestring plus isinstance hilft dir 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)

Man könnte sagen, dass basestring eine abstrakte basestring ("ABC") ist - sie 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, da PEP 3119 , das eine Verallgemeinerung von Python einführt, akzeptiert wurde und ab Python 2.6 und 3.0 implementiert wurde.

Die PEP macht deutlich, dass ABC zwar oft die Art der Ente ersetzen kann, aber dafür gibt es normalerweise keinen großen Druck (siehe here ). ABCs, wie sie in neueren Python-Versionen implementiert wurden, bieten jedoch zusätzliche isinstance : isinstance (und issubclass ) kann nun mehr bedeuten als nur "[eine Instanz von] einer abgeleiteten Klasse" (insbesondere kann jede Klasse mit einem ABC "registriert" werden) es wird als Unterklasse und seine Instanzen als Instanzen des ABC angezeigt); und ABCs können den tatsächlichen Unterklassen auf sehr natürliche Weise zusätzlichen Komfort bieten, indem Sie Vorlagenmuster-Designmuster-Anwendungen verwenden (siehe here und here [[Teil II]] für mehr zu TM DP, allgemein 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 here ; für ihre Version 3.1, sehr ähnlich, siehe here . In beiden Versionen bieten Standard-Bibliotheksmodul- collections (das ist die 3.1-Version - für die sehr ähnliche 2.6-Version, siehe here ) mehrere nützliche ABCs.

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





types