3.6. Le particolarità degli operatori and e or

In Python, gli operatori and e or eseguono le operazioni di logica booleane che ci si aspetta dal loro nome, ma non restituiscono valori booleani; essi restituiscono invece il valore di uno degli elementi che si stanno confrontando.

Esempio 3.16. Introduzione all'operatore and

>>> 'a' and 'b'         1
'b'
>>> '' and 'b'          2
''
>>> 'a' and 'b' and 'c' 3
'c'
1 Quando si usa l'operatore and, gli elementi dell'espressione vengono valutati in un contesto booleano da sinistra a destra. I valori 0, '', [], (), {}, e None sono considerati falsi in un contesto booleano; ogni altro valore è considerato vero. [3] Se tutti gli elementi hanno valore vero in contesto booleano, l'operatore and restituisce il valore dell'ultimo elemento. In questo caso, l'operatore and valuta 'a', che è vero, quindi valuta 'b', che è vero, ed infine ritorna 'b'.
2 Se uno degli elementi ha valore falso in contesto booleano, l'operatore and restituisce il valore del primo di tali elementi. In questo caso, '' è il primo valore falso.
3 Tutti gli elementi hanno valore vero, per cui l'operatore and restituisce il valore dell'ultimo elemento, 'c'.

Esempio 3.17. Introduzione all'operatore or

>>> 'a' or 'b'          1
'a'
>>> '' or 'b'           2
'b'
>>> '' or [] or {}      3
{}
>>> def sidefx():
...     print "in sidefx()"
...     return 1
>>> 'a' or sidefx()     4
'a'
1 Quando si usa l'operatore or, gli elementi sono valutati in contesto booleano da sinistra a destra, come per l'operatore and. Se uno degli elementi ha valore vero, l'operarore or ritorna immediatamente quel valore. Nel nostro caso, 'a' è il primo elemento con valore vero.
2 L'operatore or valuta '', che è falso, quindi 'b', che è vero e restituisce 'b'.
3 Se tutti gli elementi hanno valore falso, l'operatore or restituisce l'ultimo valore. L'operatore or valuta '', che è falso, quindi [], che è falso, quindi {}, che è falso e restituisce {}.
4 Si noti che l'operatore or elabora gli elementi solamente finché non ne trova uno che sia vero in contesto booleano ed ignora i rimanenti. Questa distinzione è importante se alcuni elementi dell'espressione hanno effetti collaterali. Nel nostro caso, la funzione sidefx non è mai chiamata, perché l'operatore or valuta 'a', che è vero e restituisce immediatamente 'a'.

Gli hakers del linguggio C hanno sicuramente familiarità con il costrutto bool ? a : b, che restituisce il valore a se bool è vero, altrimenti restituisce b. In ragione del modo in cui and ed or funzionano in Python, è possibile ottenere lo stesso risultato.

Esempio 3.18. Introduzione al trucchetto and-or

>>> a = "first"
>>> b = "second"
>>> 1 and a or b 1
'first'
>>> 0 and a or b 2
'second'
1 Questa sintassi sembra molto simile al costrutto bool ? a : b in C. L'espressione nella sua globalità è valutata da sinistra a destra, quindi l'operatore and è valutato per primo. L'espressione 1 and 'first' restituisce 'first', quindi l'espressione 'first' or 'second' restituisce 'second'.
2 L'espressione 0 and 'first' restituisce 0, quindi l'espressione 0 or 'second' restituisce 'second'.

Tuttavia, visto che questa espressione in Python è semplice logica booleana, non un costrutto speciale del linguaggio, vi è una differenza molto ma molto ma molto importante tra questo trucchetto and-or e l'espressione bool ? a : b in C. Se il valore di a è falso, l'espressione non funzionerà come ci si aspetta. (Si capisce che ci sono cascato? Più di una volta?)

Esempio 3.19. Quando il trucchetto and-or fallisce

>>> a = ""
>>> b = "second"
>>> 1 and a or b 1
'second'
1 Dato che a è una stringa vuota, che Python considera falsa in un contesto booleano, 1 and '' restituisce '', quindi '' or 'second' restituisce 'second'. Oops! non è quello che si voleva.
Importante
Il trucchetto and-or, cioè bool and a or b, non funziona come il costrutto C bool ? a : b quando a ha un valore falso in un contesto booleano.

Dunque la parte veramente complicata del trucchetto and-or è essere sicuri che il valore di a non sia falso. Un modo comune per constatarlo è trasformare a in [a] e b in [b] e poi considerare il primo elemento della lista risultante, che sarà a oppure b.

Esempio 3.20. Usare il trucchetto and-or in modo sicuro

>>> a = ""
>>> b = "second"
>>> (1 and [a] or [b])[0] 1
''
1 Dato che [a] non è una lista vuota, il suo valore non è mai falso. Anche se a vale 0 oppure '' o un altro valore falso, la lista [a] ha valore vero perché ha un elemento.

A questo punto, questo trucco sembra troppo complicato per quello che vale. Si potrebbe dopotutto ottenere la stessa cosa con un'istruzione if, perciò perché darsi tutto questo daffare? Bene, in molti casi si dovrà scegliere tra due valori costanti, per cui si può usare la versione più semplice del trucco senza preoccupazione, perché si sa che un elemento avrà sempre valore vero. Anche nel caso si debba usare la versione più complicata, ci sono buone ragioni per farlo; ci sono alcuni casi in Python in cui le istruzioni if non sono consentite, come per esempio in una funzione lambda.

Ulteriori letture

Footnotes

[3] Beh, quasi ogni cosa. In modo predefinito le istanze di una classe corrispondono a true in un contesto booleano, ma potete definire metodi speciali nella vostra classe per far valutare un'istanza in false. Imparerete tutto a proposito delle classi e dei loro metodi speciali nel capitolo 4.