Fundamentos
Tablas de verdad en Python: comprobar condiciones con and, or y not
Arma tablas de verdad en Python para probar condiciones con and, or y not, ver si dos expresiones son equivalentes y hallar el caso olvidado.
Por Prof. Rodrigo Faustino · Actualizado el · 13 min de lectura
Métricas de este artículo
- Palabras
- 2496
- Caracteres
- 13.546
- Tiempo de lectura
- ~13 min
Una tabla de verdad lista todas las combinaciones posibles de True y False para las variables de una expresión y muestra, al lado de cada combinación, el resultado de la expresión. Con una variable hay dos filas, con dos hay cuatro, con tres hay ocho: la cuenta es 2 elevado al número de variables. En Python, se puede imprimir una tabla así con pocas líneas de código, y sirve para tres cosas prácticas: comprobar una condición antes de usarla, descubrir si dos expresiones que parecen distintas dicen lo mismo y hallar el caso que nadie previó.
El artículo anterior presentó los operadores and, or y not. Este muestra cómo confiar en ellos. Una condición mal escrita no da error, da el comportamiento equivocado en el momento equivocado, y la tabla es la forma más barata de atrapar eso antes de que el programa llegue cerca de una placa, de un motor o de un alumno.
El problema: dos condiciones que parecen la misma
Una alarma debe dispararse cuando la puerta o la ventana esté abierta. A la hora de escribir la condición para el caso contrario, el "no se dispara", alguien piensa así: "no se dispara cuando la puerta no está abierta y la ventana tampoco". Otra persona, con apuro, escribe: "no se dispara cuando la puerta no está abierta o la ventana no está abierta". Las dos frases suenan parecidas, y en código quedan así:
nao_dispara = not (porta or janela)
outra = (not porta) or (not janela)¿Dan el mismo resultado? La intuición queda dividida, y es exactamente para eso que existe la tabla. Antes de armarla, conviene ver cómo se construye.
Del programa al semestre planificado
- Plan de enseñanza y calendario montados solos
- Diario de clases listo para pegar en el SIGA
- Diapositivas y listas de ejercicios con solucionario
Versión Community gratis · Windows
Qué es una tabla de verdad
Cada variable se vuelve una columna, y cada fila es una combinación de valores. La forma de no olvidar ninguna es contar en binario, con False en el lugar del 0 y True en el lugar del 1: la última columna alterna en cada fila, la del medio cada dos filas, la primera cada cuatro.
Cada variable de más duplica el número de filas. Dos variables dan 4, tres dan 8, cuatro dan 16, cinco dan 32 y diez dan 1024. Esto explica por qué la tabla es una herramienta para condiciones pequeñas: con tres o cuatro variables cabe en la pantalla, y con diez ya es un trabajo para la máquina.
Para dos variables, la tabla de los operadores and y or puede escribirse fila por fila:
print(2 ** 1, 2 ** 2, 2 ** 3, 2 ** 4, 2 ** 5, 2 ** 10)
print("a b and or")
print(False, False, False and False, False or False)
print(False, True, False and True, False or True)
print(True, False, True and False, True or False)
print(True, True, True and True, True or True)La primera línea confirma la cuenta de las filas: 2 4 8 16 32 1024. Las cuatro siguientes son la tabla en sí, y cada una tiene las dos variables, el resultado del and y el del or. Solo la última fila tiene True en las dos columnas, y solo la primera tiene False en las dos. Quien escribe la tabla así, a mano, aprende a leer cada fila, pero el método no escala: con tres variables serían ocho filas y con cuatro, dieciséis.
Armando la tabla con for anidado
El for es tema del módulo de repetición, pero resuelve este problema tan bien que vale un adelanto. En for a in (False, True):, la variable a asume un valor por vez, primero False y después True, y el bloque con sangría corre una vez para cada valor. Poniendo un for dentro de otro, aparecen todas las combinaciones:
print(f"{'a':<6}{'b':<6}{'and':<6}or")
for a in (False, True):
for b in (False, True):
print(f"{str(a):<6}{str(b):<6}{str(a and b):<6}{a or b}")La salida es la misma tabla, ahora alineada en columnas. El :<6 dentro de la f-string reserva seis caracteres y alinea el texto a la izquierda. El str() alrededor de cada valor no es adorno. Un booleano formateado directo, como en f"{a:<6}", aparece como 1 o 0 en lugar de True o False, tanto en el Python del computador como en el simulador. Convertido antes a texto, aparece con el nombre correcto.

Con tres variables, basta otro for dentro de los otros. El orden de las filas sigue el mismo conteo binario de la figura.
Comprobando si dos expresiones son equivalentes
Volviendo a la alarma. Dos expresiones son equivalentes cuando dan el mismo resultado en todas las filas de la tabla. En lugar de mirar fila por fila, el programa puede comparar las columnas con == e imprimir una respuesta por fila:
for porta in (False, True):
for janela in (False, True):
nao_dispara = not (porta or janela)
certa = (not porta) and (not janela)
errada = (not porta) or (not janela)
print(porta, janela, nao_dispara == certa, nao_dispara == errada)La salida tiene cuatro líneas: False False True True, False True True False, True False True False y True True True True. La primera columna de comparación, nao_dispara == certa, es verdadera en las cuatro filas: las dos expresiones son equivalentes. La segunda, nao_dispara == errada, falla en las filas en que solo uno de los lados está abierto, que son justamente las que importan: con la puerta abierta y la ventana cerrada, la versión equivocada diría que la alarma no se dispara, y debería dispararse.
Ese resultado es una de las leyes de De Morgan, que vale la pena guardar de memoria: negar un or es negar cada parte e intercambiarlo por and, y negar un and es negar cada parte e intercambiarlo por or.
for a in (False, True):
for b in (False, True):
print(a, b, not (a and b), (not a) or (not b), not (a or b), (not a) and (not b))Las cuatro columnas finales de la salida muestran lo que muestra la figura: las dos primeras son iguales en todas las filas (True True, True True, True True y False False), y las dos últimas también (True True en la primera fila y False False en las otras tres). El error más común es aplicar solo la mitad de la regla, negar las partes y olvidar intercambiar el operador. La tabla lo delata de inmediato.
Otras equivalencias útiles
Hay equivalencias que ayudan a simplificar condiciones largas. La absorción dice que a or (a and b) es lo mismo que a, y que a and (a or b) también. Si a es verdadero, la primera expresión ya es verdadera, y si a es falso, el and interno es falso y el resultado depende solo de a. La tabla lo confirma, y muestra de paso el "o exclusivo", el "uno u otro" del habla cotidiana, que Python escribe como a != b:
for a in (False, True):
for b in (False, True):
print(a, b, (a or (a and b)) == a, (a and (a or b)) == a, a != b)La salida es False False True True False, False True True True True, True False True True True y True True True True False. Las dos columnas del medio son siempre True, y la última es la del "o exclusivo": verdadera cuando los dos valores son distintos. El or de Python es inclusivo, es decir, True or True da True. Cuando la idea es "uno u otro, pero no los dos", el != lo resuelve. El operador ^ también existe, pero trabaja con los bits de números enteros y puede devolver 1 y 0 en lugar de True y False, lo que lo hace una peor elección para condiciones.
La IA que recuerda tu vida y vive en tu ordenador
- Memoria a largo plazo con la fuente de cada respuesta
- Funciona sin conexión: tus datos se quedan en tu PC
- Lee PDF, diapositivas, fotos y grabaciones de reuniones
Plan Esencial gratis para siempre
Una tabla de verdad para un caso real
Una bomba de riego conectada a una ESP32 debe funcionar cuando el suelo esté seco, el tanque tenga agua y no esté lloviendo. Son tres preguntas, y la regla es seco and agua and not chuva. Como son tres variables, la tabla tiene ocho filas.
print(f"{'seco':<6}{'agua':<6}{'chuva':<6}irrigar")
for seco in (False, True):
for agua in (False, True):
for chuva in (False, True):
irrigar = seco and agua and not chuva
print(f"{str(seco):<6}{str(agua):<6}{str(chuva):<6}{irrigar}")
total = 0
for seco in (False, True):
for agua in (False, True):
for chuva in (False, True):
for manual in (False, True):
irrigar = (seco and agua and not chuva) or manual
total += irrigar
print(total, 2 ** 4)La tabla impresa tiene una única fila con True en la última columna, la séptima, en la que el suelo está seco, hay agua y no llueve. Eso es lo que se espera de la regla, y conviene comprobarlo, porque la tabla es el único lugar donde el programador ve las siete combinaciones en que la bomba queda apagada. Una de ellas, suelo seco y tanque vacío, es la que protege a la bomba de trabajar en seco. Otra, suelo seco y lluvia, evita un desperdicio de agua.
El segundo trozo agrega una variable, manual, que enciende la bomba a la fuerza. Ahora son 16 filas, y la cuenta con total += irrigar cuenta cuántas dan True: el booleano se suma como 1 y 0. La salida es 9 16. De las 16 combinaciones, 8 tienen manual verdadero y encienden la bomba de cualquier manera, y 1 más la enciende por la regla original, lo que da 9. Es una forma de comprobar la lógica sin armar nada: si el número de filas verdaderas no coincide con lo que esperabas, la condición está mal.
Qué cambia en la ESP32
Las operaciones lógicas se comportan igual en MicroPython y en el Python del computador. Lo que cambia es de dónde vienen los valores. En una placa, casi ninguna variable nace como booleano: el pin.value() devuelve el número 0 o 1, y son las comparaciones con sensores (temperatura > 30) las que producen True y False. Para la tabla, cada comparación se vuelve una variable, y lo que importa es probar la lógica sin necesidad de conectar nada.
El caso del botón con resistencia de pull-up merece una tabla solo para él. Con el pull-up, el pin lee 1 cuando el botón está suelto y 0 cuando está presionado, lo contrario de lo que pide la intuición:
for valor in (0, 1):
print(valor, valor == 0, not valor, bool(valor), valor == True)La salida es 0 True True False False y 1 False False True True. Cuando el pin vale 0, valor == 0 y not valor son ambos verdaderos, y así se escribe "el botón está presionado": pressionado = pino.value() == 0. Las dos últimas columnas muestran que el 1 también se comporta como True en una comparación, y el 0 como False. El artículo sobre pull-up y pin flotante explica por qué la lógica queda invertida y cómo evitar lecturas inestables.
En el aula: dónde suelen aparecer las tablas
Los puntos de abajo se repiten siempre que un grupo empieza a escribir condiciones, y cada uno se resuelve mirando la tabla.
La "y" del español que se vuelve "o" en el código
La regla "los mayores de 65 años y los menores de 12 años pagan la mitad" suena natural, y la traducción a Python crea una condición que nunca es verdadera. En español, la "y" junta dos grupos de personas. En lógica, una persona no puede tener más de 65 y menos de 12 al mismo tiempo, y quien junta grupos está diciendo or.
for idade in (8, 30, 70):
print(idade, idade >= 65 and idade < 12, idade >= 65 or idade < 12)
for x in (3, 5, 9):
print(x, x < 5 or x >= 5, x < 5 and x > 10)La primera parte de la salida es 8 False True, 30 False False y 70 False True. La columna del and es falsa para todas las edades, y la del or da el resultado correcto: niños de 8 y ancianos de 70 pagan la mitad, adultos de 30 no. La segunda parte muestra el otro defecto clásico, la condición que nunca cambia de valor. x < 5 or x >= 5 es verdadera para cualquier x, y x < 5 and x > 10 es falsa para cualquier x. Los valores 3 True False, 5 True False y 9 True False lo confirman. Una condición así nunca decide nada, y la tabla lo deja en evidencia.
La "o" que en la conversación es exclusiva
"¿Quieres café o té?" casi nunca quiere decir "pueden ser los dos". El or de Python es inclusivo y acepta los dos. Quien necesita el "uno u otro" usa !=, como en la sección de arriba, y comprueba en la tabla que la fila en que los dos son verdaderos queda falsa.
La tabla con filas faltantes
Con tres variables, la tabla necesita ocho filas, y es común entregar seis, porque la persona armó las combinaciones de cabeza y se saltó dos. La defensa es contar en binario, como en la figura del comienzo, y comprobar la cuenta con 2 ** n.
El not distribuido a medias
La trampa de la figura de De Morgan aparece en casi todo código escrito con apuro: se niega cada término, pero el and o el or queda como estaba. El programa corre, no da error, y la condición vale para la mitad de los casos. Es el tipo de defecto que solo aparece cuando alguien prueba justamente la combinación que la versión equivocada trata mal.
El caso en que nada vale
Para cada decisión con varias salidas, la pregunta que la tabla hace sin que la llamen es: ¿y cuando ninguna de las condiciones sea verdadera? El robot del artículo anterior, que se desvía a la izquierda, a la derecha o se detiene, solo es seguro porque alguien dibujó la fila en que los dos lados están bloqueados. Quien arma la tabla ve esa fila. Quien escribe solo los casos "felices" ni sabe que existe.
Ejercicio: el portón del garaje
Un portón se abre cuando alguien aprieta el control o el sensor detecta un auto, pero nunca si hay un obstáculo adelante. Tres personas escribieron la condición de maneras distintas:
- A:
(controle or carro) and not obstaculo - B:
(controle and not obstaculo) or (carro and not obstaculo) - C:
controle or carro and not obstaculo
Antes de ejecutar, apuesta cuáles son equivalentes. Después arma la tabla y cuenta las filas en que la A y la C discrepan.
dif = 0
for controle in (False, True):
for carro in (False, True):
for obstaculo in (False, True):
a = (controle or carro) and not obstaculo
b = (controle and not obstaculo) or (carro and not obstaculo)
c = controle or carro and not obstaculo
dif += a != c
print(controle, carro, obstaculo, a, b, c, a == b)
print(dif)La salida tiene ocho líneas y termina en 2. En la última columna, a == b es True en las ocho: la B es solo la A con el not obstaculo distribuido, una propiedad llamada distributiva. La C es otra historia. Sin los paréntesis, el and se resuelve antes, y la expresión pasa a ser controle or (carro and not obstaculo). El contador dif cierra en 2, en las filas en que el control está apretado y hay obstáculo: la A mantiene el portón cerrado, y la C lo abre. Es el tipo de error que, en un garaje de verdad, atrapa un auto o a una persona. Fíjate también en el dif += a != c: el resultado de la comparación, un booleano, se suma como 1 o 0, y el contador cuenta las discrepancias.
Preguntas frecuentes
¿Cuántas filas tiene una tabla de verdad?
2 elevado al número de variables. Con 3 variables son 8 filas y con 4 son 16. Cada variable nueva duplica la tabla.
¿Necesito armar la tabla a mano?
Para dos variables, a mano es rápido. A partir de tres, conviene dejar que Python la genere con for anidados, como en los ejemplos de arriba, porque el computador no olvida filas.
¿Puedo usar 0 y 1 en lugar de False y True?
En las operaciones, sí: el cero cuenta como falso y cualquier otro número como verdadero. Pero el and y el or devuelven uno de los operandos, y la tabla va a mostrar números donde esperabas True y False. Prefiere los booleanos.
¿Por qué el str() dentro de la f-string?
Porque un booleano formateado con ancho, como f"{a:<6}", aparece como 1 o 0. Convertido con str(a), aparece como True o False.
¿Python tiene un operador xor?
No con ese nombre. Para dos booleanos, a != b hace el "o exclusivo". El ^ es un operador bit a bit y puede devolver número en lugar de booleano.
Para continuar
La ruta Lógica con Python tiene desafíos que ponen estas condiciones a funcionar. El Mayor de edad y el Intervalo de 10 a 20 exigen comparaciones y rangos, el Aprobación en la escuela junta dos condiciones con and, y el Nivel de acceso pide una regla con varios casos, en los que armar la tabla antes ayuda mucho. Si alguna comparación u operador quedó suelto, el artículo sobre operadores relacionales y lógicos es el punto de partida de esta serie.
El próximo tema lleva estas condiciones al programa: la estructura if y else, que elige un camino de acuerdo con el valor de una expresión como las que acabamos de probar.