В первом тесте Python пытается преобразовать объект в значение bool
, если оно еще не было. Грубо говоря, мы спрашиваем объект: вы значите или нет? Это делается с помощью следующего алгоритма:
Если объект имеет специальный метод __nonzero__
(как и числовые встроенные модули, int
и float
), он вызывает этот метод. Он должен либо возвращать значение bool
, которое затем используется напрямую, либо значение int
, которое считается False
, если оно равно нулю.
В противном случае, если у объекта есть специальный метод __len__
(как встроенные контейнеры, list
, dict
, set
, tuple
, ...), он вызывает это метод с учетом контейнера False
, если он пуст (длина равна нулю).
В противном случае объект считается True
, если только он не равен None
, в этом случае он считается False
.
Во втором тесте объект сравнивается на равенство с None
. Здесь мы задаем объекту: «Вы равны этому другому значению?» Это делается с использованием следующего алгоритма:
Если у объекта есть метод __eq__
, он вызывается, и возвращаемое значение затем преобразуется в значение bool
и используется для определения результата if
.
В противном случае, если у объекта есть метод __cmp__
, он вызывается. Эта функция должна возвращать int
, указывающий порядок двух объектов (-1
, если self < other
, 0
, если self == other
, +1
, если self > other
).
В противном случае объект сравнивается на предмет идентичности (т. Е. Он является ссылкой на тот же объект, что может быть проверено оператором 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