Frage

Nehmen wir an, dass ich über einen iterable bin Looping und möchte einige Maßnahmen ergreifen, wenn der Iterator ist leer. Die beiden besten Möglichkeiten, dass ich mir vorstellen kann, dies zu tun, sind:

for i in iterable:
     # do_something
if not iterable:
    # do_something_else

und

empty = True
for i in iterable:
    empty = False
    # do_something
if empty:
    # do_something_else

Das hängt zunächst von der die iterable einer Sammlung ist (so nutzlos, wenn die iterable in die Funktion / Methode übergeben werden, wo die Schleife ist) und die zweiten Sätze empty auf jedem Durchlauf durch die Schleife, die hässlich sein scheint.

Gibt es eine andere Art und Weise, dass ich fehle oder ist die zweite Alternative der Beste? Es wäre cool, wenn es wirklich einig Klausel ist, dass ich zu der Schleife Aussage könnte hinzufügen, dass dies für mich behandeln würde viel wie else not_found Fahnen weg gehen macht.


Ich bin nicht für klug Hacks suchen.

Ich bin nicht auf der Suche nach Lösungen, die eine Menge Code beinhalten

Ich bin auf der Suche nach einer einfachen Sprache-Funktion. Ich bin auf der Suche nach einem klar und pythonic Art und Weise zu iterieren über eine iterable und einige Maßnahmen ergreifen, wenn die iterable leer ist, dass jeder erfahrene Python-Programmierer werden verstehen. Wenn ich es ohne Setzen eines Flags bei jeder Iteration tun könnte, wäre das fantastisch. Wenn es keine einfache Sprache ist, das dies tut, dann vergessen Sie es.

War es hilfreich?

Lösung

Ich denke, das das der sauberste Weg, dies zu tun:

# first try with exceptions
def nonempty( iter ):
    """ returns `iter` if iter is not empty, else raises TypeError """
    try:
        first = next(iter)
    except StopIteration:
        raise TypeError("Emtpy Iterator")
    yield first
    for item in iter:
        yield item


# a version without exceptions. Seems nicer:
def isempty( iter ):
    """ returns `(True, ())` if `iter` if is empty else `(False, iter)`
         Don't use the original iterator! """
    try:
        first = next(iter)
    except StopIteration:
        return True, ()
    else:
        def iterator():
            yield first
            for item in iter:
                yield item
        return False, iterator()



for x in ([],[1]):
    # first version
    try:
        list(nonempty(iter(x))) # trying to consume a empty iterator raises
    except TypeError:
        print x, "is empty"
    else:
        print x, "is not empty"

    # with isempty
    empty, it = isempty(iter(x))
    print x,  "is", ("empty" if empty else "not empty")

Andere Tipps

Das ist ganz hackish, aber Sie können i löschen und dann prüfen, ob es nach der Schleife vorhanden ist (wenn nicht, wird die Schleife noch nie passiert):

try:
    del i
except NameException: pass

for i in iterable:
    do_something(i)

try:
    del i
except NameException:
    do_something_else()

Ich denke, das ist wahrscheinlich hässlicher als nur eine Fahne mit obwohl

Update 2

Ich mochte Odomontois' Antwort . IMHO ist es besser, dieses Problem geeignet als das, was ich unten geschrieben.

Aktualisieren

(nach dem OP Kommentar zu lesen und bearbeitet Frage) Sie können das auch tun. Siehe unten:

def with_divisible(n, a, b, f):
 it = (i for i in xrange(a, b) if not i % n)
 for i in wrapper(it):
  f(i)

>>> with_divisible(1, 1, 1, lambda x: x)
Traceback (most recent call last):
  File "<pyshell#55>", line 1, in <module>
    with_divisible(1, 1, 1, lambda x: x)
  File "<pyshell#54>", line 3, in with_divisible
    for i in wrapper(it):
  File "<pyshell#46>", line 4, in wrapper
    raise EmptyIterableException("Empty")
EmptyIterableException: Empty

>>> with_divisible(7, 1, 21, lambda x: x)
7
14
...Snipped...
    raise EmptyIterableException("Empty")
EmptyIterableException: Empty

Original Antwort

Interessantes Problem. Ich habe einige Experimente und kam mit dem Follow-up:

class EmptyIterableException(Exception):
    pass

def wrapper(iterable):
    for each in iterable:
        yield each
    raise EmptyIterableException("Empty")

try:
    for each in wrapper(iterable):
        do_something(each)
except EmptyIterableException, e:
    do_something_else()
if not map(do_something_callable,iterable) : 
    # do something else

Der allgemeine Weg nach vorne, wenn ein Iterator teilverbrauchten geprüft werden soll, bevor sie ist itertools.tee zu verwenden. Auf diese Weise können wir zwei Kopien des Iterator haben und prüfen eine für Leere, während immer noch die andere Kopie von Anfang an raubend.

from itertools import tee
it1, it2 = tee(iterable)
try:
    it1.next()
    for i in it2:
        do_some_action(i) #iterator is not empty
except StopIteration:
    do_empty_action() #iterator is empty

Die StopIteration Ausnahme gebunden ist, ein Ergebnis des Aufrufs zu it1.next() sein, wie alle StopIteration Ausnahmen angehoben froom innerhalb der Schleife wird diese Schleife beenden.

Bearbeiten : für diejenigen, die nicht wie solche Ausnahmen, islice verwendet werden kann, einen einzigen Schritt Schleife einzurichten:

from itertools import tee, islice
it1, it2 = tee(iterable)
for _ in islice(it1, 1):
    #loop entered if iterator is not empty
    for i in it2:
        do_some_action(i)
    break #if loop entered don't execute the else section
else:
    do_empty_action()

Ich persönlich bevorzuge den ersten Stil. YMMV.

Was ist Umkehr "wenn" und "für":

if iterable:
    for i in iterable:
        do_something(i)
else:
    do_something_else()

OK, das funktioniert nicht!

Hier ist eine andere Lösung: http: // code.activestate.com/recipes/413614-testing-for-an-empty-iterator/

Dies ist eine Kombination von Michael Mrozek 's und FM 's Antworten:

def with_divisible(n, a, b, f):
    '''apply f to every integer x such that n divides x and a <= x < b'''
    it = (i for i in xrange(a, b) if not i % n)
    for i in it:
        f(i)
    try: i            # test if `it` was empty
    except NameError: print('do something else')

def g(i):
    print i,

with_divisible( 3, 1, 10, g)   # Prints 3 6 9.
with_divisible(33, 1, 10, g)   # Prints "do something else"

Generatoren haben eine ‚gi_frame‘ Eigenschaft, die keine ist, wenn der Generator erschöpft ist, aber erst nach StopIteration angehoben worden. Wenn das akzeptabel ist, ist hier etwas, das Sie könnten versuchen:

import types

def do(x, f, f_empty):
    if type(x) == types.GeneratorType:
        # generators have a 'gi_frame' property,
        # which is None once the generator is exhausted
        if x.gi_frame:
            # not empty
            return f(x)
        return f_empty(x)
    if x:
        return f(x)
    return f_empty(x)

def nempty(lst):
    print lst, 'not empty'

def empty(lst):
    print 'Twas empty!'

# lists
do([2,3,4], nempty, empty)
do([], nempty, empty)

# generators
do((i for i in range(5)), nempty, empty)
gen = (i for i in range(1))
gen.next()
try:
    gen.next()
except StopIteration:
    pass
do(gen, nempty, empty)
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top