为什么 PyCharm 建议我更改声明以使用德摩根定律?

问题描述 投票:0回答:6

我使用 PyCharm,并且有几次使用

if
语句时,我看到了使用德摩根定律更改语句的建议,例如使用以下
if
语句:

if new_odds > 10 and new_odds <= 30:

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

对我来说,它的可读性较差,那么使用德摩根定律有什么优势吗?还是说这完全是个人选择?

python pycharm demorgans-law
6个回答
17
投票

德摩根定律指出:

“非(A 和 B)”与“(非 A)或(非 B)”相同

还有,

“非(A 或 B)”与“(非 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
的相反数是
< max_health
,否则它不是最大值。

虽然是人为的,但这向我们表明德摩根定律是使我们能够重写逻辑的工具。这种逻辑是否得到改进取决于程序员,但目的是能够生成更简单的结构,首先更具可读性,其次希望需要更少的指令,从而更快。


7
投票

在Python中,清晰几乎总是比稍微快一点要好(如果那样的话,我对此表示怀疑)。

我更喜欢这个更简单的说法:

if 10 < new_odds <= 30:

5
投票

在某些情况下,它会使事情变得更加冗长和可读。但是,如果您已经散布了一堆

not
,或者您以不自然的顺序比较事物,则分解可以减少
not
的数量或颠倒顺序。不平等的比较。例如:

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)

(第二个是有争议的……但这正是您从可读性和风格问题中所期望的。)

因此,如果它使您的代码更具可读性,那就这样做;否则,不要。


少数语言(逻辑、约束满足、关系等)情况并非如此,因为将 not

 应用于一个值不仅仅是翻转 True 和 False,还可能生成相反的结果查询速度慢得多,甚至可能是不确定的。

但 Python 或大多数其他“通用”语言的情况并非如此。


2
投票
鉴于下面的结果,似乎更复杂/可读性较差的表达式也是最慢的。无论如何,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
    

1
投票
有时它对于可读性很有用,但在执行时也是一样的。例如:

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:

此语法对于检查数值是否在特定范围内非常有用。


0
投票
单独应用德摩根定律是没有用的,因为执行过程是完全相同的,包括任何副作用。任何观察到的差异都不是来自德摩根定律本身,而是来自其他因素(例如,用

not not A

 替换 
A
)。感谢 abarnert,他已经对此进行了
评论

考虑两个表达式,我们将其分配给

expr1

expr2
:

expr1 = not (A or B) expr2 = (not A) and (not B)
它们的执行方式完全相同:

    首先,调用
  • bool(A)
    。在 
    expr1
     中,它被称为是因为这就是 
    or
     的工作原理。在 
    expr2
     中,它被称为是因为这就是 
    not
     的工作原理。请参阅有关此主题的
    文档
  • 如果
  • bool(A)
    True
    ,则分配 
    True
     值并完成评估。
  • 如果
  • bool(A)
    False
    ,则将 
    not bool(B)
     指定为结果。请注意,
    bool(B)
     会在 
    expr1
    expr2
     中被调用,因为 
    B
     最终位于 
    not
     内部。
“I-dentical”(引自 1992 年电影《My Cousin Vinny》)。

© www.soinside.com 2019 - 2024. All rights reserved.