Wie verhalten sich "und" und "oder" mit nicht-booleschen Werten?


97

Ich versuche Python zu lernen und bin auf einen Code gestoßen, der nett und kurz ist, aber keinen Sinn ergibt

Der Kontext war:

def fn(*args):
    return len(args) and max(args)-min(args)

Ich verstehe, was es tut, aber warum macht Python das - dh gibt den Wert zurück und nicht True / False?

10 and 7-2

gibt 5 zurück. Ebenso führt das Ändern von und zu oder zu einer Änderung der Funktionalität. So

10 or 7 - 2

Würde 10 zurückgeben.

Ist das ein legitimer / verlässlicher Stil oder gibt es irgendwelche Fallstricke?


1
and(sowie or) ist nicht darauf beschränkt, mit booleschen Werten zu arbeiten oder diese zurückzugeben.
CS95

1
IMNSHO: Das ist eine etwas verwirrende Art, das zu schreiben. Ich kann nicht ohne weiteres sagen, ob es einen Booleschen Wert (gibt es ein unterschiedliches Min und Max) oder eine Zahl (was ist der Unterschied zwischen Min und Max) zurückgeben soll. Wenn letzteres der Fall ist, stellt sich auch die Frage, ob es sinnvoll ist, den Unterschied einer Liste mit der Länge Null als Zahl anzugeben. (Anstelle Noneoder eine Ausnahme)
ilkkachu

7
Es funktioniert, wie andere Leute erklärt haben, aber ein mögliches Problem ist, dass man bei der Rückkehr 0nicht sagen kann, ob argses leer oder nicht leer war, aber alle Elemente gleich waren.
Besonders Kalk

@EspeciallyLime: genau. Ich habe es in meiner Antwort erwähnt .
Eric Duminil

Antworten:


135

TL; DR

Wir fassen zunächst die beiden Verhaltensweisen der beiden logischen Operatoren andund zusammen or. Diese Redewendungen bilden die Grundlage unserer nachfolgenden Diskussion.

and

Gibt den ersten Falsy-Wert zurück, falls vorhanden, andernfalls den letzten Wert im Ausdruck.

or

Geben Sie den ersten Truthy-Wert zurück, falls vorhanden, andernfalls den letzten Wert im Ausdruck.

Das Verhalten ist auch in den Dokumenten zusammengefasst , insbesondere in dieser Tabelle:

Geben Sie hier die Bildbeschreibung ein

Der einzige Operator, der unabhängig von seinen Operanden einen booleschen Wert zurückgibt, ist der notOperator.


"Truthiness" - und "Truthy" -Bewertungen

Die Aussage

len(args) and max(args) - min(args)

Ist eine sehr pythonische, prägnante (und wahrscheinlich weniger lesbare) Art zu sagen: "Wenn argsnicht leer, geben Sie das Ergebnis von zurück max(args) - min(args)", andernfalls kehren Sie zurück 0. Im Allgemeinen ist es eine präzisere Darstellung eines if-elseAusdrucks. Beispielsweise,

exp1 and exp2

Sollte (grob) bedeuten:

r1 = exp1
if r1:
    r1 = exp2

Oder gleichwertig,

r1 = exp1 if exp1 else exp2

Ähnlich,

exp1 or exp2

Ist äquivalent zu,

r1 = exp1
if not r1:
    r1 = exp2

Wo exp1und exp2sind beliebige Python-Objekte oder Ausdrücke, die ein Objekt zurückgeben. Der Schlüssel zum Verständnis der Verwendung der Logik andund der orOperatoren besteht darin, zu verstehen, dass sie nicht darauf beschränkt sind, boolesche Werte zu bearbeiten oder zurückzugeben. Hier kann jedes Objekt mit einem Wahrheitswert getestet werden. Dazu gehört auch int, str, list, dict, tuple, set, NoneType, und Benutzerobjekte definiert. Auch die Kurzschlussregeln gelten weiterhin.

Aber was ist Wahrhaftigkeit?
Es bezieht sich darauf, wie Objekte ausgewertet werden, wenn sie in bedingten Ausdrücken verwendet werden. @Patrick Haugh fasst die Wahrhaftigkeit in diesem Beitrag gut zusammen .

Alle Werte gelten als "wahr", mit Ausnahme der folgenden Werte, die "falsch" sind:

  • None
  • False
  • 0
  • 0.0
  • 0j
  • Decimal(0)
  • Fraction(0, 1)
  • [] - ein leeres list
  • {} - ein leeres dict
  • () - ein leeres tuple
  • '' - ein leeres str
  • b'' - ein leeres bytes
  • set() - ein leeres set
  • eine leere range, wierange(0)
  • Objekte für die
    • obj.__bool__() kehrt zurück False
    • obj.__len__() kehrt zurück 0

Ein "wahrheitsgemäßer" Wert erfüllt die von ifoder while Anweisungen durchgeführte Prüfung . Wir verwenden "wahr" und "falsch", um von den boolWerten Trueund zu unterscheiden False.


Wie andfunktioniert

Wir bauen auf der Frage von OP auf, um eine Diskussion darüber zu führen, wie diese Operatoren in diesen Fällen vorgehen.

Gegeben eine Funktion mit der Definition

def foo(*args):
    ...

Wie gebe ich die Differenz zwischen dem minimalen und dem maximalen Wert in einer Liste mit null oder mehr Argumenten zurück?

Das Minimum und Maximum zu finden ist einfach (nutzen Sie die eingebauten Funktionen!). Der einzige Nachteil hierbei ist die angemessene Behandlung des Eckfalls, in dem die Argumentliste leer sein könnte (z. B. Aufruf foo()). Dank des andBetreibers können wir beides in einer einzigen Zeile erledigen :

def foo(*args):
     return len(args) and max(args) - min(args)

foo(1, 2, 3, 4, 5)
# 4

foo()
# 0

Da andverwendet wird, muss der zweite Ausdruck auch ausgewertet werden, wenn der erste ist True. Beachten Sie, dass der Rückgabewert immer das Ergebnis des zweiten Ausdrucks ist, wenn der erste Ausdruck als wahr bewertet wird . Wenn der erste Ausdruck als falsch bewertet wird, ist das zurückgegebene Ergebnis das Ergebnis des ersten Ausdrucks.

In der obigen Funktion ist If, foodas ein oder mehrere Argumente empfängt, len(args)größer als 0(eine positive Zahl), sodass das zurückgegebene Ergebnis lautet max(args) - min(args). OTOH, wenn keine Argumente übergeben werden, len(args)ist 0die Falsy ist und 0zurückgegeben wird.

Beachten Sie, dass eine alternative Möglichkeit zum Schreiben dieser Funktion Folgendes wäre:

def foo(*args):
    if not len(args):
        return 0

    return max(args) - min(args)

Oder genauer gesagt:

def foo(*args):
    return 0 if not args else max(args) - min(args)

Wenn natürlich keine dieser Funktionen eine Typprüfung durchführt, verlassen Sie sich nicht auf die Einfachheit dieser Konstrukte , es sei denn, Sie vertrauen vollständig auf die bereitgestellten Eingaben .


Wie orfunktioniert

Ich erkläre die Arbeitsweise orauf ähnliche Weise anhand eines erfundenen Beispiels.

Gegeben eine Funktion mit der Definition

def foo(*args):
    ...

Wie würden Sie vervollständigen foo, um alle Zahlen zurückzugeben 9000?

Wir verwenden orhier den Eckfall. Wir definieren fooals:

def foo(*args):
     return [x for x in args if x > 9000] or 'No number over 9000!'

foo(9004, 1, 2, 500)
# [9004]

foo(1, 2, 3, 4)
# 'No number over 9000!'

fooführt eine Filterung der Liste durch, um alle Zahlen beizubehalten 9000. Wenn es solche Zahlen gibt, ist das Ergebnis des Listenverständnisses eine nicht leere Liste, die Truthy ist, also wird sie zurückgegeben (Kurzschluss in Aktion hier). Wenn es keine solchen Zahlen gibt, ist das Ergebnis der []Listenkompensation Falsy. Der zweite Ausdruck wird nun ausgewertet (eine nicht leere Zeichenfolge) und zurückgegeben.

Unter Verwendung von Bedingungen könnten wir diese Funktion wie folgt umschreiben:

def foo(*args):
    r = [x for x in args if x > 9000]
    if not r:
        return 'No number over 9000!' 

    return r

Nach wie vor ist diese Struktur hinsichtlich der Fehlerbehandlung flexibler.


33
Es ist nicht "pythonisch", alle Klarheit für die Kürze zu opfern, was meiner Meinung nach hier der Fall ist. Es ist kein einfaches Konstrukt.
DBedrenko

11
Ich denke, man sollte beachten, dass Python-bedingte Ausdrücke diese Syntax weniger verbreitet haben. Ich bevorzuge auf jeden Fall max (args) - min (args), wenn len (args) sonst 0 gegenüber dem Original ist.
Richard

3
Eine andere häufige, die zunächst verwirrend ist, ist die Zuweisung eines Wertes, wenn keiner existiert: "some_var = arg oder 3"
Erik

12
@Baldrickk Bevor Leute anfangen, diese Syntax zugunsten von ternären Operatoren zu verwenden, denken Sie daran, dass ternäre Operatoren bei n-ary Bedingungsausdrücken schnell außer Kontrolle geraten können. Zum Beispiel if ... else (if ... else (if ... else (if ... else ...)))kann es genauso gut umgeschrieben werden wie ... and ... and ... and ... and ...und an diesem Punkt wird es wirklich schwierig, die Lesbarkeit für beide Fälle zu argumentieren.
CS95

4
Es ist nicht pythonisch, Klarheit der Kürze halber zu opfern, aber das tut es nicht. Es ist eine bekannte Redewendung. Es ist eine Redewendung, die man lernen muss, wie jede andere Redewendung, aber es ist kaum "Klarheit opfern".
Miles Rout

18

Zitieren aus Python Docs

Beachten Sie, dass weder andnoch or beschränken den Wert und geben sie zurück zum Falseund True, sondern die Rückkehr zuletzt ausgewertet Argument . Dies ist manchmal nützlich, z. B. wenn ses sich um eine Zeichenfolge handelt, die durch einen Standardwert ersetzt werden soll, wenn sie leer ist, s or 'foo'liefert der Ausdruck den gewünschten Wert.

Auf diese Weise wurde Python entwickelt, um die booleschen Ausdrücke auszuwerten. Die obige Dokumentation gibt uns einen Einblick, warum sie dies getan haben.

Um einen booleschen Wert zu erhalten, geben Sie ihn einfach ein.

return bool(len(args) and max(args)-min(args))

Warum?

Kurzschluss.

Beispielsweise:

2 and 3 # Returns 3 because 2 is Truthy so it has to check 3 too
0 and 3 # Returns 0 because 0 is Falsey and there's no need to check 3 at all

Das Gleiche gilt auch für ordas heißt, es wird den Ausdruck " Wahrheit" zurückgeben , sobald er gefunden wird, da die Auswertung des restlichen Ausdrucks überflüssig ist.

Anstatt Hardcore Trueoder zurückzugeben False, gibt Python Truthy oder Falsey zurück , die ohnehin zu Trueoder ausgewertet werden False. Sie können den Ausdruck unverändert verwenden, und er funktioniert weiterhin.


Um zu wissen, was Truthy und Falsey sind , überprüfen Sie die Antwort von Patrick Haugh


7

und und oder führen eine boolesche Logik durch, aber sie geben beim Vergleich einen der tatsächlichen Werte zurück. Bei Verwendung von und werden Werte in einem booleschen Kontext von links nach rechts ausgewertet. 0, '', [], (), {} und None sind in einem booleschen Kontext falsch; alles andere ist wahr.

Wenn alle Werte in einem booleschen Kontext wahr sind und den letzten Wert zurückgeben.

>>> 2 and 5
5
>>> 2 and 5 and 10
10

Wenn ein Wert in einem booleschen Kontext falsch ist und den ersten falschen Wert zurückgibt.

>>> '' and 5
''
>>> 2 and 0 and 5
0

Also der Code

return len(args) and max(args)-min(args)

Gibt den Wert , max(args)-min(args)wenn es args sonst kehrt len(args)die 0 ist.


5

Ist das ein legitimer / verlässlicher Stil oder gibt es irgendwelche Fallstricke?

Dies ist legitim, es handelt sich um eine Kurzschlussauswertung, bei der der letzte Wert zurückgegeben wird.

Sie liefern ein gutes Beispiel. Die Funktion wird zurückgegeben, 0wenn keine Argumente übergeben werden, und der Code muss nicht nach einem Sonderfall suchen, in dem keine Argumente übergeben wurden.

Eine andere Möglichkeit, dies zu verwenden, besteht darin, None-Argumente standardmäßig einem veränderlichen Grundelement wie einer leeren Liste zuzuweisen:

def fn(alist=None):
    alist = alist or []
    ....

Wenn ein nicht wahrheitsgemäßer Wert an alistihn übergeben wird, wird standardmäßig eine leere Liste verwendet. Dies ist eine praktische Möglichkeit, um eine ifAnweisung und die veränderbare Standardargument -Falle zu vermeiden


3

Fallstricke

Ja, es gibt ein paar Fallstricke.

fn() == fn(3) == fn(4, 4)

Erstens, wenn fnzurückgegeben wird 0, können Sie nicht wissen, ob es ohne Parameter, mit einem Parameter oder mit mehreren gleichen Parametern aufgerufen wurde:

>>> fn()
0
>>> fn(3)
0
>>> fn(3, 3, 3)
0

Was heißt fndas

Dann ist Python eine dynamische Sprache. Es ist nirgendwo festgelegt, was fnfunktioniert, wie seine Eingabe sein soll und wie seine Ausgabe aussehen soll. Daher ist es sehr wichtig, die Funktion richtig zu benennen. Ebenso müssen Argumente nicht aufgerufen werden args. delta(*numbers)oder calculate_range(*numbers)könnte besser beschreiben, was die Funktion tun soll.

Argumentfehler

Schließlich soll der logische andOperator verhindern, dass die Funktion fehlschlägt, wenn sie ohne Argument aufgerufen wird. Es schlägt jedoch immer noch fehl, wenn ein Argument keine Zahl ist:

>>> fn('1')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 2, in fn
TypeError: unsupported operand type(s) for -: 'str' and 'str'
>>> fn(1, '2')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 2, in fn
TypeError: '>' not supported between instances of 'str' and 'int'
>>> fn('a', 'b')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 2, in fn
TypeError: unsupported operand type(s) for -: 'str' and 'str'

Mögliche Alternative

Hier ist eine Möglichkeit, die Funktion gemäß "Einfacher um Vergebung als um Erlaubnis zu bitten" zu schreiben . Prinzip :

def delta(*numbers):
    try:
        return max(numbers) - min(numbers)
    except TypeError:
        raise ValueError("delta should only be called with numerical arguments") from None
    except ValueError:
        raise ValueError("delta should be called with at least one numerical argument") from None

Als Beispiel:

>>> delta()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 7, in delta
ValueError: delta should be called with at least one numerical argument
>>> delta(3)
0
>>> delta('a')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 5, in delta
ValueError: delta should only be called with numerical arguments
>>> delta('a', 'b')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 5, in delta
ValueError: delta should only be called with numerical arguments
>>> delta('a', 3)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 5, in delta
ValueError: delta should only be called with numerical arguments
>>> delta(3, 4.5)
1.5
>>> delta(3, 5, 7, 2)
5

Wenn Sie beim deltaAufruf ohne Argument wirklich keine Ausnahme auslösen möchten , können Sie einen Wert zurückgeben, der sonst nicht möglich ist (z. B. -1oder None):

>>> def delta(*numbers):
...     try:
...         return max(numbers) - min(numbers)
...     except TypeError:
...         raise ValueError("delta should only be called with numerical arguments") from None
...     except ValueError:
...         return -1 # or None
... 
>>> 
>>> delta()
-1

0

Ist das ein legitimer / verlässlicher Stil oder gibt es irgendwelche Fallstricke?

Ich möchte dieser Frage hinzufügen, dass sie nicht nur legitim und zuverlässig ist, sondern auch äußerst praktisch. Hier ist ein einfaches Beispiel:

>>>example_list = []
>>>print example_list or 'empty list'
empty list

Daher können Sie es wirklich zu Ihrem Vorteil nutzen. Um gewissenhaft zu sein, sehe ich das so:

Or Operator

Der Python- orOperator gibt den ersten Truth-y-Wert oder den letzten Wert zurück und stoppt

And Operator

Der Python- andOperator gibt den ersten False-y-Wert oder den letzten Wert zurück und stoppt

Hinter den Kulissen

In Python werden alle Zahlen mit TrueAusnahme von 0 interpretiert.

0 and 10 

ist das gleiche wie:

False and True

Welches ist klar False. Es ist daher logisch, dass 0 zurückgegeben wird


0

Ja. Dies ist das richtige Verhalten und Vergleich.

Zumindest in Python, A and Bkehrt , Bwenn Aim wesentlichen Trueauch , wenn Anicht null, nicht Nonekein leerer Behälter (wie zum Beispiel eines leeren list, dictusw.). Awird zurückgegeben IFF Aist im Wesentlichen Falseoder Noneoder leer oder null.

Auf der anderen Seite, A or Bkehrt , Awenn Aim Wesentlichen Trueeinschließlich , wenn Anicht NULL ist, NICHT Nonekein leerer Behälter (zB eine leere list, dictusw.), ansonsten gibt B.

Es ist leicht, dieses Verhalten nicht zu bemerken (oder zu übersehen), da in Python jedes non-nullnicht leere Objekt, das als True ausgewertet wird, wie ein Boolescher Wert behandelt wird.

Bei allen folgenden Zeichen wird beispielsweise "True" ausgegeben.

if [102]: 
    print "True"
else: 
    print "False"

if "anything that is not empty or None": 
    print "True"
else: 
    print "False"

if {1, 2, 3}: 
    print "True"
else: 
    print "False"

Auf der anderen Seite wird bei allen folgenden Zeichen "Falsch" ausgegeben.

if []: 
    print "True"
else: 
    print "False"

if "": 
    print "True"
else: 
    print "False"

if set ([]): 
    print "True"
else: 
    print "False"

Danke dir. Ich wollte schreiben Aist im Wesentlichen True. Korrigiert.
Emmanuelsa

0

auf einfache Weise zu verstehen,

UND : if first_val is False return first_val else second_value

z.B:

1 and 2 # here it will return 2 because 1 is not False

aber,

0 and 2 # will return 0 because first value is 0 i.e False

und => wenn jemand falsch ist, wird es falsch sein. Wenn beide wahr sind, wird nur es wahr

ODER : if first_val is False return second_val else first_value

Grund ist, wenn zuerst falsch ist, wird geprüft, ob 2 wahr ist oder nicht.

z.B:

1 or 2 # here it will return 1 because 1 is not False

aber,

0 or 2 # will return 2 because first value is 0 i.e False

oder => wenn jemand falsch ist, wird es wahr sein. Wenn also der erste Wert falsch ist, egal welcher Wert angenommen wird. es gibt also den zweiten Wert zurück, was auch immer es sein kann.

Wenn jemand wahr ist, wird es wahr. Wenn beide falsch sind, wird es falsch.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.