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

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

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

if not someobj:
    #do something

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

if someobj == None:
    #do something

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

I’ve seen several examples of code like this:

if not someobj:
    #do something

But I’m wondering why not doing:

if someobj == None:
    #do something

Is there any difference? Does one have an advantage over the other?


回答 0

在第一个测试中,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

In the first test, Python try to convert the object to a bool value if it is not already one. Roughly, we are asking the object : are you meaningful or not ? This is done using the following algorithm :

  1. If the object has a __nonzero__ special method (as do numeric built-ins, int and float), it calls this method. It must either return a bool value which is then directly used, or an int value that is considered False if equal to zero.

  2. Otherwise, if the object has a __len__ special method (as do container built-ins, list, dict, set, tuple, …), it calls this method, considering a container False if it is empty (length is zero).

  3. Otherwise, the object is considered True unless it is None in which case, it is considered False.

In the second test, the object is compared for equality to None. Here, we are asking the object, “Are you equal to this other value?” This is done using the following algorithm :

  1. If the object has a __eq__ method, it is called, and the return value is then converted to a boolvalue and used to determine the outcome of the if.

  2. Otherwise, if the object has a __cmp__ method, it is called. This function must return an int indicating the order of the two object (-1 if self < other, 0 if self == other, +1 if self > other).

  3. Otherwise, the object are compared for identity (ie. they are reference to the same object, as can be tested by the is operator).

There is another test possible using the is operator. We would be asking the object, “Are you this particular object?”

Generally, I would recommend to use the first test with non-numerical values, to use the test for equality when you want to compare objects of the same nature (two strings, two numbers, …) and to check for identity only when using sentinel values (None meaning not initialized for a member field for exemple, or when using the getattr or the __getitem__ methods).

To summarize, we have :

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

回答 1

这些实际上都是不好的做法。曾几何时,随意将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

These are actually both poor practices. Once upon a time, it was considered OK to casually treat None and False as similar. However, since Python 2.2 this is not the best policy.

First, when you do an if x or if not x kind of test, Python has to implicitly convert x to boolean. The rules for the bool function describe a raft of things which are False; everything else is True. If the value of x wasn’t properly boolean to begin with, this implicit conversion isn’t really the clearest way to say things.

Before Python 2.2, there was no bool function, so it was even less clear.

Second, you shouldn’t really test with == None. You should use is None and is not None.

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

How many singletons are there? Five: None, True, False, NotImplemented and Ellipsis. Since you’re really unlikely to use NotImplemented or Ellipsis, and you would never say if x is True (because simply if x is a lot clearer), you’ll only ever test None.


回答 2

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

Because None is not the only thing that is considered false.

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, (), [], {} and "" are all different from None, so your two code snippets are not equivalent.

Moreover, consider the following:

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

if object: is not an equality check. 0, (), [], None, {}, etc. are all different from each other, but they all evaluate to False.

This is the “magic” behind short circuiting expressions like:

foo = bar and spam or eggs

which is shorthand for:

if bar:
    foo = spam
else:
    foo = eggs

although you really should write:

foo = spam if bar else egg

回答 3

PEP 8 -风格指南Python代码建议使用不是,如果你正在测试无岬

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

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

PEP 8 — Style Guide for Python Code recommends to use is or is not if you are testing for None-ness

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

On the other hand if you are testing for more than None-ness, you should use the boolean operator.


回答 4

如果你问

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文档。

If you ask

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

the __nonzero__ method of spam gets called. From the Python manual:

__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 you ask

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

the __eq__ method of spam gets called with the argument None.

For more information of the customization possibilities have a look at the Python documenation at https://docs.python.org/reference/datamodel.html#basic-customization


回答 5

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

These two comparisons serve different purposes. The former checks for boolean value of something, the second checks for identity with None value.


回答 6

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

For one the first example is shorter and looks nicer. As per the other posts what you choose also depends on what you really want to do with the comparison.


回答 7

答案是“取决于”。

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

The answer is “it depends”.

I use the first example if I consider 0, “”, [] and False (list not exhaustive) to be equivalent to None in this context.


回答 8

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

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

Personally, I chose a consistent approach across languages: I do if (var) (or equivalent) only if var is declared as boolean (or defined as such, in C we don’t have a specific type). I even prefix these variables with a b (so it would be bVar actually) to be sure I won’t accidentally use another type here.
I don’t really like implicit casting to boolean, even less when there are numerous, complex rules.

Of course, people will disagree. Some go farther, I see if (bVar == true) in the Java code at my work (too redundant for my taste!), others love too much compact syntax, going while (line = getNextLine()) (too ambiguous for me).