Есть ли какие-либо преимущества в использовании законов де Моргана в python?

Я использую pycharm и несколько раз при использовании if утверждения я видел предложение изменить утверждение, используя законы де Моргана, например, со следующим if о себе:

if new_odds > 10 and new_odds <= 30:

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

для меня это делает его менее читаемым, так есть ли какие-либо преимущества в использовании законов де Моргана или это строго личный выбор?

5 ответов


законы де Моргана указано следующее:

"не (A и B)" и "(не А) или (не б)"

и

"не (А или Б)" - это то же самое, что"(не А) и (не б)"

, которые используются для преобразования логики между альтернативными формами. Поэтому, хотя трансформация, которую вы сделали, соответствует законам де Моргана, ее стало труднее читать. Как другие предложили многое проще 10 < new_odds <= 30 было бы гораздо более читаемым, очень важно понимать, что это сокращение для 10 < new_odds and new_odds <= 30, потому что исходя из этого вы можете сделать логику типа:

10 < new_odds <= 30 != max_odds | default_condition

, который расширяется:

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

Итак, имея в виду этот синтаксический сахар, давайте посмотрим на другой пример:

мы рассмотрим надуманный пример в простой ролевой игре, где мы смотрим на навык, который мы будем называть "голландское мужество". Предпосылка этого нападения это то, что вы можете бонус, если у вас максимальное здоровье, и либо ваша броня, либо ваш рейтинг атаки недостаточны для атаки врага. Мне нужно знать, когда это правило не применяется.

написание этого у нас есть 4 условия:

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

используя законы де Моргана, я могу разложить это так:

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)

Ну, теперь я могу разложить это дальше...

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

здесь мы предполагаем противоположное == max_health is < max_health в противном случае его не максимальный.

хотя надуманный, это показывает нам, что законы де Моргана-это инструмент, позволяющий нам переписать логику. Улучшена ли эта логика или нет, зависит от программиста, но цель состоит в том, чтобы иметь возможность создавать более простые конструкции, которые, во-первых, более читабельны, а во-вторых, и, надеюсь, требуют меньше инструкций и, следовательно, быстрее.


в Python это почти всегда лучше быть ясным, чем немного быстрее (если это даже было бы, в чем я сомневаюсь).

Я предпочитаю этот еще проще о себе:

if 10 < new_odds <= 30:

В некоторых случаях, это делает вещи более подробный и четкий. Однако в случаях, когда у вас уже есть куча nots разбросаны вокруг, или где вы сравниваете вещи в менее чем естественном порядке, demorganing может уменьшить количество nots или обратный порядок неравных сравнений. Например:

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)

(второй спорный... но это именно то, что вы ожидаете от вопроса читаемости и стиля.)

так, если это делает ваш код более читаемый, сделайте это; в противном случае не делайте.


здесь are нескольких языков (логика, ограничение-удовлетворение, реляционной и т. д.) где это не так, потому что применение not значение не просто переворачивает True и False, но генерирует обратный, возможно, намного медленнее или, возможно, даже неопределенный запрос.

но это не относится к Python или большинству других языков "общего назначения".


учитывая приведенные ниже результаты, кажется, что более сложное/менее читаемое выражение также является самым медленным. В любом случае читаемость большую часть времени более ценна в 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

это может быть полезно иногда для удобочитаемости, но это то же самое при выполнении. Например:

not(A AND B) === not(A) OR not(B)

if not a() and not b():
if not(a() or b()):

исполнение будет таким же с a() True или False.

для вашего примера лучшим решением остается использовать мощь синтаксиса Python и написать:

if 10 < new_odds <= 30:

этот синтаксис чрезвычайно полезен для проверки того, что числовое значение находится в определенном диапазоне.