23

The Zen of Python says "Explicit is better than implicit". Yet the "pythonic" way to check for emptiness is using implicit booleaness:

if not some_sequence:
    some_sequence.fill_sequence()

This will be true if some_sequence is an empty sequence, but also if it is None or 0.

Compare with a theoretical explicit emptiness check:

if some_sequence is Empty:
    some_sequence.fill_sequence()

With some unfavorably chosen variable name the implicit booleaness to check for emptiness gets even more confusing:

if saved:
    mess_up()

Compare with:

if saved is not Empty:
    mess_up()

See also: "Python: What is the best way to check if a list is empty?". I find it ironic that the most voted answer claims that implicit is pythonic.

So is there a higher reason why there is no explicit emptiness check, like for example is Empty in Python?

Community
  • 1
  • 1
Lesmana
  • 25,663
  • 9
  • 82
  • 87
  • `saved != []` and `saved == []` work just fine. How are those not explicit emptyness checks? Are you just put out that `is` is stricter than `==`? – Jon Jay Obermark Sep 04 '14 at 15:04

5 Answers5

21

Polymorphism in if foo: and if not foo: isn't a violation of "implicit vs explicit": it explicitly delegates to the object being checked the task of knowing whether it's true or false. What that means (and how best to check it) obviously does and must depend on the object's type, so the style guide mandates the delegation -- having application-level code arrogantly asserts it knows better than the object would be the height of folly.

Moreover, X is Whatever always, invariably means that X is exactly the same object as Whatever. Making a totally unique exception for Empty or any other specific value of Whatever would be absurd -- hard to imagine a more unPythonic approach. And "being exactly the same object" is obviously transitive -- so you could never any more have distinct empty lists, empty sets, empty dicts... congratulations, you've just designed a completely unusable and useless language, where every empty container crazily "collapses" to a single empty container object (just imagine the fun when somebody tries to mutate an empty container...?!).

Alex Martelli
  • 854,459
  • 170
  • 1,222
  • 1,395
  • 9
    But truth and emptiness are completely different concepts! It's fine to claim that delegating trueness to the object makes sense **if and only if** there is a clear boolean value encapsulated by the object. For the case of sequences / arrays, why is the equivalence of empty to true any better than the equivalence of "all items are true" to truth? I agree with the OP; it seems confusing and implicit to understand that sequences are false iff they are empty... – Steven Schlansker Aug 15 '10 at 17:55
  • This is a heritage of C where false was equivalent to 0 and a !=0 meant a is true. So in python trueness means "having some significant value" and this meaning in wide use. – Odomontois Aug 15 '10 at 19:17
  • 1
    @Odomontois I'd be more inclined to say that it descends from lisp where `()`, the empty set, is taken to be the __definition__ of boolean false. – aaronasterling Aug 15 '10 at 19:30
  • 4
    @Steven, "truth" is a concept in _logic_ in which, strictly speaking, it applies only to predicates: everything else, _including_ "boolean values", requires an agreed-upon mapping, a _projection_ of a type's instances into logic concepts. `all(ctr)` and `any(ctr)` provide explicit mappings for the (rarer but very useful) cases of "all items are true" and "any item is true", `bool(ctr)` for the most common case of "there is any item at all". The ability to _always_ optonally elide the `bool` call in `if` and `while` makes for clarity, regularity, and conciseness, without really any sacrifice. – Alex Martelli Aug 15 '10 at 19:55
  • 4
    @Steven Schlansker: It's better because it's convention; the boolean value of a container is whether it's non-empty. If you're going to ignore convention, give up; all is lost. You may as well object that "+" implicitly means "addition". – Glenn Maynard Aug 15 '10 at 20:00
  • 9
    @Glenn, yep, a very implicit thing -- bad enough to use `+` to mean "et", as that Nicole d'Oresme (or a copyist) did in the 14th century, but extending that to mean "positive" (as Johannes Widmann did in the 15th) and even worse to "addition" (as Heinrich Schreyber did in the 16th), is just intolerably implicit and is a fad that will never catch on. See how much better and more explicit is Cobol's "ADD X TO Y GIVING Z" than the intolerable implicitness of `Z = X + Y` is this upstart Python thingy! Youth these days... – Alex Martelli Aug 15 '10 at 20:21
  • i see that the `is` operator is the identity operator and so `is Empty` means identity compare against the object "Empty". in java the identity operator is `==`, which in python is the equals operator (calling `__eq__`), which in java is the `.equals` method. so much for equality. i already knew that but somehow it didn't sunk in, enough. still everytime i write `is None` it just feels so right to also write `is Empty`. if i was Guido (the python creator) i would put on my whitespace battle hardened asbestos suit and hardcode `is Empty` to check for emptyness. – Lesmana Aug 16 '10 at 00:21
  • @lesmana, thanks for making me **SO** grateful to Fate, all over again, that Python was designed, and is still shepherded, by Guido, and not by you (or, to be honest, by me -- or just about any other programmer -- but relatively few of us would make **such** a dog's breakfast of language design as you seem keen to). Of course, if the language had such absurdities from the start, I wouldn't have given it a 2nd look when I first met it ten years ago, but then I would have missed _so_ many wonderful things that happened in my life exactly because of this wonderful language (story I've often told) – Alex Martelli Aug 16 '10 at 00:33
  • To be fair, it's possible to have useful languages in which empty sequences are all identical objects -- look at how Clojure does it, in which `(seq anything)` yields `nil` when `anything` is empty. – Charles Duffy May 07 '13 at 00:10
15

The reason why there is no is Empty is astoundingly simple once you understand what the is operator does.

From the python manual:

The operators is and is not test for object identity: x is y is true if and only if x and y are the same object. x is not y yields the inverse truth value.

That means some_sequence is Empty checks whether some_sequence is the same object as Empty. That cannot work the way you suggested.

Consider the following example:

>>> a = []
>>> b = {}

Now let's pretend there is this is Empty construct in python:

>>> a is Empty
True
>>> b is Empty
True

But since the is operator does identity check that means that a and b are identical to Empty. That in turn must mean that a and b are identical, but they are not:

>>> a is b
False

So to answer your question "why is there no is Empty in python?": because is does identity check.

In order to have the is Empty construct you must either hack the is operator to mean something else or create some magical Empty object which somehow detects empty collections and then be identical to them.

Rather than asking why there is no is Empty you should ask why there is no builtin function isempty() which calls the special method __isempty__().

So instead of using implicit booleaness:

if saved:
  mess_up()

we have explicit empty check:

if not isempty(saved):
  mess_up()

where the class of saved has an __isempty__() method implemented to some sane logic.

I find that far better than using implicit booleaness for emptyness check.

Of course you can easily define your own isempty() function:

def isempty(collection):
  try:
    return collection.__isempty__()
  except AttributeError:
    # fall back to implicit booleaness but check for common pitfalls
    if collection is None:
      raise TypeError('None cannot be empty')
    if collection is False:
      raise TypeError('False cannot be empty')
    if collection == 0:
      raise TypeError('0 cannot be empty')
    return bool(collection)

and then define an __isempty__() method which returns a boolean for all your collection classes.

Lesmana
  • 25,663
  • 9
  • 82
  • 87
6

I agree that sometimes if foo: isn't explicit for me when I really want to tell the reader of the code that it's emptiness I'm testing. In those cases, I use if len(foo):. Explicit enough.

I 100% agree with Alex w.r.t is Empty being unpythonic.

Virgil Dupras
  • 2,634
  • 20
  • 22
  • 4
    By the same logic, `if len(foo):` relies the fact that 0 is implictly false ;-p You have to start with convention *somewhere*. – Jochen Ritzel Aug 15 '10 at 18:04
  • 7
    Never do this; you're forcing the container to calculate the length, which can be expensive for some data structures, when all you want to know is if it's empty. – Glenn Maynard Aug 15 '10 at 19:52
  • 4
    @Glenn Maynard: "Never" is quite a strong word. 1. Most of the time, it's lists we're working with. 2. Premature optimization is the root of all evil. 3. Readability counts. – Virgil Dupras Aug 16 '10 at 06:12
  • 2
    1. Don't assume you're working with a list, or any specific type, unless you have a specific reason to. Duck typing is a fundamental Python design concept. 2. Using well-understood conventions which are also efficient is not premature optimization. 3. Not relevant, since `if val:` is perfectly readable. – Glenn Maynard Aug 16 '10 at 19:56
  • @Glenn: What you say apply to libraries, although I agree I went too far with "premature optimization". It's just that you went too far with "Never". Not everyone develops libraries. Moreover, data structures for which `len` is expensive are rare. – Virgil Dupras Aug 17 '10 at 04:47
  • I've switched to using `len` because `if foo` causes exceptions if you're using a numpy array. Having a standard `empty` method (like for C++ iterators) would be handy for this purpose. – Danica May 07 '13 at 00:57
0

There is an explicit emptyness check for iterables in Python. It is spelled not. What's implicit there? not gives True when iterable is empty, and gives False when it is nonempty.

What exactly do you object to? A name? As others have told you, it's certainly better than is Empty. And it's not so ungrammatical: considering how things are usually named in Python, we might imagine a sequence called widgets, containing, surprisingly, some widgets. Then,

if not widgets:

can be read as "if there are no widgets...".

Or do you object the length? Explicit doesn't mean verbose, those are two different concepts. Python does not have addition method, it has + operator, that is completely explicit if you know the type you're applying it to. The same thing with not.

Veky
  • 2,646
  • 1
  • 21
  • 30
0

Consider that Lisp has been using () empty list or its symbol NIL quite some years as False and T or anything not NIL as True, but generally computation of Truth already produced some useful result that need not be reproduce if needed. Look also partition method of strings, where middle result works very nicely as while control with the non-empty is True convention.

I try generally avoid using of len as it is most times very expensive in tight loops. It is often worth while to update length value of result in program logic instead of recalculating length.

For me I would prefer Python to have False as () or [] instead of 0, but that is how it is. Then it would be more natural to use not [] as not empty. But now () is not [] is True so you could use:

emptyset = set([])    
if myset == emptyset:

If you want to be explicit of the empty set case (not myset is set([]))

I myself quite like the if not myset as my commenter.

Now came to my mind that maybe this is closest to explicit not_empty:

if any(x in myset for x in myset): print "set is not empty"

and so is empty would be:

if not any(x in myset for x in myset): print "set is empty"
Tony Veijalainen
  • 5,447
  • 23
  • 31
  • `if not myset` is explicit enough. – aaronasterling Aug 15 '10 at 21:10
  • not related to initial question but have you examples of "It is often worth while to update length value of result in program logic instead of recalculating length"? Is it about built in collections? – Odomontois Aug 15 '10 at 21:18
  • In my anagram program I could speed up the check of possible anagram by not using len but let the contains function return not only the remaining letters if string contains letters of the other, but also the length of the remaining letters. Maybe I misgeneralized, but profiling showed considerable part spent in len functions before the optimization. The data type was string, so it is unmutable sequence. Maybe I should edit often in my statement as 'sometimes'. I do not think len is O(1) operation for any sequence type. – Tony Veijalainen Aug 15 '10 at 21:38
  • Theoreticlay, `len` is an O(n) operation for best case, worst case and average. If you store the length in implementation and keep it updated, then it is O(1). – aaronasterling Aug 15 '10 at 21:42