TL; DR
Iniziamo riassumendo i due comportamenti dei due operatori logici and
e or
. Questi idiomi formeranno la base della nostra discussione di seguito.
and
Restituisce il primo valore Falsy se ce ne sono, altrimenti restituisce l'ultimo valore nell'espressione.
or
Restituisce il primo valore Truthy se ce ne sono, altrimenti restituisce l'ultimo valore nell'espressione.
Il comportamento è anche riassunto nei documenti , soprattutto in questa tabella:
L'unico operatore che restituisce un valore booleano indipendentemente dai suoi operandi è l' not
operatore.
Valutazioni "Verità" e "Verità"
La dichiarazione
len(args) and max(args) - min(args)
È un molto divinatorio conciso (e probabilmente meno leggibile) modo di dire "se args
non è vuota, restituisce il risultato di max(args) - min(args)
", altrimenti restituisce 0
. In generale, è una rappresentazione più concisa di if-else
un'espressione. Per esempio,
exp1 and exp2
Dovrebbe (approssimativamente) tradurre in:
r1 = exp1
if r1:
r1 = exp2
O, equivalentemente,
r1 = exp1 if exp1 else exp2
Allo stesso modo,
exp1 or exp2
È equivalente a,
r1 = exp1
if not r1:
r1 = exp2
Dove exp1
e exp2
sono oggetti Python arbitrari o espressioni che restituiscono un oggetto. La chiave per comprendere gli usi degli operatori logici and
e or
qui è capire che non sono limitati a operare o restituire valori booleani. Qualsiasi oggetto con un valore di veridicità può essere testato qui. Questo include int
, str
, list
, dict
, tuple
, set
,NoneType
, e l'utente oggetti definiti. Anche le regole di cortocircuito si applicano.
Ma cos'è la verità?
Si riferisce al modo in cui gli oggetti vengono valutati quando utilizzati nelle espressioni condizionali. @Patrick Haugh riassume bene la verità in questo post .
Tutti i valori sono considerati "veritieri" tranne i seguenti, che sono "falsi":
None
False
0
0.0
0j
Decimal(0)
Fraction(0, 1)
[]
- un vuoto list
{}
- un vuoto dict
()
- un vuoto tuple
''
- un vuoto str
b''
- un vuoto bytes
set()
- un vuoto set
- un vuoto
range
, comerange(0)
- oggetti per i quali
obj.__bool__()
ritorna False
obj.__len__()
ritorna 0
Un valore "veritiero" soddisferà il controllo eseguito da if
o while
. Usiamo "truthy" e "falsy" per differenziarci dai
bool
valori True
e False
.
Come and
funziona
Partiamo dalla domanda di OP come seguito in una discussione su come questi operatori in questi casi.
Data una funzione con la definizione
def foo(*args):
...
Come restituisco la differenza tra il valore minimo e massimo in un elenco di zero o più argomenti?
Trovare il minimo e il massimo è facile (usa le funzioni integrate!). L'unico intoppo qui è gestire in modo appropriato il caso d'angolo in cui l'elenco degli argomenti potrebbe essere vuoto (ad esempio, la chiamata foo()
). Possiamo fare entrambe le cose in un'unica linea grazie and
all'operatore:
def foo(*args):
return len(args) and max(args) - min(args)
foo(1, 2, 3, 4, 5)
# 4
foo()
# 0
Poiché and
viene utilizzata, la seconda espressione deve essere valutata anche se la prima è True
. Nota che, se la prima espressione viene valutata come vera, il valore restituito è sempre il risultato della seconda espressione . Se la prima espressione viene valutata come falsa, il risultato restituito è il risultato della prima espressione.
Nella funzione precedente, If foo
riceve uno o più argomenti, len(args)
è maggiore di 0
(un numero positivo), quindi il risultato restituito è max(args) - min(args)
. OTOH, se vengono passati argomenti, len(args)
è 0
che è Falsy, e 0
viene restituito.
Nota che un modo alternativo per scrivere questa funzione sarebbe:
def foo(*args):
if not len(args):
return 0
return max(args) - min(args)
O, più concisamente,
def foo(*args):
return 0 if not args else max(args) - min(args)
Ovviamente nessuna di queste funzioni esegue alcun controllo del tipo, quindi a meno che non ti fidi completamente dell'input fornito, non fare affidamento sulla semplicità di questi costrutti.
Come or
funziona
Spiego il funzionamento di or
in modo simile con un esempio artificioso.
Data una funzione con la definizione
def foo(*args):
...
Come completeresti foo
per restituire tutti i numeri 9000
?
Usiamo or
per gestire il caso d'angolo qui. Definiamo foo
come:
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!'
foo
esegue un filtraggio nell'elenco per mantenere tutti i numeri 9000
. Se esistono tali numeri, il risultato della comprensione della lista è un elenco non vuoto che è Vero, quindi viene restituito (cortocircuito in azione qui). Se non esistono tali numeri, il risultato della lista comp è []
che è Falsy. Quindi la seconda espressione viene ora valutata (una stringa non vuota) e viene restituita.
Usando i condizionali, potremmo riscrivere questa funzione come,
def foo(*args):
r = [x for x in args if x > 9000]
if not r:
return 'No number over 9000!'
return r
Come prima, questa struttura è più flessibile in termini di gestione degli errori.
and
(così comeor
) non si limita a lavorare o restituire valori booleani.