如果A vs如果A不为None:


154

我可以用吗:

if A:

代替

if A is not None:

后者似乎太冗长。有区别吗?

Answers:


149

该声明

if A:

将调用A.__nonzero__()(请参阅特殊方法名称文档)并使用该函数的返回值。总结如下:

object.__nonzero__(self)

调用实现真值测试和内置操作bool();应该返回FalseTrue,或者它们的整数等效值01。如果未定义此方法,__len__()则调用该方法(如果已定义),并且如果其结果为非零,则认为该对象为true。如果一个类既未定义,也__len__()未定义__nonzero__(),则其所有实例均被视为true。

另一方面,

if A is not None:

将参考A与进行比较None以查看其是否相同。


4
因此A is not None速度更快,因为要做的工作少得多
John La Rooy

40
@gnibbler不是根据我的测试。if object(): pass每个循环约0.130微秒,而if object() is not None: pass约0.135微秒。无论如何,您不应该使用性能来在这两者之间进行选择,而应该看看它们在工作方式上的差异,因为它们并不等效
Lauritz V. Thaulow

1
@gnibbler if A is not None似乎比较慢,因为它是一个比较,需要加载内置单例None作为与之比较的中间步骤A(请参阅参考资料dis.dis())。如果我真的错了,但if A:似乎更有效,请改正我,只要您真的想测试真实值而不是None身份。
cedbeu

2
@cedbeu,似乎取决于A的值。我现在测试python -m timeit -s"a=0" "if a: pass" "else: pass"的速度快python -m timeit -s"a=0" "if a is None: pass" "else: pass"python -m timeit -s"a=1" "if a: pass" "else: pass"慢。可能取决于平台,看看您是否获得相同的结果
John La Rooy

2
@cedbeu,在Python3中它们的速度都快得多,但是is None对于我而言,测试确实是最慢的。在pypy中,它们的测量结果完全相同:)
John La Rooy

51

PEP8所述

  • 与单例(如None)的 比较应始终使用“ is”或“ is not”进行,永远不要使用相等运算符进行

    另外,要当心您的意思是“如果x不是None”时要写“ if x” -例如,在测试是否将默认为None的变量或参数设置为其他值时。另一个值可能具有在布尔上下文中可能为false的类型(例如容器)!


7
嗯,这还不是很清楚,也没有对OP做出响应。自动重定向到PEP并非总是好的选择。通常,人们对单例的身份测试不感兴趣None,而只是对真值检查感兴趣。在这种情况下,if A:似乎效率更高(采取dis.dis(),则有一些额外的步骤来加载内置函数None并与进行比较if A is not None:,而jump_if在另一种情况下则只有一个)。
cedbeu

29
if x: #x is treated True except for all empty data types [],{},(),'',0 False, and None

所以不一样

if x is not None # which works only on None

17

如果没有适当的结果,很多函数将返回None。例如,.first()如果结果中没有行,则SQLAlchemy查询的方法将返回None。假设您选择的值可能返回0,并且需要知道它实际上是0还是查询根本没有结果。

一个常见的习惯用法是为函数或方法的可选参数提供默认值None,然后测试该值为None的值是否已指定。例如:

def spam(eggs=None):
    if eggs is None:
        eggs = retrievefromconfigfile()

比较:

def spam(eggs=None):
    if not eggs:
        eggs = retrievefromconfigfile()

在后者中,如果您致电spam(0)或会发生什么spam([])?该函数将(错误地)检测到您尚未传递的值,eggs并会为您计算默认值。那可能不是您想要的。

或设想一种诸如“返回给定帐户的交易列表”之类的方法。如果该帐户不存在,则可能返回无。这不同于返回一个空列表(这意味着“此帐户存在,但尚未记录交易”)。

最后,回到数据库。NULL和空字符串之间有很大的区别。一个空字符串通常会说“这里有一个值,而这个值根本没有任何东西”。NULL表示“尚未输入此值”。

在每种情况下,您都想使用if A is None。您正在检查一个特定的值-无-不只是“碰巧转换为False的任何值”。


10

他们做的事情截然不同

下面的检查是否有不同的价值观什么False[]None''0。它检查A 的

if A:

下面检查A是否是不同于None的对象。它检查并比较A和None 的引用(内存地址)。

if A is not None:

更新:进一步的说明

两者似乎经常做同样的事情,所以很多人可以互换使用它们-这是一个非常糟糕的主意。由于解释器/编译器的优化(例如interning或其他方法)的优化,纯属巧合,因此两者给出相同结果的原因很多次。

考虑到这些优化,相同值的整数和字符串最终将使用相同的内存空间。这可能可以解释为什么两个单独的字符串看起来像是一样的。

> a = 'test'
> b = 'test'
> a is b
True
> a == b
True

其他事情虽然表现不一样。

> a = []
> b = []
> a is b
False
> a == b
True

这两个列表显然具有各自的记忆。令人惊讶的是,元组的行为类似于字符串。

> a = ()
> b = ()
> a is b
True
> a == b
True

可能是因为保证元组不变,因此重用相同的内存是有意义的。

最重要的是,您不能依靠巧合。仅仅因为它像鸭子一样嘎嘎叫,并不意味着它就是鸭子。使用is==依赖于你真正想要检查。这些内容可能很难调试,因为is我们常常只是略读散文,就像散文一样。


None是单例,不是实现细节(与int或string interning不同)。我不确定我是否遵循您的意思。
Lev Levitsky

@LevLevitsky我的观点不是None行为不同于intstr归因于实习。我的意思是,is并且==正在检查不同的事物;首先检查一个内存地址,第二个检查内存地址的内容。
皮蒂科斯

@LevLevitsky我现在编辑了答案,使其更易于理解。
Pithikos


6

我见过的大多数指南都建议您使用

如果一个:

除非您有理由更具体。

有一些细微的差异。除了None以外,还有其他值返回False,例如空列表或0,因此请考虑一下您真正要测试的内容。


5

在Python中none是一个特殊值,通常会指定一个未初始化的变量。要测试A是否不具有此特定值,请使用:

if A is not None

Falsey值是Python中的特殊对象类(例如false,[])。要测试A是否为假,请使用:

if not A

因此,这两个表达式并不相同,您最好不要将它们视为同义词。


PS None也是假的,因此第一个表达式表示第二个表达式。但是第二个覆盖了除None之外的其他虚假值。现在...如果您可以确定除了A中的None以外,不能有其他虚假值,则可以将第二个表达式替换为第一个表达式。


这并不是说您错了,但是这个答案已经涵盖了这一点。
Makoto 2016年

嘘,如果您不介意的话,我想请您删除票证。我的答案与所有​​其他答案都相同,也许与您所指的答案不太一样,但是从不同的角度给出了答案,对于某些人来说可能更容易理解。此外,当我看到SO投票失败时,我认为答案不对...您承认并非如此。
mircealungu

好吧,这个假设是不完整的。这可能是错误的,或者根本没有用。由于您已经回答了我所说的答案,因此我不认为这是有用的。
Makoto 2016年

4

这取决于上下文。

if A:在希望A成为某种集合时使用,并且我只想在集合不为空的情况下执行该块。这使调用者可以传递任何行为良好的集合(无论是否为空),并使其按照我的期望进行。它还允许NoneFalse禁止执行该块,这有时会方便调用代码。

OTOH,如果我希望A是一个完全任意的对象,但可能默认将其设置为None,则我始终使用if A is not None,因为调用代码可能故意将对空集合,空字符串或0值数字类型的引用传递给它,或者布尔值False或在布尔上下文中碰巧为false的某个类实例。

另一方面,如果我期望A是更特定的东西(例如,我要调用其方法的类的实例),但它可能已默认为None,并且我认为默认布尔转换为我不介意在所有子类上强制执行该类的属性,那么我将使用它if A:来省掉输入额外12个字符的沉重负担,从而省去了手指的麻烦。


3

我创建了一个名为的文件,test.py并在解释器上运行它。您可以更改所需的内容,以测试场景背后的情况。

import dis

def func1():

    matchesIterator = None

    if matchesIterator:

        print( "On if." );

def func2():

    matchesIterator = None

    if matchesIterator is not None:

        print( "On if." );

print( "\nFunction 1" );
dis.dis(func1)

print( "\nFunction 2" );
dis.dis(func2)

这是汇编程序的区别:

资源:

>>> import importlib
>>> reload( test )

Function 1
  6           0 LOAD_CONST               0 (None)
              3 STORE_FAST               0 (matchesIterator)

  8           6 LOAD_FAST                0 (matchesIterator)
              9 POP_JUMP_IF_FALSE       20

 10          12 LOAD_CONST               1 ('On if.')
             15 PRINT_ITEM
             16 PRINT_NEWLINE
             17 JUMP_FORWARD             0 (to 20)
        >>   20 LOAD_CONST               0 (None)
             23 RETURN_VALUE

Function 2
 14           0 LOAD_CONST               0 (None)
              3 STORE_FAST               0 (matchesIterator)

 16           6 LOAD_FAST                0 (matchesIterator)
              9 LOAD_CONST               0 (None)
             12 COMPARE_OP               9 (is not)
             15 POP_JUMP_IF_FALSE       26

 18          18 LOAD_CONST               1 ('On if.')
             21 PRINT_ITEM
             22 PRINT_NEWLINE
             23 JUMP_FORWARD             0 (to 26)
        >>   26 LOAD_CONST               0 (None)
             29 RETURN_VALUE
<module 'test' from 'test.py'>

2

前者是Pythonic(更好的意识形态代码),但是如果A为False(不是None),则不会执行该块。


7
-1。正如@klesh所提到的,PEP8表示要使用is/is not None。紧随PEP8之后的是Pythonic。除了这两个测试是不同的
JCotton 2012年

1

python> = 2.6,

如果我们写这样

if A:

将产生警告,

FutureWarning:此方法的行为将在将来的版本中更改。而是使用特定的“ len(elem)”或“ elem不是None”测试。

所以我们可以使用

if A is not None:
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.