0

This might be a duplicate, but all I am finding are questions on how for ... else works.

Python has for ... else syntax:

for(iteratable):
  do_stuff()
  conditional break
else:
  print("didn't break")

I understand how this works (run the else block when no break was encountered). This is not the question I am asking, but I am looking for something that works like the niave assumption people make when they see it for the first time.

I have come across the situation where I have done the following a number of times:

def foo(iterable):
  if iterable:
    for i in iterable:
      print (i)
  else:
    print("iterable empty")

This looks fine, we pass in a list:

>>> foo([0,1,2])
0
1
2
>>>

an empty list:

>>> foo([])
iterable empty
>>>

all seems well. Lets pass in a generator:

>>> foo((x for x in range(3)))
0
1
2
>>>

well, this function seems to be working just fine. One last test, what about a generator that has no more values to generate?

>>> foo((x for x in range(0)))
>>>

Hmm. the function hasn't done anything. How interesting.

What method should I use to detect a loop that has run 0 times? Clearly just testing the truthiness of the iterable is not enough. Is there a built-in method for detecting 0 iterations, or will I need to use a counter or flag within the loop to detect an iteration?

Note: this question isn't asking how I can detect an empty generator. Generator specific work-arounds will not work with lists for example.

Baldrickk
  • 4,291
  • 1
  • 15
  • 27
  • @Mitch and somehow I missed that one when searching... – Baldrickk Feb 07 '17 at 17:31
  • 2
    Title looks like really bad :/ – cubuspl42 Feb 07 '17 at 17:32
  • @Baldrickk No worries. I (think) you can mark your own question as a dupe if you agree it is a duplicate :) – miradulo Feb 07 '17 at 17:32
  • "How do I know if a generator is empty from the start?" Idk, just kidding. :P – cubuspl42 Feb 07 '17 at 17:35
  • Checking the other question, it applies only to generators, to take an example answer, it uses `next(generator)` to get the first value, and then puts it back on the front with `itertools.chain()`. Solutions like that won't work here, as the function can take any iterable, and `next()` only works with generators. I'm after a more generic solution. – Baldrickk Feb 07 '17 at 17:38
  • You are giving nothing to function. It is not special 'None' object you are giving. It is none. So it is not working, I mean it cannot check if none is true or false. Try this: foo([x for x in range(0)]) Now it is working right. You are giving nothing to the function because there is no empty tuple in python. – Ozgur Bagci Feb 07 '17 at 17:43
  • @Baldrickk There are a couple notable workarounds on that question - I can think of how you could adapt one easily for your case.I still think this is a duplicate. – miradulo Feb 07 '17 at 17:46
  • @ÖzgürBağcı that's passing in an empty list. I know that works, in fact, it was my second example. I'm looking for a solution that allows me to take in any iterator and have it handled correctly. e.g. a program that dynamically generates generators may very well have 0 or infinite ouputs to a generated generator, and I need to handle those as well as lists. – Baldrickk Feb 07 '17 at 17:46
  • @Mitch the other question is generator _specific_, which is why all the answers there use `next()` which results in a `TypeError` when applied to a `list`. – Baldrickk Feb 07 '17 at 17:49
  • @Baldrickk Why not just get an iterator with `iter`? – miradulo Feb 07 '17 at 17:53
  • @Baldrickk I think I found a way. Try this: if 'generator object' is in str(iterable): print('iterable empty') – Ozgur Bagci Feb 07 '17 at 17:57
  • @Mitch usecase example? `iter((x for x in []))` still just returns an empty generator, am I misunderstanding you? – Baldrickk Feb 07 '17 at 17:58
  • @ÖzgürBağcı `if 'generator object' is str(iterable): print('iterable empty') else for i in iterator...` will loop over empty lists. – Baldrickk Feb 07 '17 at 18:00
  • @Baldrickk You have a function accepting an _iterable_. Your issue is that you can't detect if an iterator is empty. Well all lists are iterable and not all iterables are lists - so `iterable=iter(iterable)` at the start of your function. Now the ambiguity is gone and we have a duplicate question. – miradulo Feb 07 '17 at 18:00
  • @Mitch oh you mean, it would cause iterable to have a `next()` ... could work. they seem like long winded workarounds for something that should be simple... ok. – Baldrickk Feb 07 '17 at 18:06
  • @Baldrickk That's a completely incorrect interpretation but yes, it would turn a list into a list iterator which you could then call `next()` on. I'm certain there's an easier way to accomplish whatever you're trying to do, but as your current question stands, yeah, that's the solution. Or you could set a flag as soon as you see something inside the iterable, and print based on the flag condition. – miradulo Feb 07 '17 at 18:08
  • @Baldrickk OK lets try that: define a foo2 that is not printing but returning all i's in a list. Make a recursive call from foo if iterable is true, in that recursive call check if foo2(iterable) returns empty list. If it is returning empty list print iterable empty. if for i in iterable print i. – Ozgur Bagci Feb 07 '17 at 18:17
  • @Mitch looks like the flag condition is the way to go. Simplest, will work in all cases, and answers the zero loops question as opposed to the empty iterator question. All I needed was "nope, use a flag ;)" – Baldrickk Feb 08 '17 at 09:07

0 Answers0