It will fail for non-list inputs, which is another reason it can be handy if you want clarify that you’re enforcing a list.
len will work for strings, tuples, dicts, sets, plus all kinds of custom classes that implement the __len__ magic method.
I just want to enforce that the object acts like a list, mainly versus a float, int, or boolean. Tuples are definitely 100% ok, and if someone wants to give me a set, that's almost certainly OK too, in the use cases I'm considering. And so is a numpy vector!
Often these objects are held in a container (a dict or class variable), so type annotations would be clumsy as far as I know. Wouldn't I need to create a separate object to enforce the annotation?
And the type annotation would need to allow tuples and all flavors of lists or numpy arrays.
On the other end of the scale, I don't think either of us wants to use isinstance checks!
My primary intent isn't to be bulletproof, it's to show intent. The main fail of using len() in this way is that it accepts strings.
It's a protocol (structural) runtime type check, not a class (nominal) runtime type check.
isinstance(items, Sequence)
Or perhaps: isinstance(items, Iterable) # works with numpy arrays
Or, equivalent to using len() for type checking, you could do: isinstance(items, Sized)Type checking via mypy, pyright, etc., is great, but in library (as opposed to an app) you can't count on consumers doing that, and isinstance() checks don't support duck-typing.
It will fail for non-list inputs, which is another reason it can be handy if you want clarify that you’re enforcing a list.