【问题标题】:Why is "if not someobj:" better than "if someobj == None:" in Python?为什么 Python 中的“if not someobj:”比“if someobj == None:”更好?
【发布时间】:2010-09-11 04:09:01
【问题描述】:

我见过几个这样的代码示例:

if not someobj:
    #do something

但我想知道为什么不这样做:

if someobj == None:
    #do something

有什么不同吗?一个比另一个有优势吗?

【问题讨论】:

  • 通常 'someobj is None' 优于 'someobj == None'
  • 如果 X 不是 None,那么伪代码呢?还是X != None

标签: python


【解决方案1】:

在第一个测试中,Python 尝试将对象转换为 bool 值(如果它还不是一个值)。粗略地说,我们在问对象:你有意义吗?这是使用以下算法完成的:

  1. 如果对象具有__nonzero__ 特殊方法(如数字内置函数intfloat),它会调用此方法。它必须要么返回一个 bool 值,然后直接使用,要么返回一个 int 值,如果等于 0,则被视为 False

  2. 否则,如果对象具有__len__ 特殊方法(如容器内置,listdictsettuple,...),它调用这个方法,考虑容器False,如果它是空的(长度为零)。

  3. 否则,对象被视为True,除非它是None,在这种情况下,它被视为False

在第二个测试中,比较对象与None 的相等性。在这里,我们询问对象,“你是否等于这个其他值?”这是使用以下算法完成的:

  1. 如果对象具有__eq__方法,则调用该方法,然后将返回值转换为bool值并用于确定if的结果。

  2. 否则,如果对象有__cmp__ 方法,则调用它。这个函数必须返回一个int,表示两个对象的顺序(-1如果self < other0如果self == other+1如果self > other)。

  3. 否则,将比较对象的身份(即它们引用同一对象,可以通过is 运算符进行测试)。

使用is 运算符可以进行另一个测试。 我们会问对象,“你是这个特定的对象吗?”

一般来说,我建议使用非数值的第一个测试,当您想要比较相同性质的对象(两个字符串,两个数字,...)时使用相等性测试并检查仅在使用标记值时标识(例如,None 表示未为成员字段初始化,或者在使用 getattr__getitem__ 方法时)。

总而言之,我们有:

>>> class A(object):
...    def __repr__(self):
...        return 'A()'
...    def __nonzero__(self):
...        return False

>>> class B(object):
...    def __repr__(self):
...        return 'B()'
...    def __len__(self):
...        return 0

>>> class C(object):
...    def __repr__(self):
...        return 'C()'
...    def __cmp__(self, other):
...        return 0

>>> class D(object):
...    def __repr__(self):
...        return 'D()'
...    def __eq__(self, other):
...        return True

>>> for obj in ['', (), [], {}, 0, 0., A(), B(), C(), D(), None]:
...     print '%4s: bool(obj) -> %5s, obj == None -> %5s, obj is None -> %5s' % \
...         (repr(obj), bool(obj), obj == None, obj is None)
  '': bool(obj) -> False, obj == None -> False, obj is None -> False
  (): bool(obj) -> False, obj == None -> False, obj is None -> False
  []: bool(obj) -> False, obj == None -> False, obj is None -> False
  {}: bool(obj) -> False, obj == None -> False, obj is None -> False
   0: bool(obj) -> False, obj == None -> False, obj is None -> False
 0.0: bool(obj) -> False, obj == None -> False, obj is None -> False
 A(): bool(obj) -> False, obj == None -> False, obj is None -> False
 B(): bool(obj) -> False, obj == None -> False, obj is None -> False
 C(): bool(obj) ->  True, obj == None ->  True, obj is None -> False
 D(): bool(obj) ->  True, obj == None ->  True, obj is None -> False
None: bool(obj) -> False, obj == None ->  True, obj is None ->  True

【讨论】:

  • 虽然技术上是正确的,但这并不能说明元组、列表、字典、str、unicode、int、float 等具有非零。依赖内置类型的真值比依赖自定义 nonzero 方法更常见。
【解决方案2】:

这些实际上都是不好的做法。曾几何时,人们认为随意将 None 和 False 视为相似是可以的。但是,从 Python 2.2 开始,这不是最佳策略。

首先,当您进行if xif not x 类型的测试时,Python 必须将x 隐式转换为布尔值。 bool 函数的规则描述了大量错误的事物;其他一切都是真实的。如果 x 的值一开始就不是正确的布尔值,那么这种隐式转换并不是最清晰的表达方式。

在 Python 2.2 之前,没有 bool 函数,所以更不清晰。

其次,您不应该真正使用== None 进行测试。你应该使用is Noneis not None

请参阅 PEP 8,Style Guide for Python Code

- Comparisons to singletons like None should always be done with
  'is' or 'is not', never the equality operators.

  Also, beware of writing "if x" when you really mean "if x is not None"
  -- e.g. when testing whether a variable or argument that defaults to
  None was set to some other value.  The other value might have a type
  (such as a container) that could be false in a boolean context!

有多少个单例?五:NoneTrueFalseNotImplementedEllipsis。因为你真的不太可能使用NotImplementedEllipsis,而且你永远不会说if x is True(因为简单的if x 更清楚),你只会测试None

【讨论】:

  • 第二种形式绝对是不错的做法。 PEP 8 建议使用 if x twice。首先是序列(而不是使用 len),然后是 True 和 False(而不是使用 is)。几乎我见过的所有 Python 代码都使用 if x 和 if not x。
【解决方案3】:

因为None 不是唯一被认为是错误的东西。

if not False:
    print "False is false."
if not 0:
    print "0 is false."
if not []:
    print "An empty list is false."
if not ():
    print "An empty tuple is false."
if not {}:
    print "An empty dict is false."
if not "":
    print "An empty string is false."

False0()[]{}"" 都与 None 不同,所以你的两个代码 sn-ps 不是等价的。

此外,请考虑以下事项:

>>> False == 0
True
>>> False == ()
False

if object: 不是相等检查。 0()[]None{} 彼此不同,但它们都 评估 为 False。

这就是短路表达式背后的“魔法”,例如:

foo = bar and spam or eggs

简写为:

if bar:
    foo = spam
else:
    foo = eggs

虽然你真的应该写:

foo = spam if bar else egg

【讨论】:

  • 关于你的最后一个问题,它们是等价的。
  • 两者都不正确,因为“”是假的。第二个应该读为'(“”,)或(“s”,)'。无论如何,现代版本的 python 有一个适当的三元运算符。应该禁止这种容易出错的黑客攻击。
【解决方案4】:

PEP 8 -- Style Guide for Python Code 建议使用 isis not 如果您正在测试 None-ness

- Comparisons to singletons like None should always be done with
  'is' or 'is not', never the equality operators.

另一方面,如果您要测试的不仅仅是无,您应该使用布尔运算符。

【讨论】:

    【解决方案5】:

    如果你问

    if not spam:
        print "Sorry. No SPAM."
    

    spam__nonzero__ 方法被调用。来自 Python 手册:

    __nonzero__(自我) 调用实现真值检测,内置操作bool();应该返回 False 或 True,或它们的整数等价物 0 或 1。如果未定义此方法,则调用 __len__()(如果已定义)(见下文)。如果一个类既没有定义 __len__() 也没有定义 __nonzero__(),它的所有实例都被认为是真的。

    如果你问

    if spam == None:
        print "Sorry. No SPAM here either."
    

    spam__eq__ 方法使用参数 None 调用。

    有关自定义可能性的更多信息,请查看 Python 文档https://docs.python.org/reference/datamodel.html#basic-customization

    【讨论】:

      【解决方案6】:

      这两个比较有不同的目的。前者检查某物的布尔值,后者检查无值的身份。

      【讨论】:

      • 绝对正确,第二个检查 -equality- 是否为 None 值..."someobj is None" 检查身份。
      【解决方案7】:

      第一个例子更短,看起来更好。根据其他帖子,您选择的内容还取决于您真正想通过比较做什么。

      【讨论】:

        【解决方案8】:

        答案是“视情况而定”。

        如果我认为 0、""、[] 和 False(列表不详尽)在这种情况下等同于 None,我将使用第一个示例。

        【讨论】:

          【解决方案9】:

          就我个人而言,我选择了跨语言的一致方法:只有当 var 被声明为布尔值(或定义为布尔值,在 C 中我们没有特定类型)时,我才使用 if (var)(或等效方法)。我什至在这些变量前面加上 b(所以实际上是 bVar),以确保我不会在这里意外使用其他类型。
          我真的不喜欢隐式转换为布尔值,当有许多复杂的规则时更是如此。

          当然,人们会不同意。有些人走得更远,我在我工作的 Java 代码中看到 if (bVar == true)(对我来说太多余了!),其他人喜欢过于紧凑的语法,使用 while (line = getNextLine())(对我来说太模棱两可了)。

          【讨论】:

            猜你喜欢
            • 2011-02-12
            • 2015-06-15
            • 2010-10-04
            • 2011-03-14
            • 1970-01-01
            • 1970-01-01
            • 2013-08-10
            • 1970-01-01
            • 2015-09-10
            相关资源
            最近更新 更多