Kontraintuitives Verhalten von int () in Python

83

In den Dokumenten wird klar angegeben, dass int (number) eine Konvertierung des Bodentyps ist:

int(1.23)
1

und int (string) gibt genau dann ein int zurück, wenn der String ein ganzzahliges Literal ist.

int('1.23')
ValueError

int('1')
1

Gibt es dafür einen besonderen Grund? Ich finde es nicht intuitiv, dass die Funktion in einem Fall Stockwerke hat, im anderen nicht.

StefanS
quelle

Antworten:

123

Es gibt keinen besonderen Grund. Python wendet einfach sein allgemeines Prinzip an, keine impliziten Konvertierungen durchzuführen, die bekannte Ursachen für Probleme sind, insbesondere für Neulinge, in Sprachen wie Perl und Javascript.

int(some_string)ist eine explizite Anforderung zum Konvertieren einer Zeichenfolge in das Ganzzahlformat. Die Regeln für diese Konvertierung legen fest, dass die Zeichenfolge eine gültige Ganzzahl-Literaldarstellung enthalten muss. int(float)ist eine explizite Anforderung zum Konvertieren eines Gleitkommas in eine Ganzzahl; Die Regeln für diese Konvertierung legen fest, dass der Bruchteil des Floats abgeschnitten wird.

Um int("3.1459")zurückkehren zu können 3, müsste der Interpreter den String implizit in einen Float konvertieren. Da Python keine impliziten Konvertierungen unterstützt, wird stattdessen eine Ausnahme ausgelöst.

holdenweb
quelle
type(3)kehrt zurück <type int>. Python beschwert sich jedoch nicht float("3"). Konvertiert Python den String nicht implizit in einen Int und dann in einen Float?
Franksands
Nein. "3" ist ein gültiger Gleitkommawert, obwohl er als Programmliteral als Ganzzahl interpretiert wird. Es ist keine Ganzzahlkonvertierung erforderlich.
Holdenweb
75

Dies ist mit ziemlicher Sicherheit ein Fall, in dem drei der Prinzipien aus dem Zen von Python angewendet werden :

Explizit ist besser implizit.

[...] Praktikabilität schlägt Reinheit

Fehler sollten niemals stillschweigend vergehen

In einigen Prozent der int('1.23')Fälle ruft jemand die falsche Konvertierung für seinen Anwendungsfall auf und möchte so etwas wie floatoder decimal.Decimalstattdessen. In diesen Fällen ist es eindeutig besser, einen sofortigen Fehler zu erhalten, den sie beheben können, als stillschweigend den falschen Wert anzugeben.

Im Fall , dass Sie es zu truncate möchten , dass in einen int, es trivial ist es explizit tun, indem sie durch den floatersten und dann einer der Aufruf int, round, trunc, flooroder ceilnach Bedarf. Dies macht auch Ihren Code selbsterklärend, gegen eine spätere Änderung Bewachung „Korrektur“ eine hypothetische leise beschneidenden intAnruf an , floatindem es klar , dass der gerundete Wert ist , was Sie wollen.

lvc
quelle
Ich denke, diese Prinzipien wurden lange vor der Formulierung des Zen übernommen, aber so oder so scheinen die beiden in Harmonie zu sein.
Holdenweb
17

Manchmal kann ein Gedankenexperiment nützlich sein.

  • Verhalten A: int('1.23')schlägt mit einem Fehler fehl. Dies ist das vorhandene Verhalten.
  • Verhalten B: int('1.23')erzeugt 1fehlerfrei. Das schlagen Sie vor.

Mit Verhalten A ist es einfach und trivial, die Wirkung von Verhalten B zu erzielen: Verwenden Sie int(float('1.23'))stattdessen.

Andererseits ist es bei Verhalten B wesentlich komplizierter, die Wirkung von Verhalten A zu erhalten:

def parse_pure_int(s):
    if "." in s:
        raise ValueError("invalid literal for integer with base 10: " + s)
    return int(s)

(Und selbst mit dem obigen Code habe ich nicht das volle Vertrauen, dass es keinen Eckfall gibt, den es falsch behandelt.)

Verhalten A ist daher ausdrucksvoller als Verhalten B.

Eine andere zu berücksichtigende Sache: '1.23'ist eine Zeichenfolgendarstellung eines Gleitkommawertes. Umwandlung '1.23'in eine Ganzzahl umfasst konzeptionell zwei Konvertierungen (string schweben zu integer), sondern int(1.23)und int('1')jeweils beinhalten nur eine Umwandlung.


Bearbeiten:

In der Tat gibt es Eckfälle, die der obige Code nicht behandeln würde: 1e-2und 1E-2beide sind auch Gleitkommawerte.

Jamesdlin
quelle
Zur Verdeutlichung: Ich würde Verhalten B nicht vorschlagen, weil das nur gefährlich ist, wie Sie und andere sagten. Ich bin mir nicht sicher, ob es eine bessere Lösung als die derzeitige gibt. Eine Möglichkeit wäre, den Funktionen unterschiedliche Namen zu geben, aber das ist nur mehr zu tippen. Die offensichtliche Lösung, dass int (1.23) fehlschlägt und nur int (Gleitkomma ohne Dezimalstellen) eine Ganzzahl zurückgibt, macht in einer dynamisch typisierten Sprache keinen Sinn.
StefanS
1
Eckfall könnte sein int('123E-2')oder int('1L').
Jared Goguen
11

In einfachen Worten - sie sind nicht die gleiche Funktion.

  • int (dezimal) verhält sich wie 'Boden, dh klopft den Dezimalteil ab und kehrt als int zurück'
  • int (string) verhält sich wie 'dieser Text beschreibt eine Ganzzahl, konvertiert sie und gibt als int zurück'.

Es handelt sich um zwei verschiedene Funktionen mit demselben Namen , die eine Ganzzahl zurückgeben, es handelt sich jedoch um verschiedene Funktionen.

'int' ist kurz und leicht zu merken und seine Bedeutung für jeden Typ ist für die meisten Programmierer intuitiv, weshalb sie es gewählt haben.

Es gibt keine Implikation, dass sie dieselbe oder kombinierte Funktionalität bereitstellen. Sie haben einfach denselben Namen und geben denselben Typ zurück. Sie könnten genauso gut als "floorDecimalAsInt" und "convertStringToInt" bezeichnet werden, aber sie entschieden sich für "int", weil es leicht zu merken ist, (99%) intuitiv und Verwirrung selten auftreten würde.

Das Parsen von Text als Ganzzahl für Text, der einen Dezimalpunkt wie "4.5" enthält, würde in den meisten Computersprachen einen Fehler auslösen und von den meisten Programmierern einen Fehler auslösen, da der Textwert keine Ganzzahl darstellt und impliziert Sie liefern fehlerhafte Daten


quelle
2
Warum haben dann zwei "verschiedene Funktionen" den gleichen Namen? Klingt nach einer Verletzung von Zen-Unsinn.
Hobbs
weil der Name für 2 verschiedene Funktionen Sinn macht und prägnant ist. Int-ify eine Dezimalstelle (Etage), konvertieren Sie eine Zeichenfolge in eine int (Konvertierung)
Technisch kann es hilfreich sein, sich daran zu erinnern, dass intes sich um einen Typ handelt (und noch dazu um einen eingebauten). Sein Schöpfer ( __new__) verwendet eine Reihe möglicher Argumenttypen. Das Verhalten für jeden Typ ist genau definiert.
Holdenweb
Diese Antwort ist wie angegeben einfach falsch. intist in der Tat keine Funktion, sondern ein Typ, dessen __new__und __init__Methoden ein String- oder Float-Argument verwenden und mit jedem angemessen umgehen. Es wäre genauer zu sagen, dass der Typ die beiden Argumenttypen unterschiedlich verarbeitet, aber es gibt nur einen int.
Holdenweb