4 ms·
I think it comes down to the idea that going out of your way to make the library work either way makes it easier for people to use, even if it makes the library
by dec0dedab0de 5y ago
I think it comes down to the idea that going out of your way to make the library work either way makes it easier for people to use, even if it makes the library itself a bit more complicated.
I wish more library devs would go out of their way to add such niceties.
A big one that I always do is if I'm expecting an iterator of
objects I make it just work with one.
from collections.abc import Iterable
def my_function(arg):
# slightly different if you're looking for a collection of strings or bytes
if not isinstance(arg, Iterable):
arg = [arg]
for item in arg:
do the thing
Or if you have a specific type of object you want it goes like this
from collections.abc import Iterable
def my_function(arg):
if isinstance(arg, MyObjectIWant):
arg = [arg]
for item in arg:
do the thing
I like to think of my libraries as mini programs for users, and I hate when validation is too strict, when it could be so easy to fix. Like when a phone number validator insists on (XXX)XXX-XXXX or XXX.XXX.XXXX or XXXXXXXXXX when it could just ignore everything that isn't a number and make sure there is 10 of them.
- goodside 5y agoThis sounds like a nice idea in theory, and makes a lot of sense for polished, publicly visible libraries where convenience trumps simplicity, but the edge cases can lead to confusing failures and bloat otherwise simple code — as you noted, your example code appears to work for arbitrary objects but actually fails for `str` or `bytes`. A great case study in the issues here is Pandas, which routinely allows arguments to be columns, lists of columns, string column labels, lists of string column labels, and so on. It works surprisingly well, but at the cost of inventing a new semantic distinction between `list` objects and other sequence types like `tuple` — someone unfamiliar with Pandas who thinks “Why does this need to be a list comprehension when a generator expression will do?” is likely introducing a bug. Another subtle issue is that code permissive with inputs is harder to extend via wrapper code. Suppose you have a function that does some sort of processing for any number of given datetimes, but also accepts integer seconds since 1970-01-01, a formatted date string, or any mixed sequence of these types. If you need to write a wrapper that first rounds all times to the most recent hour, your task is much easier if the only accepted type is `Iterable[datetime]`.