I have a function that makes a call to a REST API. One of the parameter is a comma-separated list of names. To generate it, I do this:
','.join(names)
However, I also want to allow the user to provide a single name. The problem is that if names = ERII
, for instance, it results in ['E', 'R', 'I', 'I']
, but I need it to be ['ERII']
instead.
Now, I could force the user to enter a list with only one value (names=['ERRI']
or names=('ERII',)
. I would prefer to allow the user to provide a single String. Is there a clever way to do that without a if else
statement checking if the provided value is an Iterable
?
Also, I am uncertain as what would be the best practice here, force to provide a list, or allow a single string?
Parameters that can be either a thing or an iterable or things are a code smell. It’s even worse when the thing is a string, because a string is an iterable, and even a sequence (so your test for isinstance(names, Iterable)
would do the wrong thing).
The Python stdlib does have a few such cases—most infamously, str.__mod__
—but most of those err in the other direction, explicitly requiring a tuple rather than any iterable, and most of them are considered to be mistakes, or at least things that wouldn’t be added to the language today. Sometimes it is still the best answer, but the smell should make you think before doing it.
I don’t know exactly what your use case is, but I suspect this will be a lot nicer:
def spam(*names):
namestr = ','.join(names)
dostuff(namestr)
Now the user can call it like this:
spam('eggs')
spam('eggs', 'cheese', 'beans')
Or, if they happen to have a list, it’s still easy:
spam(*ingredients)
If that’s not appropriate, another option is keywords, maybe even keyword-only params:
def spam(*, name=None, names=None):
if name and names:
raise TypeError('Not both!')
if not names: names = [name]
But if the best design really is a string or a (non-string) iterable of strings, or a string or a tuple of strings, the standard way to do that is type switching. It may look a bit ugly, but it calls attention to the fact that you’re doing exactly what you’re doing, and it does it in the most idiomatic way.
def spam(names):
if isinstance(names, str):
names = [names]
dostuff(names)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With