[Python] Ist das null, aber nicht leere Objekt wahr oder falsch?

Ich war neugierig auf Truthy and False in Python, daher ist dies ein Artikel, in dem ich während der Untersuchung über Truthy and False sprechen kann. Wenn Sie nur an dem Titel interessiert sind, überspringen Sie bitte [hier](# python% E3% 81% AB% E3% 81% 8A% E3% 81% 91% E3% 82% 8Btruthy - falsy).

Chatten Sie über Truthy und Farsy

Was ist Wahrheit / Falschheit?

Die Begriffe Truthy und Farsy erscheinen in JavaScript, aber das Konzept selbst erscheint in vielen Sprachen, daher werden wir es hier verallgemeinern.

Wahrheit ist ein Akronym, das einen Wert angibt, der bei logischer Auswertung als wahr behandelt wird. Falsy ist ein Akronym, das einen Wert angibt, der bei der logischen Auswertung als falsch behandelt wird.

Abhängig von der Person können nur die Werte, die bei der logischen Auswertung als wahr / falsch behandelt werden, mit Ausnahme des "repräsentativen Werts", als Wahrheit / Falsch bezeichnet werden, hier jedoch alle gemäß der Definition in JavaScript Truthy. Und Farsy.

Haltung zu Wahrheit / Falschheit

Ich persönlich denke, dass die Haltung zur Sprache Wahrheit / Falschheit in die folgenden drei Muster unterteilt werden kann. In diesem Artikel wird Muster A als "reine Authentizität" bezeichnet, und Muster B und Muster C werden als "erweiterte Authentizität" bezeichnet.

Muster A: Nur eine Wahrheit und eine Fallsy

Diese Art von Sprache hat einen Typ, der Wahrheitswerten gewidmet ist, und begrenzt die möglichen Werte auf zwei Typen. Alternativ kann es ein "nur wahres Objekt" oder ein "nur gefälschtes Objekt" als Singleton-Klasse haben. Der Versuch, andere Werte als diese logisch auszuwerten, führt zu einem Fehler.

Java ist ein typisches Beispiel. Im Fall von Java gibt es jedoch primitive Wahrheitswerte und umhüllte Wahrheitswerte, so dass gesagt werden kann, dass dies nicht genau der Fall ist.

Muster B: Alle Werte sind entweder Truthy oder Fallsy

Jeder Wert kann in dieser Art von Sprache logisch ausgewertet werden. In einigen Fällen gibt es möglicherweise sogar keinen Typ, der den Wahrheitswerten gewidmet ist. Das Vorhandensein eines "repräsentativen Werts" ist jedoch als Rückgabewert für Vergleichsoperationen und logische Ablehnungen erforderlich.

Ein typisches Beispiel wäre JavaScript. ~~ Du sagst also, dass Java und JavaScript unterschiedlich sind ~~

Muster C: Es gibt mehrere Wahrheiten / Falschheiten, aber einige Werte sind keine

Persönlich habe ich auch keinen guten Eindruck davon. Wenn die "Ausnahme" jedoch "null" ist, kann es sich in gewissem Sinne um ein gültiges Design handeln (später beschrieben).

C-Sprache ist dies beabsichtigt. Dies liegt daran, dass die C-Sprache ganzzahlige Typen für logische Operationen usw. verwendet und alles außer "0" als wahr betrachtet. Im Gegenteil, nur ein ganzzahliger Typ kann logisch ausgewertet werden.

Vor- und Nachteile einer erweiterten Authentizitätssprache

Erweiterte Authentizitätssprachen bieten mehrere Vorteile.

Zum einen kann der bedingte Ausdruck vereinfacht werden.

Um beispielsweise eine Nullteilung zu vermeiden, können Sie Code wie folgt schreiben:

if (b !== 0) {
    console.log(a / b)
} else {
    console.log('Es wird nicht bei Null brechen.')
}

"0" ist jedoch Farsy, also

if (b) {
    console.log(a / b)
} else {
    console.log('Es wird nicht bei Null brechen.')
}

Sie können sehen, dass das genug ist.

Das andere ist, dass einige logische Operationen verallgemeinert werden können.

In der klassischen Logik ist die Summe der Logik beispielsweise eine Operation, die "falsch zurückgibt, wenn sowohl die linke als auch die rechte Seite falsch sind, und ansonsten true zurückgibt", dies jedoch "wenn die linke Seite wahr ist, die linke Seite falsch ist und die linke Seite falsch ist". Sie können die vorhandene Wahrheitstabelle erweitern, ohne sie zu ändern, indem Sie "wenn die rechte Seite zurückgegeben wird" interpretieren.

Sie können dies verwenden, um einige Logik zu vereinfachen. Die Logik zur Eingabe der Standardzeichenfolge, wenn keine Eingabe (leere Zeichenfolge) vorhanden ist, lautet beispielsweise wie folgt, wenn versucht wird, sie direkt zu schreiben.

if (!name) {
    name = 'Die Gäste';
}

/*Oder verwenden Sie den ternären Operator*/
name = name ? 'Die Gäste' : name

Wenn Sie jedoch die Tatsache ausnutzen, dass die leere Zeichenfolge Farsy ist, können Sie Folgendes unter Verwendung der verallgemeinerten logischen Summe schreiben.

name = name || 'Die Gäste'

Dies liegt daran, dass, wenn name Truthy ist (dh wenn es sich nicht um eine leere Zeichenfolge handelt), die linke Seite unverändert zurückgegeben wird und wenn sie False ist (dh wenn es sich um eine leere Zeichenfolge handelt), die rechte Seite zurückgegeben wird.

Diese haben jedoch auch Nachteile.

Im ersteren Fall gibt es beispielsweise keine Garantie dafür, dass die in "b" enthaltene Zahl tatsächlich ein numerischer Wert ist. Wenn ein nicht numerischer Truthy-Wert eingegeben wird, wird der bedingte Ausdruck umgangen und ein Laufzeitfehler generiert.

Diese Fallstricke treten in dynamisch typisierten Sprachen auf, aber selbst in statisch typisierten Sprachen gibt es viele Sprachen, in denen nur Null ausnahmsweise zugewiesen werden kann (das sogenannte Null ist nicht sicher). Wenn Null auch logisch ausgewertet werden kann, tritt unerwartetes Verhalten auf. Es kann vorkommen.

Ich denke eher, dass Leute, die dynamisch typisierte Sprachen verwenden, bereit sind, die Typintegrität selbst zu handhaben (und bereits daran gewöhnt sind, Typfehler so oft auszuführen), daher bevorzuge ich eine statisch typisierte Sprache, die der Kompilierung überlassen bleibt. Es kann genauso gefährlich sein wie die Person, die es benutzt.

Diese Spezifikation wurde auch der einzige Elternteil, der die berüchtigte Yoda-Notation erstellte. Das andere übergeordnete Element ist die "Angabe, dass der Zuweisungsoperator einen Wert zurückgibt". Ergebnis,

if (a == 5) {
    /* ... */
}

Ich wollte schreiben

if (a = 5) {
    /* ... */
}

Auch wenn es ist

  1. Der Ausdruck "a = 5" gibt den Wert "5" zurück (unabhängig vom Wert von "a").
  2. 5 ist Wahrheit
  3. Wenn Blockinhalte ausgeführt werden!

Und eine Brutstätte von Käfern geschaffen. Um es zu umgehen

if (5 == a) {
    /* ... */
}

Deshalb wurde ein seltsamer Brauch des Schreibens geboren.

Dies bleibt in den Legacy-Codierungsregeln Ihres Unternehmens erhalten, und selbst in Situationen, in denen Sie es nicht benötigen (dh in einer Sprache, die keinen Wert für eine reine Authentizitätssprache oder einen Zuweisungsoperator zurückgibt, oder in einem Compiler, der Sie warnt, dass Sie eine Zuweisung in einem if-bedingten Ausdruck ausführen. Es entsteht eine Situation, in der Sie gezwungen werden (auch wenn Sie es verwenden). Die Bräuche, die aus der Notwendigkeit heraus geboren wurden, vergessen schließlich ihre Bedeutung und werden zu bedeutungslosen "Manieren", die nur Formen sind ... wie es in der realen Welt oft der Fall ist.

Ruhige Gesprächspause. Auf diese Weise hat die Haltung von Truthy / Falsy Vor- und Nachteile (Effizienz) und Nachteile (Fallstricke). Wenn Sie also zum ersten Mal eine Sprache verwenden, mit der Sie in Ihrer Arbeit in Kontakt kommen, handelt es sich entweder um eine reine Authentizitätssprache oder um eine erweiterte Authentizitätssprache. Es ist wichtig, sich dessen bewusst zu sein. Später, wenn es um "Ich wusste das nicht ..." geht, ist es schmerzhaft (Erfahrungsgeschichte).

Wahrheit / Falschheit in Python

Was entscheidet Farsy

Nun, endlich der Titel. Von hier aus werden wir eine Weile über Python2 sprechen. Im Fall von 3 werde ich es am Ende berühren.

Python gehört zur erweiterten Authentizitätssprache. Alle Nicht-Falsch-Werte sind Wahrheit. Unter den allgemeinen Werten sind die folgenden Werte False.

Übrigens ist der Bool-Typ in Python tatsächlich eine Unterklasse des int-Typs, und "True" und "False" entsprechen tatsächlich "1" bzw. "0". Basierend auf,

Es scheint, dass es verallgemeinert werden kann, oder?

Neben dem speziellen Wert "None" definiert Python spezielle Methoden, die "Null oder nicht" und "Sammlungslänge" angeben. Mit anderen Worten, indem Sie diese implementieren, können Sie steuern, ob Ihr eigenes Objekt wahr oder falsch ist.

Lass es uns gleich versuchen. Der erste ist, wenn keiner implementiert ist.

class MyObjA:
    def __init__(self):
        pass


my_obj = MyObjA()
print('Truthy' if my_obj else 'Falsy')
Truthy

Sie können sehen, dass es als wahr behandelt wird, weil es kein Element gibt, das falsch ist.

Als nächstes sagen wir "Null in numerischen Begriffen". Implementieren Sie die Methode __nonzero__.

class MyObjB:
    def __init__(self, nz):
        self.nz = nz

    def __nonzero__(self):
        return self.nz


my_obj = MyObjB(True)
print('Truthy' if my_obj else 'Falsy')
my_obj = MyObjB(False)
print('Truthy' if my_obj else 'Falsy')
Truthy
Falsy

Wie Sie sehen können, können Sie sehen, dass das Objekt selbst als False behandelt wird, wenn __nonzero__`` False zurückgibt (um genau zu sein, gibt es einen Wert vom Typ int zurück).

Versuchen wir als nächstes "Leere Sammlung". Implementieren Sie die Methode len.

class MyObjC:
    def __init__(self, ln):
        self.ln = ln

    def __len__(self):
        return self.ln


my_obj = MyObjC(10)
print('Truthy' if my_obj else 'Falsy')
my_obj = MyObjC(0)
print('Truthy' if my_obj else 'Falsy')
Truthy
Falsy

Wie Sie sehen können, können Sie sehen, dass das Objekt selbst als Farsy behandelt wird, wenn __len__`` 0 zurückgibt (um genau zu sein, gibt es einen Wert vom Typ int zurück).

Im Konfliktfall

Aber was ist, wenn diese Bedingungen nicht miteinander übereinstimmen? Das bedeutet der Titel.

Wenn ich es tatsächlich versuche ...

class MyObjD:
    def __init__(self, nz, ln):
        self.nz = nz
        self.ln = ln

    def __nonzero__(self):
        return self.nz

    def __len__(self):
        return self.ln


my_obj = MyObjD(True, 10)
print('Truthy' if my_obj else "Falsy")

my_obj = MyObjD(False, 0)
print('Truthy' if my_obj else "Falsy")

my_obj = MyObjD(True, 0)
print('Truthy' if my_obj else "Falsy")

my_obj = MyObjD(False, 10)
print('Truthy' if my_obj else "Falsy")
Truthy
Falsy
Truthy
Falsy

Wie Sie sehen können, wird "Null oder nicht" unabhängig von der Länge priorisiert. Der Grund dafür kann aus der Fehlermeldung abgelesen werden, wenn __len__ einen ungültigen Wert zurückgibt.

class MyObjC:
    def __init__(self, ln):
        self.ln = ln

    def __len__(self):
        return self.ln


my_obj = MyObjC(0.0)
print('Truthy' if my_obj else 'Falsy')
Traceback (most recent call last):
  File "Main.py", line 10, in <module>
    print('Truthy' if my_obj else 'Falsy')
TypeError: __nonzero__ should return an int

" __Nonzero__ sollte den int-Typ zurückgeben ", war wütend. Mit anderen Worten, es kann gefolgert werden, dass die Standardimplementierung "nonzero" "len" aufruft. Lass uns nachsehen. __nonzero__ wird von der eingebauten Funktion bool aufgerufen.

class MyObjE:
    def __init__(self, ln):
        self.ln = ln

    def __len__(self):
        print('__len__ called!')
        return self.ln


my_obj = MyObjE(0)
b = bool(my_obj)
print(b)
print(type(b))
__len__ called!
False
<type 'bool'>

Sie raten! Die Standardimplementierung "nonzero", die "len" aufruft, bedeutet, dass eine Prüfung auf "len" in MyObjC tatsächlich "nonzero" heißt ... das heißt, beides Selbst wenn Sie es implementieren, sehen Sie nur "nonzero". Daher wird "ein Objekt, das Null ist, aber nicht leer", zu "Falsch".

In Python3

Übrigens wurde die Diskrepanz zwischen dem speziellen Methodennamen "nonzero" und dem integrierten Funktionsnamen "bool" in Python3 korrigiert und wurde zu "bool". Daher wird es so.

class MyObjD:
    def __init__(self, nz, ln):
        self.nz = nz
        self.ln = ln

    def __nonzero__(self):
        return self.nz

    def __len__(self):
        return self.ln


my_obj = MyObjD(True, 10)
print('Truthy' if my_obj else "Falsy")

my_obj = MyObjD(False, 0)
print('Truthy' if my_obj else "Falsy")

my_obj = MyObjD(True, 0)
print('Truthy' if my_obj else "Falsy")

my_obj = MyObjD(False, 10)
print('Truthy' if my_obj else "Falsy")
Truthy
Falsy
Falsy
Truthy
class MyObjF:
    def __init__(self, nz, ln):
        self.nz = nz
        self.ln = ln

    def __bool__(self):
        return self.nz

    def __len__(self):
        return self.ln


my_obj = MyObjF(True, 10)
print('Truthy' if my_obj else "Falsy")

my_obj = MyObjF(False, 0)
print('Truthy' if my_obj else "Falsy")

my_obj = MyObjF(True, 0)
print('Truthy' if my_obj else "Falsy")

my_obj = MyObjF(False, 10)
print('Truthy' if my_obj else "Falsy")
Truthy
Falsy
Truthy
Falsy

Und wie der Name schon sagt, erlaubt __bool__ nur Rückgabewerte vom Typ bool.

class MyObjF:
    def __init__(self, nz, ln):
        self.nz = nz
        self.ln = ln

    def __bool__(self):
        return self.nz

    def __len__(self):
        return self.ln


my_obj = MyObjF(1, 10)
print('Truthy' if my_obj else "Falsy")
Traceback (most recent call last):
  File "Main.py", line 14, in <module>
    print('Truthy' if my_obj else "Falsy")
TypeError: __bool__ should return bool, returned int

__bool__ erlaubt jetzt nur noch den Typ bool, aber was ist mit der Spezifikation, die die Standardimplementierung __len__ aufruft?

class MyObjE:
    def __init__(self, ln):
        self.ln = ln

    def __len__(self):
        print('__len__ called!')
        return self.ln


my_obj = MyObjE(0)
b = bool(my_obj)
print(b)
print(type(b))
__len__ called!
False
<class 'bool'>

Es scheint keine Änderung in der Spezifikation zu geben, dass die Standardimplementierung "bool" "len" aufruft. Es scheint kein Problem zu geben, da der Rückgabewert von __len__ von Anfang an in den Bool-Typ umgewandelt wird.

Recommended Posts

[Python] Ist das null, aber nicht leere Objekt wahr oder falsch?
Python ist schmerzhaft. Aber benutze
[Python] Was wird zuerst ausgeführt, Klassenvariable oder __init__?
[Python] Was ist @? (Über Dekorateure)
Was ist besser, PyPy oder Python?
Wofür ist der Python-Unterstrich (_)?
Python gibt keine Fehler oder Ausgaben aus, nur weil der Einzug falsch ausgerichtet ist
[Python] Bestimmen Sie, ob sich ein Koordinatenpunkt innerhalb oder außerhalb des Polygons befindet
Nehmen Sie die logische Summe von List in Python (Zip-Funktion)
Wo ist der Python-Instanziierungsprozess geschrieben?
[Python3] Schreiben Sie das Codeobjekt der Funktion neu
Was ist "Mahjong" in der Python-Bibliothek? ??
[Python] [Meta] Ist der Python-Typ ein Typ?
Was ist schneller, Python Shuffle oder Sample?