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

时间:2020-03-06 14:25:17  来源:igfitidea点击:

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

if not someobj:
    #do something

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

if someobj == None:
    #do something

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

解决方案

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

  • 如果对象具有__nonzero__特殊方法(如数字内置int和float),则将调用此方法。它必须返回一个可以直接使用的布尔值,或者一个等于零的被认为是False的int值。
  • 否则,如果对象具有__len__特殊方法(如容器内置,list,dict,set,tuple等),则考虑容器使用该方法如果为空(长度为零),则为False。
  • 否则,除非该对象为"无",否则该对象被视为"真",在这种情况下,它被视为"假"。

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

  • 如果对象具有__eq__方法,则将其调用,然后将返回值转换为bool值,并用于确定'if'的结果。
  • 否则,如果对象具有__cmp__方法,则将其调用。此函数必须返回一个" int",指示两个对象的顺序(如果" self <other",则为" -1",如果" self == other",则为" 0",如果" self> other",则为" +1")。
  • 否则,将比较对象的身份(即,它们引用的是同一对象,可以通过" is"运算符进行测试)。

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

通常,我建议对非数字值使用第一个测试,如果要比较具有相同性质(两个字符串,两个数字,...)的对象,并且仅在以下情况下检查身份,请使用该测试是否相等使用哨兵值(例如,"无"表示未为成员字段初始化,或者使用" 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

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

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

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

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

如果要测试无度,则建议使用PEP 8 -Python代码样式指南,是否使用

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

另一方面,如果要测试的不只是无度,则应使用布尔运算符。

答案是"取决于"。

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

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

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

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

首先,当我们执行" if x"或者" if not x"类型的测试时,Python必须将" x"隐式转换为布尔值。 "布尔"功能的规则描述了许多错误的事物。其他一切都是真的。如果x的值开头不是正确的布尔值,那么这种隐式转换实际上并不是最清晰的表达方式。

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

其次,我们不应该真正使用" == None"进行测试。我们应该使用is Nonenot 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!

有多少个单身人士?五:"无","真实","错误","未实现"和"省略号"。由于我们实际上不太可能使用NotImplemented或者Ellipsis,而且我们永远不会说if x is True'(因为只是if x更清楚),因此我们只会测试None`。

如果你问

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

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

__nonzero__(self)
    Called to implement truth value testing, and the built-in operation bool(); should return False or True, or their integer equivalents 0 or 1. When this method is not defined, __len__() is called, if it is defined (see below). If a class defines neither __len__() nor __nonzero__(), all its instances are considered true.

如果你问

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

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

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