为什么在Python中,"if not someobj:"比"if someobj == None:"更好?
为什么在Python中使用"if not someobj:"比"if someobj == None:"更好?
因为None并不是唯一被视为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
关于你的最后一个问题,它们是等价的。
而且两者都是错误的,因为""是False。第二个应该写成 '("",) or ("s",)'。无论如何,现代版本的Python已经有了正确的三元运算符。这种容易出错的技巧应该被淘汰。
为什么在Python中,使用"if not someobj:"比"if someobj == None:"更好?
在Python中,这两种写法实际上都是不好的实践。过去,将None和False视为相似的方式被认为是可以接受的。然而,自从Python 2.2版本开始,这已不再是最佳策略。
首先,当你进行if x
或if not x
这样的测试时,Python必须隐式地将x
转换为布尔值。对于bool
函数的规则描述了一系列为False的情况;其他所有情况均为True。如果x的值一开始就不是合适的布尔值,这种隐式转换并不是最清晰的表达方式。
在Python 2.2之前,甚至没有bool函数,所以更加不清晰。
其次,你不应该使用== None
来进行测试。你应该使用is None
和is not None
。
参见PEP 8,Python代码风格指南。
- 对于单例(singleton)如None的比较,应该始终使用'is'或'is not',而不是等号操作符。 同时,当你真正想要判断一个默认值为None的变量或参数是否被设置为其他值时,要小心不要写成"if x",而应该写成"if x is not None"。 -- 例如,当测试一个变量或参数的默认值是否被设置为其他值时,其他值可能具有在布尔环境中为False的类型(例如容器)!
有多少个单例存在?有五个:None
,True
,False
,NotImplemented
和Ellipsis
。由于你几乎不太可能使用NotImplemented
或Ellipsis
,并且你永远不会写if x is True
(因为简单地写if x
更清晰明了),你只会测试None
。
第二种写法绝对不是不好的实践。PEP 8建议使用if x两次。首先用于序列(而不是使用len),然后用于True和False(而不是使用is)。我所见过的几乎所有Python代码都使用了if x和if not x。
在Python中,使用"if not someobj:"比"if someobj == None:"更好的原因是,前者会自动调用对象的特殊方法来判断对象是否有意义,而后者只是简单地比较对象是否等于None。
在第一个测试中,如果对象不是bool
类型,Python会尝试将其转换为bool
值。大致上,我们询问对象:你是否有意义?这是通过以下算法来实现的:
1. 如果对象有__nonzero__
特殊方法(如数字内置类型int
和float
),则调用该方法。该方法必须返回一个bool
值,该值将直接使用,或者返回一个int
值,如果等于零则被认为是False
。
2. 否则,如果对象有__len__
特殊方法(如容器内置类型list
,dict
,set
,tuple
等),则调用该方法,如果容器为空(长度为零),则认为是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
尽管技术上是正确的,但这并没有解释元组、列表、字典、字符串、Unicode、整数、浮点数等都具有nonzero
方法。更常见的是依赖内置类型的真值,而不是依赖自定义的nonzero
方法。