Saltar al contenido
IDE libre

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.

FastTeacherPara profesores

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
Conocer FastTeacher

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.

A la izquierda, una tabla con las variables a, b y c y ocho filas numeradas de 0 a 7, en la que las columnas alternan False y True como en un contador binario: c alterna en cada fila, b cada dos y a cada cuatro. A la derecha, barras que duplican su tamaño: 2 filas para una variable, 4 para dos, 8 para tres, 16 para cuatro y 32 para cinco. Una franja avisa que diez variables dan 1024 filas.
Una tabla de verdad lista todas las combinaciones contando en binario, y el número de filas se duplica con cada variable nueva: 2 elevado a n.

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.

El IDE de PyPlaca con los dos bucles for en el editor y, en el Serial, la tabla de verdad impresa en columnas alineadas: el encabezado a, b, and, or y las cuatro combinaciones de False y True con los resultados de and y or.
Dos bucles for generan las cuatro combinaciones y la f-string alinea las columnas: la tabla de verdad de and y or sale lista en el Serial.

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.

Tabla con las variables a y b y cuatro filas. La columna not (a and b) y la columna (not a) or (not b) tienen los mismos valores: True, True, True, False. La columna not (a or b) y la columna (not a) and (not b) también son iguales: True, False, False, False. Una franja roja avisa que not (a or b) no es igual a (not a) or (not b), y que las dos columnas difieren en las filas 2 y 3.
Las leyes de De Morgan comprobadas en una tabla: negar un and o un or equivale a negar cada parte e intercambiar el operador.
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.

FastMindAsistente de IA local

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
Descargar gratis para Windows

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.

Tabla con las variables seco, agua y chuva y la columna irrigar, en ocho filas. Solo la séptima fila, con seco True, agua True y chuva False, tiene irrigar True y aparece resaltada. A la derecha, la regla irrigar igual a seco and agua and not chuva y el aviso de que, con una variable manual agregada por or, la tabla pasa a tener 16 filas, 9 de ellas verdaderas.
La tabla de la bomba de riego: de las ocho combinaciones, solo una enciende la bomba. Agregar el modo manual duplica la tabla a 16 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.

Para practicar en PyPlaca

Rutas

Lecciones

Fichas de componentes

FastMindAsistente de IA local

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
Descargar gratis para Windows

Plan Esencial gratis para siempre

FastTeacherPara profesores

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
Conocer FastTeacher

Versión Community gratis · Windows

FastMindAsistente de IA local

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
Descargar gratis para Windows

Plan Esencial gratis para siempre