Suppose I have a function which can either take an iterable/iterator or a non-iterable as an argument. Iterability is checked with try: iter(arg)
.
Depending whether the input is an iterable or not, the outcome of the method will be different. Not when I want to pass a non-iterable as iterable input, it is easy to do: I’ll just wrap it with a tuple.
What do I do when I want to pass an iterable (a string for example) but want the function to take it as if it’s non-iterable? E.g. make that iter(str)
fails.
Edit – my original intention:
I wanted to generalise the zip
function in that it can zip iterables with non-iterables. The non-iterable would then repeat
itself as often as the other iterables haven’t finished.
The only general solution fo me seems now to be, that I shouldn’t check inside the general_zip
function (because of the string issues); but that instead I’ll have to add the repeat
iterator to the argument before calling zip
. (This actually saves me from inventing the general_zip
function — although I still might because with a non-iterable as an input it would be unambiguous without the extra repeat.)
The more I think about it, it seems like it’s not possible to do without type checking or passing argments to the function.
However, depending on the intention of the function, one way to handle it could be:
from itertools import repeat
func(repeat(string_iterable))
func
still sees an iterable but it won’t iterate through the charaters of the string itself. And effectively, the argument works as if it’s a constant non-iterable.
Whoo! It appears you want to be able to pass iterables as iterables, iterables as noniterables, noniterables as iterables, and noniterables as noniterables. Since you want to be able to handle every possibility, and the computer can not (yet) read minds, you are going to have to tell the function how you want the argument to be handled:
def foo_iterable(iterable):
...
def foo_noniterable(noniterable):
...
def foo(thing,isiterable=True):
if isiterable:
foo_iterable(thing)
else:
foo_noniterable(thing)
Apply foo to an iterable
foo(iterable)
Apply foo to an iterable as a non-iterable:
foo_noniterable(iterable) # or
foo(iterable, isiterable=False)
Apply foo to a noniterable as a noniterable:
foo_noniterable(noniterable) # or
foo(noniterable,isiterable=False)
Apply foo to a noniterable as an iterable:
foo((noniterable,))
PS. I'm a believer in small functions that do a single job well. They are easier to debug and unit-test. In general I would advise avoiding monolithic functions that behave differently depending on type. Yes, it puts a little extra burden on the developer to call exactly the function that is intended, but I think the advantages in terms of debugging and unit-testing more than make up for it.
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