(Title and contents updated after reading Alex's answer)
In general I believe that it's considered bad form (un-Pythonic) for a function to sometimes return an iterable and sometimes a single item depending on its parameters.
For example struct.unpack
always returns a tuple even if it contains only one item.
I'm trying to finalise the API for a module and I have a few functions that can take one or more parameters (via *args
) like this:
a = s.read(10) # reads 10 bits and returns a single item
b, c = s.read(5, 5) # reads 5 bits twice and returns a list of two items.
So it returns a single item if there's only one parameter, otherwise it returns a list. Now I think this is fine and not at all confusing, but I suspect that others may disagree.
The most common use-case for these functions would be to only want a single item returned, so always returning a list (or tuple) feels wrong:
a, = s.read(10) # Prone to bugs when people forget to unpack the object
a = s.read(10)[0] # Ugly and it's not clear only one item is being returned
Another option is to have two functions:
a = s.read(10)
b, c = s.read_list(5, 5)
which is OK, but it clutters up the API and requires the user to remember twice as many functions without adding any value.
So my question is: Is sometimes returning an iterable and sometimes a single item confusing and un-Pythonic? If so what's the best option?
Update: I think the general consensus is that it's very naughty to only return an iterable sometimes. I think that the best option for most cases would be to always return the iterable, even if it contained only one item.
Having said that, for my particular case I think I'll go for the splitting into two functions (read(item)
/ readlist(*items)
), the reasoning being that I think the single item case will happen much more often than the multiple item case, so it makes it easier to use and the API change less problematic for users.
Thanks everyone.
If you are going to be returning iterators sometimes, and single objects on others, I'd say return always an iterator, so you don't have to think about it.
Generaly, you would use that function in a context that expects an iterator, so if you'd have to check if it where a list to iterate or an object to do just one time the work, then its easier to just return an iterator and iterate always, even if its one time.
If you need to do something different if you are returned one element, just use if len(var):
.
Remember, consistency is a valuable good.
I lean towards returning a consistent object, not necesarily the same type, but if I return ever an iterable, I return always an iterable.
In general, I would have to say that returning two different types is bad practice.
Imagine the next developer coming to read and maintain your code. At first he/she will read a method using your function and think "Ah, read() returns a single item."
Later they will see code treating read()'s result as a list. At best this will simply confuse them and force them to examine read()'s usage. At worst they might think there is a bug in the implementation using read() and attempt to fix it.
Finally, once they understand read() returns two possible types they will have to ask themselves "is there possibly a third return type I need to be ready for?"
This reminds me of the saying: "Code as if the next guy to maintain your code is a homicidal maniac who knows where you live."
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