طريقة الاصطلاحية لاتخاذ إجراء بشأن محاولة لحلق أكثر من الفراغ
-
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)