¿Hay alguna ventaja al usar las leyes de De Morgan en python?

Utilizo pycharm y unas cuantas veces al usar sentencias if he visto la sugerencia de cambiar la sentencia utilizando las leyes de De Morgan, como la siguiente statement if :

 if new_odds > 10 and new_odds  10) or not (new_odds <= 20)): 

Para mí, lo hace menos legible, así que, ¿hay alguna ventaja en el uso de las leyes de De Morgan o es una elección estrictamente personal?

Las leyes de De Morgan establecen que:

“no (A y B)” es lo mismo que “(no A) o (no B)”

y también,

“no (A o B)” es lo mismo que “(no A) y (no B)”

Que se utilizan para transformar la lógica entre formas alternativas. Entonces, mientras la transformación que has hecho se ajusta a las leyes de De Morgan, se ha vuelto más difícil de leer. Como otros han sugerido, los 10 < new_odds <= 30 mucho más simples serían mucho más legibles, sin embargo , es muy importante entender que esto es corto para 10 < new_odds and new_odds <= 30 porque, a partir de esto, puede hacer una lógica como:

 10 < new_odds <= 30 != max_odds | default_condition 

Que se expande a:

 10 < new_odds and new_odds <= 30 and 30 != max_odds and max_odds | default_condition 

Entonces, con ese azúcar sintáctico en mente, veamos otro ejemplo:

Consideraremos un ejemplo artificial en un juego de rol simple en el que observamos una habilidad que llamaremos "coraje holandés". La premisa de este ataque es que puedes obtener una bonificación si tienes la salud máxima, y ​​tu armadura o tu clasificación de ataque no son suficientes para atacar a un enemigo. Necesito saber cuándo no se aplicará esta regla.

Escribiendo esto tenemos 4 condiciones:

 A = health == max_health B = armor > enemy.attack C = attack > enemy.defense no_bonus = not(A and not(B and C)) 

Usando las leyes de De Morgan puedo descomponer esto así:

 not(A and not(B and C)) not(A) or not(B and C) not(A) or not(B) or not(C) not(health == max_health) or not(armor > enemy.attack) or (attack > enemy.defense) 

Bueno, ahora puedo descomponer esto aún más ...

 health < max_meath or armor < enemy.attack < attack > enemy.defense 

Aquí asumimos que lo contrario de == max_health es < max_health contrario no es el máximo.

Aunque ideado, esto nos muestra que las leyes de De Morgan son una herramienta que nos permite reescribir la lógica . Si esta lógica se mejora o no, depende del progtwigdor, pero la intención es poder producir construcciones más simples, que primero son más legibles y, en segundo lugar, y con suerte requieren menos instrucciones y, por lo tanto, son más rápidas.

En Python, casi siempre es mejor ser claro que un poco más rápido (si eso hubiera sido así, lo cual dudo).

Prefiero esta afirmación aún más simple :

 if 10 < new_odds <= 30: 

En algunos casos, hace que las cosas sean más detalladas y legibles. Sin embargo, en los casos en los que ya tienes un montón de not rociados, o cuando estás comparando cosas en un orden menos que natural, la desorganización puede reducir el número de not s o revertir el orden de las comparaciones desiguales. Por ejemplo:

 if not foo() and not bar(): if not(foo() or bar()): if new_score <= high_score and new_level <= high_level: if not (new_score > high_score or new_level > high_level) 

(El segundo es discutible … pero eso es exactamente lo que cabría esperar de una cuestión de legibilidad y estilo).

Entonces, si hace que tu código sea más legible, hazlo; de lo contrario, no lo hagas


Hay un puñado de lenguajes (lógica, constricción-satisfacción, relacional, etc.) donde esto no es cierto, porque aplicar un not a un valor no es solo cambiar de Verdadero y Falso, sino generar una conversación, posiblemente mucho más lenta, o posiblemente Incluso indeterminado, consulta.

Pero ese no es el caso con Python, o la mayoría de los otros lenguajes de “propósito general”.

Dados los resultados a continuación, parece que la expresión más compleja / menos legible también es la más lenta. En cualquier caso, la legibilidad es la mayor parte del tiempo más valiosa en python.

 In [1]: new_odds = 0 In [2]: %timeit if new_odds > 10 and new_odds <= 30: pass 10000000 loops, best of 3: 24.3 ns per loop In [3]: %timeit if not (not (new_odds > 10) or not (new_odds <= 20)): pass 10000000 loops, best of 3: 48.6 ns per loop In [4]: %timeit if 10 < new_odds <= 30:pass 10000000 loops, best of 3: 43.4 ns per loop In [5]: new_odds = 20 In [6]: %timeit if new_odds > 10 and new_odds <= 30: pass 10000000 loops, best of 3: 57.7 ns per loop In [7]: %timeit if not (not (new_odds > 10) or not (new_odds <= 20)): pass 10000000 loops, best of 3: 102 ns per loop In [8]: %timeit if 10 < new_odds <= 30:pass 10000000 loops, best of 3: 52.7 ns per loop 

Puede ser útil a veces para facilitar la lectura, pero es lo mismo en la ejecución. Por ejemplo con:

 not(A AND B) === not(A) OR not(B) if not a() and not b(): if not(a() or b()): 

La ejecución será la misma con a() Verdadero o Falso.

Para su ejemplo, la mejor solución sigue siendo usar la potencia de la syntax de Python y escribir:

 if 10 < new_odds <= 30: 

Esta syntax es extremadamente útil para verificar que un valor numérico esté en un rango particular.