惯用的方式采取行动企图循环的一个空可迭代
-
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
标志走。
我不是在寻找聪明的黑客。
我不是在寻找解决方案,涉及大量的代码
我在寻找一个简单的语言功能。我在寻找一个 清楚的 和 python 路迭代的一个迭代,并采取了一些行动,如果可迭代是空的任何经验丰富的蟒蛇的程序员会理解。如果我可以这样做没有设定一个标志上的每一次迭代,那将是太棒了。如果没有简单的谚语,这样做,然后忘了这件事。
解决方案
我认为这是做到这一点的最干净的方法:
# 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
我喜欢 奥多托伊斯的答案. 。恕我直言,它比我在下面写的更适合这个问题。
更新
(阅读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
异常从内部循环将终止这一循环。
编辑:对于那些不喜欢这样的例外情况, 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()
我个人比较喜欢第一个风格。情况因人而异。
逆转“如果”和“ for”呢?
if iterable:
for i in iterable:
do_something(i)
else:
do_something_else()
好的,这不起作用!
这是另一个解决方案: http://code.activestate.com/recipes/413614-testing-for-an-ymenty-terator/
这是 迈克尔·姆罗泽克(Michael Mrozek)'沙 调频的答案:
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)