Python 3 per a no programadors/Expressions booleanes
Aquí hi ha un petit exemple d'expressions booleanes:
a = 6
b = 7
c = 42
print(1, a == 6)
print(2, a == 7)
print(3, a == 6 and b == 7)
print(4, a == 7 and b == 7)
print(5, not a == 7 and b == 7)
print(6, a == 7 or b == 7)
print(7, a == 7 or b == 6)
print(8, not (a == 7 and b == 6))
print(9, not a == 7 and b == 6)
L'execució d'aquest codi origina la següent resposta:
1 True 2 False 3 True 4 False 5 True 6 True 7 False 8 True 9 False
Què és el que passa? El programa consisteix en un seguit de valoracions print
) de declaracions. Cada declaració print
retorna un número i una expressió. El número serveix per ajudar-nos a fer el seguiment de quina resposta correspon a cada declaració. Vegeu que cada expressió es resol amb una resposta que és False
(Fals) o True
(Veritat). En Python false també es pot escriure amb un 0 i true amb un 1.
Les línies:
print(1, a == 6)
print(2, a == 7)
donen com a resposta True
i False
respectivament ja que la primera expressió és veritat i la segona és falsa. La tercera resposta, print(3, a == 6 and b == 7)
, és una mica diferent. L'operador and
condiciona a que ambdues expressions siguin veritables perquè el valor del conjut de l'expressió sigui veritable. Si una o les dues expressions són False aleshores el resultat de l'expressió serà False. La línia següent, print(4, a == 7 and b == 7)
, permet comprovar que si una part d'una expressió amb un and
és falsa, aleshores el conjunt és fals. El comportament de l'operador and
es pot resumir de la següent manera:
expressió | resultat |
---|---|
true and true
|
true |
true and false
|
false |
false and true
|
false |
false and false
|
false |
Vegeu que si la primera expressió és falsa Python ja no comprova la segona expressió perquè el conjunt serà fals. Proveu d'executar False and print("Hi")
i compareu-ho amb l'execució de True and print("Hi")
El terme tècnic per això és Evaluació a curt circuit
La línia següent, print(5, not a == 7 and b == 7)
, fa servir l'operador not
. not
simplemet retorna l'oposat de l'expressió. (L'expressió es podria reescriure com a print(5, a != 7 and b == 7)
).
Aquesta és la taula:
expressió | resultat |
---|---|
not true
|
false |
not false
|
true |
Les dues línies següents, print(6, a == 7 or b == 7)
i print(7, a == 7 or b == 6)
, fan servir l'operador or
. L'operador or
retorna true si la primera expressió és true, o si la segona expressió és true o ambdues són true. Si cap de les expressions és true aleshores retorna false. Aquesta és la taula:
expressió | resultat |
---|---|
true or true
|
true |
true or false
|
true |
false or true
|
true |
false or false
|
false |
Vegeu que si la primera expressió és true aleshores Python ja no comprova la segona expressió perquè ja sap que el conjunt de l'expressió serà true (per l'operador or). Això funciona de manera que or
és true si com a mínim algun dels elements de l'expressió és cert (true). La primera part és certa (true) de manera que la segona part podria ser certa o falsa, però el conjunt de l'expressió continuaria essent cert (perquè la condició és que algun sigui cert).
Les dues línies següents, print(8, not (a == 7 and b == 6))
i print(9, not a == 7 and b == 6)
, permeten comprovar que els parèntesis es poden fer servir per agrupar expressions i forçar que s'evaluï primer una de les parts. Vegeu que els parèntesis canvien el resultat de l'expressió de fals a cert. Això passa perquè els parèntesis fan que s'apliqui el not
a tota l'expressió en comptes d'aplicar-se només a la porció de l'expressió a == 7
.
A continuació es mostra un exemple d'utilització d'una expressió booleana:
llista = ["Vida", "Univers", "Tot", "Jack", "Jill", "Vida", "Jill"]
# fa una còpia de la llista. Vegeu el capítol "Més sobre llistes" per veure què significa [:].
copia = llista[:]
# ordena la còpia
copia.sort()
prev = copia[0]
del copia[0]
comptador = 0
# Avança per la llista per trobar una coincidència
while comptador < len(copia) and copia[comptador] != prev:
prev = copia[comptador]
comptador = comptador + 1
# Si no s'ha trobat cap coincidència aleshores comptador no pot ser < len
# ja que el bucle while continua mentre comptador és < len
# i no es troba cap coincidència
if comptador < len(copia):
print("Primera coincidència:", prev)
L'execució d'aquest codi origina la següent resposta:
Primera coincidència: Jill
Aquest programa el que fa és anar comprovant determinades condicions while comptador < len(copia) i copia[comptador] no és igual a prev
. Si comptador
és més gran que el darrer index de copia
o bé copia[comptador] és igual a prev aleshores l'and
deixa de ser cert i surt del bucle. L'if
simplement fa una comprovació per assegurar que el while
ha sortit perquè s'ha trobat una coincidència.
L'altra característica de l'and
es fa servir en aquest exemple. Si mireu la taula per l'and
podeu veure que la tercera entrada és "false and false". Si comptador >= len(copia)
(en altres paraules comptador < len(copia)
és false) aleshores mai es mirarà copia[comptador]
. Això és així perquè Python sap que si el primer és fals aleshores ambdós no poden ser veritat. Això es coneix com a curt circuit i és útil si la segona meitatde l'and
pot causar un error si hi ha alguna cosa que no és correcta. S'ha fet servir la primera expressió (comptador < len(copia)
) per comprovar i veure si comptador
era un índex vàlid per a copia
. (Si teniu algun dubte que això sigui així podeu suprimir les coicidències "Jill" i "Life", comprovar que encara funcionai aleshores capgirar l'ordre de comptador < len(copia) and copia[comptador] != prev
perquè sigui copia[comptador] != prev and comptador < len(copia)
.)
Les expressions booleanes es poden fer servir quan és necesasri comprovar dues o més coses diferents a la vegada.
Notes sobre Operadors Booleans
modificaUn error comú entre els programadors més novells és la mala interpretació sobre la manera com funcionen els operadors booleans, que ve de la manera com l'intèrpret de python llegeix aquestes expressions. Per exemple, després de l'aprenentatge inicial sobre les declaracions "and " i "or", es pot assumir que l'expressió x == ('a' or 'b')
comprovarà si la variable x
és equivalent a una de les dues cadenes 'a'
o 'b'
. Això no és així. Per veure què és el que realment passa, obriu una sessió amb l'intèrpret i introduïu les expressions següents:
>>> 'a' == ('a' or 'b') >>> 'b' == ('a' or 'b') >>> 'a' == ('a' and 'b') >>> 'b' == ('a' and 'b')
I aquest serà el resultat intuïtiu:
>>> 'a' == ('a' or 'b') True >>> 'b' == ('a' or 'b') False >>> 'a' == ('a' and 'b') False >>> 'b' == ('a' and 'b') True
En aquest punt, els operadors and
i or
semblen erronis. No sembla que tingui sentit que, per les dues primeres expressions, 'a'
sigui equivalent a 'a'
o 'b'
mentre que 'b'
no ho sigui. A més a més, no té cap sentit que 'b' sigui equivalent a 'a'
i 'b'
. Després d'examinar què fa l'intèrpret amb els operadors booleans, aquests resultats fan de fet exactament el que se'ls demana, que no és el mateix que el que penses que estàs demanant.
Quan l'intèrpret Python mira l'expressió or
, agafa la primera declaració i fa la comprovació per veure si és certa (true). Si la primera declaració és certa (true), aleshores Python retorna aquest valor de l'objecte sense comprovar la segona declaració. Això és així perquè per una expressió or
, el conjunt és cert si un dels valors és cert; el programa no té per què molestar-se a mirar la segona declaració. D'altra banda, si el primer valor és evaluat com a fals Python comprova la segona meitat i retorna aquest valor. Aquesta segona meitat determina el valor veritable del conjunt de l'expressió des del moment en què la primera meitat era falsa. Aquesta "mandra" per part de l'intèrpret s'anomena "curt circuit" i és una manera comuna d'avaluar les expressions booleanes en molts llenguatges de programació.
De manera similar, per una expressió and
, Python fa servir la tècnica del curt circuit per accelerar l'avaluació del valor veritable. Si la primera declaració és falsa aleshores el conjunt ha de ser fals, de manera que retorna aquest valor. D'altra banda si el primer valor és veritat (true), aleshores comprova el segon i retorna aquest valor.
Una cosa a tenir en compte en aquest punt és que l'expressió booleana retorna un valor indicant True
o False
, però Python considera un número de coses diferents per assignar-hi un valor cert. Per comprovar el valor cert de qualsevol objecte donat x
, es pot fer servir la funció bool(x)
per veure els seus valors veritables.
A continuació es mostra una taula amb exemples de valors veritables de diversos objectes:
True | False |
---|---|
True | False |
1 | 0 |
Números diferents de zero | La cadena 'None' |
Cadenes que no són buides | Cadenes buides |
Llistes que no són buides | Llistes buides |
Diccionaris que no són buits | Diccionaris buits |
Ara és possible entendre el resultats confusos que s'han obtingut fent la prova de les expressions booleanes anteriors. Fem un cop d'ull al que "veu" l'intèrpret tal com va llegint el codi:
Primer cas:
>>> 'a' == ('a' or 'b') # Fixem-nos primer en els parèntesis, de manera que evaluem l'expressió "('a' or 'b')" # 'a' és una cadena que no és buida, de manera que el primer valor és True # Retorna aquest primer valor: 'a' >>> 'a' == 'a' # la cadena 'a' és equivalent a la cadena 'a', per la qual cosa l'expressió és True True
Segon cas:
>>> 'b' == ('a' or 'b') # Fixem-nos primer en els parèntesis, de manera que evaluem l'expressió "('a' or 'b')" # 'a' és una cadena que no és buida, de manera que el primer valor és True # Retorna aquest primer valor: 'a' >>> 'b' == 'a' # la cadena 'b' no és equivalent a la cadena 'a', per la qual cosa l'expressió és False False
Tercer cas:
>>> 'a' == ('a' and 'b') # Fixem-nos primer en els parèntesis, de manera que evaluem l'expressió "('a' and 'b')" # 'a' és una cadena que no és buida, de manera que el primer valor és True; examina el segon valor # 'b' és una cadena que no és buida, de manera que el segon valor és True # Retorna aquest segon valor com a resultat de tota l'expressió: 'b' >>> 'a' == 'b' # la cadena 'a' no és equivalent a la cadena 'b', i per tant l'expressió és False False
Quart cas:
>>> 'b' == ('a' and 'b') # Fixem-nos primer en els parèntesis, de manera que evaluem l'expressió "('a' and 'b')" # 'a' és una cadena que no és buida, de manera que el primer valor és True; examina el segon valor # 'b' és una cadena que no és buida, de manera que el segon valor és True # Retorna aquest segon valor com a resultat de tota l'expressió: 'b' >>> 'b' == 'b' # la cadena 'b' és equivalent a la cadena 'b', i per tant l'expressió és True True
Per la qual cosa Python estava fent realment la seva feina quan ens donava aquells resultats aparentment sorprenents. Tal com s'ha esmentant anteriorment, el que és important e´s reconèixer quin serà el valor que retornarà l'expressió quan s'avaluï, perquè no sempre és obvi.
Tornant enrere cap a les expressions inicials, així és com s'hauria d'haver escrit per obtenir el que amb tota probabilitat volíeu obtenir:
>>> 'a' == 'a' or 'a' == 'b' True >>> 'b' == 'a' or 'b' == 'b' True >>> 'a' == 'a' and 'a' == 'b' False >>> 'b' == 'a' and 'b' == 'b' False
Quan s'avaluen aquestes comparacions retornen valors veritables en termes de True o False, no pas cadenes de text, i per tant obtenim els resultats adients.
Exemples
modificapassword1.py
## Aquest programa pregunta a l'usuari un nom i una paraula clau.
# Aleshores comprova que l'usuari tingui accés.
nom = input("Quin és el vostre nom? ")
paraula_clau = input("Quina és la paraula clau? ")
if nom == "Jordi" and paraula_clau == "Divendres":
print("Benvingut Jordi")
elif nom == "Joan" and paraula_clau == "Saturn":
print("Benvingut Joan")
else:
print("No sabem qui sou.")
L'exemple funciona de la següent manera
Quin és el vostre nom? Jordi Quina és la paraula clau? Divendres Benvingut Jordi
Quin és el vostre nom? Miquel Quina és la paraula clau? Banc No sabem qui sou.
Exercicis
modificaEscriure un programa perquè un usuari encerti el vostre nom, però que només tingui tres oportunitats abans que el programa surti.
print("Proveu de descobrir el meu nom!")
comptador = 1
nom = "Guillem"
pregunta = input("Quin és el meu nom? ")
while comptador < 3 and pregunta.lower() != nom:
print("No és aquest!")
pregunta = input("Quin és el meu nom? ")
comptador = comptador + 1
if pregunta.lower() != nom:
print("No és aquest!") # aquest missatge no s'escriu després del tercer intent, que és quan s'escriu
print("S'han acabat les oportunitats.")
else:
print("Sí! El meu nom és", nom + "!")