Question about EAFP usage in heapq.nsmallest / heapq.nlargest

In the implementation of heapq.nsmallest and heapq.nlargest, I saw that the fast-path check uses an EAFP pattern:

try:
    size = len(iterable)
except (TypeError, AttributeError):
    pass


I’m curious why this method was chosen instead of using LBYL-style checks, like checking whether length is supported first.

From a design perspective:

EAFP benefits

  • Python’s duck-typing philosophy

  • Directly relies on len() behaviour

Possible LBYL benefits

  • Avoids triggering exceptions when the iterable has no length

  • Makes capability checking explicit

Are there any historical reasons for choosing EAFP here instead of LBYL?

I’m trying to understand the design decision rather than proposing a change.

The existing code is simple and guaranteed to be correct and probably as fast or faster at least on ‘average’ as any alternative. The fact that TypeError is being caught suggests that there has existed an ‘iterable’ whose class has .__len__ but which raised TypeError for some instance when called. (Yes, would seem strange, perhaps buggy.) There is no other way to check for this. If you are really curious, use git blame to find the issues and PRs that created/edited the code and check for comments.

The TypeError might be for user-defined classes that have __len__ returning something that cannot be turned into a non-negative int. builtin_len checks these conditions for what __len__ gives it.

For example len(iter([]))causes TypeError.