طريقة الاصطلاحية لاتخاذ إجراء بشأن محاولة لحلق أكثر من الفراغ

StackOverflow https://stackoverflow.com/questions/3486257

  •  28-09-2019
  •  | 
  •  

سؤال

لنفترض أنني أتعامل مع الإفراط وأرغب في اتخاذ بعض الإجراءات إذا كان التكرار فارغًا. أفضل طريقتين يمكنني التفكير في القيام بذلك هي:

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

و

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

الأول يعتمد على أن تكون مجموعة كبيرة (لا جدوى لها عندما يتم نقلها إلى الوظيفة/الطريقة التي تكون فيها الحلقة) والمجموعات الثانية empty على كل تمريرة من خلال الحلقة التي تبدو قبيحة.

هل هناك طريقة أخرى في عداد المفقودين أم أن البديل الثاني هو الأفضل؟ سيكون من الرائع حقًا إذا كان هناك بعض الشرط الذي يمكنني إضافته إلى بيان الحلقة الذي سيتعامل مع هذا الأمر بالنسبة لي كثيرًا مثل else يصنع not_found الأعلام تذهب بعيدا.


أنا لا أبحث عن اختراقات ذكية.

أنا لا أبحث عن حلول تنطوي على الكثير من التعليمات البرمجية

أنا أبحث عن ميزة لغة بسيطة. أنا أبحث عن صافي و بيثوني الطريق للتكرار أكثر من ذلك واتخاذ بعض الإجراءات إذا كان الأمر فارغًا أن يفهم أي مبرمج بيثون ذي خبرة. إذا كان بإمكاني القيام بذلك دون تعيين علامة على كل تكرار ، فسيكون ذلك رائعًا. إذا لم يكن هناك تعبير بسيط يقوم بذلك ، فنسى ذلك.

هل كانت مفيدة؟

المحلول

أعتقد أن هذا أنظف طريقة للقيام بذلك:

# 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")

نصائح أخرى

هذا أمر مخترق تمامًا ، لكن يمكنك حذفه i ثم تحقق مما إذا كانت موجودة بعد الحلقة (إذا لم يكن الأمر كذلك ، فإن الحلقة لم تحدث أبدًا):

try:
    del i
except NameException: pass

for i in iterable:
    do_something(i)

try:
    del i
except NameException:
    do_something_else()

أعتقد أن هذا ربما يكون أكثر قبيحة من مجرد استخدام العلم

تحديث 2

أحببت إجابة Odomontois. Imho هو أكثر ملاءمة لهذه المشكلة مما كتبت أدناه.

تحديث

(بعد قراءة تعليق OP والسؤال المحرر) يمكنك القيام بذلك أيضًا. انظر أدناه:

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

الإجابة الأصلية

مشكلة مثيرة للاهتمام. قمت ببعض التجارب وتوصلت إلى ما يلي:

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

الطريق العام للمضي قدمًا إذا تم التحقق جزئيًا من التكرار itertools.tee. وبهذه الطريقة ، يمكننا الحصول على نسختين من التكرار والتحقق من واحدة من أجل الفراغ مع استمرار استهلاك النسخة الأخرى من البداية.

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

ال StopIteration الاستثناء لا بد أن يكون نتيجة للدعوة إلى it1.next(), ، كأي StopIteration الاستثناءات التي أثيرت FROT داخل الحلقة ستنتهي تلك الحلقة.

يحرر: بالنسبة لأولئك الذين لا يحبون مثل هذه الاستثناءات ، islice يمكن استخدامها لإعداد حلقة خطوة واحدة:

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()

أنا شخصيا أفضل النمط الأول. ymmv.

ماذا عن الانعكاس "إذا" و "ل":

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

حسنًا ، هذا لا يعمل!

هنا حل آخر: http://code.activestate.com/recipes/413614-testing-for-an-epty-iterator/

هذا مزيج من مايكل مروزيك'رمل FMإجابات:

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"

لدى المولدات خاصية "Gi_frame" والتي لا شيء بمجرد استنفاد المولد ، ولكن بعد التوقف فقط. إذا كان ذلك مقبولًا ، فإليك شيء يمكنك تجربته:

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)
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top