为什么在Python中,“if not someobj:”比“if someobj == None:”更好?

147

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

if not someobj:
    #do something

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

if someobj == None:
    #do something

有区别吗?它们之间有优劣之分吗?


15
通常使用“someobj is None”比“someobj == None”更好。 - Aaron Maenpaa
如果X不是None,伪代码怎么样?或者X != None呢? - Charlie Parker
9个回答

210

在第一个测试中,如果对象不是bool值,则Python尝试将对象转换为bool值。大致上,我们在问对象:你是否有意义?这是使用以下算法完成的:

  1. 如果对象具有__nonzero__特殊方法(如数字内置型intfloat),则调用此方法。它必须返回一个bool值,该值直接使用,或者返回一个int值,如果为零,则被视为False

  2. 否则,如果对象具有__len__特殊方法(如容器内置型listdictsettuple等),则调用此方法,并且如果容器为空(长度为零)则被视为False

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

在第二个测试中,将对象与None进行比较。在这里,我们要问对象:"你是否等于另一个值?" 这是使用以下算法完成的:

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

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

  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

5
尽管从技术上讲是正确的,但这并没有解释元组、列表、字典、字符串、Unicode、整数、浮点数等具有 __nonzero__。通常更常依赖于内置类型的真值而不是自定义的 nonzero 方法。 - ddaa

56

这实际上是两种不好的做法。 曾经有一段时间,轻松地将None和False视为相似被认为是可以接受的。 然而,自从Python 2.2以来,这已经不是最佳策略。

首先,在进行if xif not x这类测试时,Python必须隐式转换x为布尔值。 bool函数的规则描述了一系列False值;其他所有值均为True。 如果x的值一开始就不是布尔值,这种隐式转换并不是最清晰的表达方式。

在Python 2.2之前,没有bool函数,因此更不清楚。

第二,您不应该使用== None进行测试。 您应该使用is Noneis not None

请参阅PEP 8,Python代码风格指南

- 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。由于你很少使用NotImplemented或者Ellipsis,而且你永远不会说if x is True(因为简单地使用if x更加清晰),因此你只会测试None

3
第二种形式并不是不好的实践。PEP 8 建议使用 if x 两次。首先用于序列(而不是使用 len),然后用于 True 和 False(而不是使用 is)。我看到的几乎所有 Python 代码都使用 if x 和 if not x。 - Antti Rasinen

41

因为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."

False, 0, (), [], {}""None 不同,因此你的两个代码片段是不相等的

而且,请考虑以下内容:

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

关于你上一个问题,它们是等价的。 - Sylvain Defresne
两者都是错误的,因为“”是False。第二个应该写成'("",)或("s",)'。无论如何,现代版本的Python都有一个适当的三元运算符。这种容易出错的hack应该被禁止。 - ddaa

6

PEP 8 -- Python代码风格指南建议在测试是否为None时使用isis not

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

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


3
如果您询问
if not spam:
    print "Sorry. No SPAM."
< p >调用spam__nonzero__方法,这是来自Python手册的说明:

__nonzero__(self) 被用于实现真值测试和内置操作bool()。应该返回False或True,或它们的整数等价物0或1。当未定义此方法时,如果定义了__len__()(见下文),则调用该方法。如果一个类既不定义__len__()也不定义__nonzero__(),则其所有实例都被视为True。

如果你询问:

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

spam__eq__方法会被调用,传入参数None

如需了解更多自定义可能性,请参阅Python文档:https://docs.python.org/reference/datamodel.html#basic-customization


2

这两种比较有不同的目的。前者检查某个东西的布尔值,而后者检查它是否与None值相同。


1
要绝对正确,第二个检查与None值的-equality-…“someobj is None”检查身份。 - Matthew Trevor

0

首先,第一个示例更短,看起来更好。根据其他帖子所述,您选择的方式也取决于您想要使用比较做什么。


0
答案是“这要看情况而定”。
在这种情况下,如果我认为0、""、[]和False(列表不详尽)等同于None,则使用第一个示例。

0
个人而言,我选择跨语言使用一致的方法:只有在变量被声明为布尔值(或在C中定义为布尔类型)时,才会使用if(var)(或等效语句)。我甚至会在这些变量前加上一个b前缀(实际上应该是bVar),以确保我不会意外地在此处使用其他类型。
我不太喜欢将隐式转换为布尔值,尤其是当存在众多复杂规则时。

当然,人们会有不同的看法。有些人会更进一步,我在我的工作中看到Java代码中的if(bVar == true)(对我来说过于冗余!),而其他人则过于喜欢紧凑的语法,如while(line = getNextLine())(对我来说过于模糊)。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接