Richtige Methode, um den Variablentyp in Python zu bestätigen

72

Bei der Verwendung einer Funktion möchte ich sicherstellen, dass der Typ der Variablen wie erwartet ist. Wie mache ich es richtig?

Hier ist ein Beispiel für eine gefälschte Funktion, die versucht, genau dies zu tun, bevor sie mit ihrer Rolle fortfährt:

def my_print(begin, text, end):
    """Print 'text' in UPPER between 'begin' and 'end' in lower

    """
    for i in (begin, text, end):
        assert isinstance(i, str), "Input variables should be strings"
    out = begin.lower() + text.upper() + end.lower()
    print out

def test():
    """Put your test cases here!

    """
    assert my_print("asdf", "fssfpoie", "fsodf")
    assert not my_print("fasdf", 33, "adfas")
    print "All tests passed"

test()

Ist die Behauptung der richtige Ansatz? Soll ich stattdessen try / exception verwenden?

Außerdem scheint meine Testreihe nicht richtig zu funktionieren: S.

Danke Pythononeer

Morlock
quelle
10
Ich denke, Sie haben die größte Schwäche von Python entdeckt: Keine formale Möglichkeit, Typen anzugeben, wenn Sie möchten. Der geringere Aspekt dieses Problems ist, dass Sie die Typen manuell überprüfen müssen (wie in Ihrer Frage). Das größere Problem ist, dass Ihre Werkzeuge Ihnen nicht helfen können. Python wäre die perfekte Sprache, wenn es nur die dynamische Typisierung unterstützen würde, aber auch die Option hätte, Typen anzugeben, wenn keine Dynamik benötigt würde.
Tom
2
Ein Hinweis aus dem Jahr 2018: Selbst mit dem Aufkommen des typingModuls in Python 3.6, Mypy und anderen Tools gilt die obige Aussage so: Es wäre großartig, eine statisch typisierte Version von Python zu haben.
Evgeny

Antworten:

65

Das isinstanceEingebaute ist der bevorzugte Weg, wenn Sie es wirklich müssen, aber noch besser ist es, sich an Pythons Motto zu erinnern: "Es ist einfacher, um Vergebung zu bitten als um Erlaubnis"! -) (Es war eigentlich Grace Murray Hoppers Lieblingsmotto ;-). Dh:

def my_print(text, begin, end):
    "Print 'text' in UPPER between 'begin' and 'end' in lower"
    try:
      print begin.lower() + text.upper() + end.lower()
    except (AttributeError, TypeError):
      raise AssertionError('Input variables should be strings')

Übrigens, damit funktioniert die Funktion auch bei Unicode-Strings einwandfrei - ohne zusätzlichen Aufwand! -)

Alex Martelli
quelle
9
assertDies ist manchmal nützlich für Überprüfungen der Integrität, die Sie nur in der Entwicklungsphase benötigen (nicht in optimiertem Produktionscode, wo er wegoptimiert wird). Überprüfen Sie daher keine gültigen Eingaben (die Sie anderweitig ausführen müssen), sondern die Überprüfung der Integrität selbst Logik (Schleifenvarianten, Klasseninvarianten usw.) - natürlich nicht typbezogen. Das Testframework von Drittanbietern py.test verwendet assertdie Art und Weise, wie der Standard unittestMethoden wie assertEqual& c verwendet.
Alex Martelli
5
Die Funktion my_print sollte sich nicht wirklich darum kümmern, ob die Argumente vom Typ str oder unicode sind. es kümmert sich nur , dass die Argumente haben lower(), upper()und __add__()Methoden. Dies wird als Ententypisierung bezeichnet (es sieht aus wie eine Ente und spricht wie eine Ente (hat die gleichen Methoden), auch wenn es wirklich überhaupt keine Ente ist), und es ist die bevorzugte Methode für Python. Wenn Sie eine Ausnahme auslösen möchten, weil die übergebenen Argumente vom falschen Typ waren, sollten Sie eine auslösen TypeError.
3
@ 007brendan, en.wikipedia.org/wiki/Duck_typing unter History schreibt mir die früheste nachvollziehbare Verwendung des Begriffs "Ententyping" (vor 10 Jahren, als ich noch ein Anfänger in Python war) zu, wenn Sie meinen Beitrag lesen Ich verwende den Ausdruck in diesem Beitrag nicht wirklich (ich erfinde viele Entenanalogien, verwende aber meistens traditionellere Begriffe wie "Typumschaltung") - jedenfalls denke ich, dass ich mit dem Konzept einigermaßen vertraut bin ;-). Ich bin damit einverstanden, dass ein Typfehler gut übereinstimmt (der Versuch / Ausnahme kann Attributfehler, immer ein hybrides Ding, in Typfehler verwandeln).
Alex Martelli
1
@Alex, ich verwende Ihre Ratschläge und Lösungen in vielen meiner eigenen Programmierungen, sodass mir klar wird, dass Sie in all diesen Bereichen über einen unglaublichen Wissens- und Erfahrungsschatz verfügen (ich verwende sogar Ihre eigenen Analogien!). Mein Beitrag war eher für das OP gedacht, um einen Kontext zu liefern, warum Ihre Lösung die kanonische ist.
1
@strpeter, heutzutage gibt es tatsächlich ein neues Kind in der Stadt, das ich "Gänse-Typisierung" genannt habe (Überprüfung gegen abstrakte Basisklassen), aber es bietet keine Hilfe für Zeichenfolgen, so dass "Enten-Typisierung" an den meisten Orten immer noch Regeln gilt: - )
Alex Martelli
18

Vielleicht möchten Sie dieses Beispiel für Version 2.6 von Python ausprobieren.

def my_print(text, begin, end):
    "Print text in UPPER between 'begin' and 'end' in lower."
    for obj in (text, begin, end):
        assert isinstance(obj, str), 'Argument of wrong type!'
    print begin.lower() + text.upper() + end.lower()

Haben Sie jedoch darüber nachgedacht, die Funktion stattdessen auf natürliche Weise ausfallen zu lassen?

Noctis Skytower
quelle
Ich bin etwas rückläufig und verwende immer noch 2.6: P Vielen Dank für die Funktion 'isinstance ()'.
Morlock
10

Tun type('')ist effektiv gleichbedeutend mit strundtypes.StringType

so type('') == str == types.StringTypewird zu " True" auswerten

Beachten Sie, dass Unicode-Zeichenfolgen, die nur ASCII enthalten, fehlschlagen, wenn Typen auf diese Weise überprüft werden. Daher möchten Sie möglicherweise etwas wie assert type(s) in (str, unicode)oder tun assert isinstance(obj, basestring), wobei letzteres in den Kommentaren von 007Brendan vorgeschlagen wurde und wahrscheinlich bevorzugt wird.

isinstance() ist nützlich, wenn Sie fragen möchten, ob ein Objekt eine Instanz einer Klasse ist, z.

class MyClass: pass

print isinstance(MyClass(), MyClass) # -> True
print isinstance(MyClass, MyClass()) # -> TypeError exception

Aber für Grundtypen, zum Beispiel str, unicode, int, float, longusw. fragen type(var) == TYPEWille Arbeit OK.

Kryo
quelle
2
Sie können Folgendes tun: isinstance (obj, basestring) - str und unicode erben beide von basestring, sodass dies für beide funktioniert.
Vielen Dank für diesen Vorschlag - ich habe ihn der Antwort hinzugefügt.
Kryo