1

I have some programming experience in other languages, but I've been taking the Core Python course from Pluralsight to contrast the language features. In the iterables section of the course, the instructor created this function that uses the EAFP principle to raise an exception if the iterable argument is empty:

def first(iterable):
    iterator = iter(iterable)
    try:
        return next(iterator)
    except StopIteration:
        raise ValueError("iterable is empty")

I saw this and thought, "couldn't I just raise that exception in an else clause? So I wrote this function:

def first(iterable):
    if(len(iterable) == 0):
        raise ValueError("iterable is empty")
    else:
        return iterable[0]

When writing this post, I found this Stack Overflow question about the implicit boolness of an empty list and thought I could refactor my code to make it even more pythonic:

def first(iterable):
    if not iterable:
        raise ValueError("iterable is empty")
    else:
        return iterable[0]

They seem to work the same way, and mine still uses the EAFP principle, right? Is the first one faster? Or is it just a demonstration of the iter() and next() functions?

BaileyH
  • 11
  • 2
  • `try` and `except` are costly operations, so in this case, your code is faster. But your code has an issue as it is not able to differentiate between an actual iterable object and noniterable object for example `first(22322)` for int input your code is considering as an iterable and trying to get first element in actual it is not. – sahasrara62 Apr 26 '20 at 02:40
  • @sahasrara62 I'm not sure about this; I recall a core dev (Marc Andre Lemburg) giving a talk some years back where he said `try` itself was cheap, but entering the `except` clause was expensive. – snakecharmerb Apr 27 '20 at 08:01
  • you can test the speed and example in any ide (i tested this in pcharm) yeah try is basically o(1) cost operation but except is costly one – sahasrara62 Apr 27 '20 at 08:09

1 Answers1

0
def first(iterable):
    if(len(iterable) == 0):
        raise ValueError("iterable is empty")
    else:
        return iterable[0]

This function assumes that the iterable has a meaningful length (that is, it has a __len__ method). This wouldn't work for example, for generator expressions

>>> len(x for x in  range(4))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: object of type 'generator' has no len()

Simalarly this function

def first(iterable):
    if not iterable:
        raise ValueError("iterable is empty")
    else:
        return iterable[0]

assumes that bool(iterable) will evaluate to False if it's empty. Again, this may not be true:

>>> bool(x for x in  [])
True

Useful reading:

snakecharmerb
  • 47,570
  • 11
  • 100
  • 153