为什么在Python中“ if not someobj:”优于“ if someobj == None:”?


127

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

if not someobj:
    #do something

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

if someobj == None:
    #do something

有什么区别吗?一个人比另一个人有优势吗?


13
一般“someObj中是无”优选“someObj中==无”
亚伦Maenpaa

如果X不为None,伪代码怎么办?还是X != None
查理·帕克

这个问题比其他问题更冗长...荣誉...
alamin

Answers:


187

在第一个测试中,Python尝试将对象转换为bool值(如果尚未转换为值)。粗略地,我们在问对象:您有意义吗?这可以通过以下算法完成:

  1. 如果对象具有__nonzero__特殊方法(数字内置intfloat),则调用此方法。它必须要么返回bool其随后直接使用值,或者int被认为是值False如果等于零。

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

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

在第二个测试中,将对象与进行相等性比较None。在这里,我们要问对象“您等于这个其他值吗?” 这可以通过以下算法完成:

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

  2. 否则,如果对象具有__cmp__方法,则将其调用。此函数必须返回一个int指示两个对象的顺序的指示符(-1if self < other0if self == other+1if 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

4
尽管从技术上讲是正确的,但这并不能说明元组,列表,字典,str,str,unicode,int,float等具有 非零值。依赖于内置类型的真值比依赖自定义非零方法更为常见。
ddaa

50

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

首先,当您进行测试if xif not x某种测试时,Python必须隐式转换x为布尔值。该bool函数的规则描述了许多错误的事物。其他一切都是真的。如果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。由于您实际上不太可能使用NotImplementedEllipsis,因此您永远不会说if x is True(因为if x它很清楚),因此您只会进行测试None


3
第二种形式绝对不是坏习惯。PEP 8建议使用if x 两次。首先是序列(而不是使用len),然后是True和False(而不是使用is)。我见过的几乎所有Python代码都使用x和x。
Antti Rasinen

35

因为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,所以你的两段代码是等价的。

此外,请考虑以下事项:

>>> 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具有适当的三元运算符。这种容易出错的骇客应该被驱逐掉。
ddaa


3

如果你问

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

垃圾邮件__nonzero__方法被调用。从Python手册中:

__nonzero__self)调用以实现真值测试,并内置操作bool(); 应该返回False或True,或者它们的等效整数0或1。如果未定义此方法,则调用__len __()(如果已定义)(请参见下文)。如果一个类未定义__len __()或__nonzero __(),则其所有实例均被视为true。

如果你问

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

使用参数None调用垃圾邮件__eq__方法。

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


2

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


1
绝对正确,第二个检查带有-None值的-equality -...“ someobj is None”检查身份。
马修·特雷弗

0

对于一个例子,第一个例子更短,看起来更好。与其他帖子一样,您选择的内容还取决于您真正想要进行比较的内容。



0

就个人而言,我选择了一种跨语言的一致方法:if (var)仅当var声明为布尔值时(或如此定义,在C中,我们没有特定的类型),我才做(或等效)。我什至给这些变量加上前缀bbVar实际上是这样),以确保我不会在这里偶然使用其他类型。
我真的不喜欢将隐式强制转换为布尔值,甚至在有许多复杂规则时也是如此。

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.