2

I ran into some code recently that was checking datetime objects and figured it could be cleaned up a little:

# original
if (datetime1 and (datetime2 is None)):
  do_things()

# slightly cleaner version
if (datetime1 and not datetime2):
  do_things()

But I realized that these two statements are not quite equal IF there is a valid datetime object that evaluates as False. For example:

if not '':
  print('beep')
else:
  print('boop')
# output: beep

if '' is None:
  print('beep')
else:
  print('boop')
# output: boop

Upon looking for information about the boolean value of datetime/time/date objects in Python, I came up empty. Does anyone know how Python handles this? And if I run into a similar problem with a different object type, is there a reliable way to check when its boolean value is true or false?

Sam Zuk
  • 309
  • 1
  • 3
  • 14
  • I guess it's a matter of taste, but why even change the code? To me, the first version is actually *"cleaner"* (or at least clea**r**er)... – Tomerikoo Jul 12 '22 at 15:15
  • @Tomerikoo there really wasn't too much of a reason to change the code besides the matter of personal preference (fwiw the code is mine)-- my question was less "how do I approach this situation" and more "are these two code snippets equivalent" – Sam Zuk Jul 12 '22 at 18:07

1 Answers1

7

Unless the class implements a __bool__() or __len__() method that customizes it, all class instances are truthy by default. If __bool__() is defined, it's used. If not, the default object.__bool__() calls __len__(); if it returns non-zero, the object is truthy.

The datetime.datetime class doesn't have such either method, so all datetime objects are truthy. So your transformation is valid.

This is documented at object.__bool__():

Called to implement truth value testing and the built-in operation bool(); should return False or True. When this method is not defined, __len__() is called, if it is defined, and the object is considered true if its result is nonzero. If a class defines neither __len__() nor __bool__(), all its instances are considered true.

Barmar
  • 741,623
  • 53
  • 500
  • 612
  • 1
    I went to write a helper function to test if a class implements this and got some odd results. The function was equivalent to `lambda x: '__bool__' in dir(type(x))` and it did return True for an integer, but returned False for a string. I also tried calling `__bool__()` on objects of both types and found the same. – Sam Zuk Jul 11 '22 at 22:23
  • @Sam I think you can avoid the `dir()` and string find by changing your lambda to `lambda x: getattr(x, '__bool__', None) is not None` – Pranav Hosangadi Jul 11 '22 at 23:40
  • 1
    My answer was incomplete. It also tries using `__len__()`. @SamZuk – Barmar Jul 12 '22 at 15:11