问题:单行检查迭代器是否产生至少一个元素?
目前,我正在这样做:
try:
something = iterator.next()
# ...
except StopIteration:
# ...
但是我想要一个可以放在简单if
语句中的表达式。是否有内置的东西可以使这段代码显得不太笨拙?
any()
False
如果iterable为空,则返回,但如果不是,则可能会遍历所有项目。我只需要检查第一项即可。
有人问我要做什么。我编写了一个函数,该函数执行SQL查询并产生其结果。有时,当我调用此函数时,我只想知道查询是否返回了任何内容,并据此做出决定。
Currently I’m doing this:
try:
something = iterator.next()
# ...
except StopIteration:
# ...
But I would like an expression that I can place inside a simple if
statement.
Is there anything built-in which would make this code look less clumsy?
any()
returns False
if an iterable is empty, but it will potentially iterate over all the items if it’s not.
I only need it to check the first item.
Someone asks what I’m trying to do.
I have written a function which executes an SQL query and yields its results.
Sometimes when I call this function I just want to know if the query returned anything and make a decision based on that.
回答 0
any
如果为True,则不会超出第一个元素。万一迭代器产生一些虚假的东西,您可以编写any(True for _ in iterator)
。
any
won’t go beyond the first element if it’s True. In case the iterator yields something false-ish you can write any(True for _ in iterator)
.
回答 1
在Python 2.6+中,如果名称sentinel
绑定到迭代器无法产生的值,
if next(iterator, sentinel) is sentinel:
print('iterator was empty')
如果您不知道迭代器可能产生的结果,请使用以下命令创建自己的标记(例如,在模块顶部)
sentinel = object()
否则,您可以在哨兵角色中使用您“知道”(基于应用程序考虑)迭代器可能无法产生的任何值。
In Python 2.6+, if name sentinel
is bound to a value which the iterator can’t possibly yield,
if next(iterator, sentinel) is sentinel:
print('iterator was empty')
If you have no idea of what the iterator might possibly yield, make your own sentinel (e.g. at the top of your module) with
sentinel = object()
Otherwise, you could use, in the sentinel role, any value which you “know” (based on application considerations) that the iterator can’t possibly yield.
回答 2
这并不是真正干净,但是它显示了一种无损地将其打包在函数中的方法:
def has_elements(iter):
from itertools import tee
iter, any_check = tee(iter)
try:
any_check.next()
return True, iter
except StopIteration:
return False, iter
has_el, iter = has_elements(iter)
if has_el:
# not empty
这并不是真正的pythonic,在特定情况下,可能会有更好(但不太通用)的解决方案,例如下一个默认解决方案。
first = next(iter, None)
if first:
# Do something
这不是一般性的,因为None在许多可迭代对象中都可以是有效元素。
This isn’t really cleaner, but it shows a way to package it in a function losslessly:
def has_elements(iter):
from itertools import tee
iter, any_check = tee(iter)
try:
any_check.next()
return True, iter
except StopIteration:
return False, iter
has_el, iter = has_elements(iter)
if has_el:
# not empty
This isn’t really pythonic, and for particular cases, there are probably better (but less general) solutions, like the next default.
first = next(iter, None)
if first:
# Do something
This isn’t general because None can be a valid element in many iterables.
回答 3
您可以使用:
if zip([None], iterator):
# ...
else:
# ...
但这对于代码阅读器来说有点不解
you can use:
if zip([None], iterator):
# ...
else:
# ...
but it’s a bit nonexplanatory for the code reader
回答 4
最好的方法是使用peekable
from more_itertools
。
from more_itertools import peekable
iterator = peekable(iterator)
if iterator:
# Iterator is non-empty.
else:
# Iterator is empty.
请注意,如果您保留对旧迭代器的引用,则该迭代器将变得高级。从那时起,您必须使用新的可窥视迭代器。但是,实际上,peekable期望是修改该旧迭代器的唯一代码,因此无论如何您都不应保留对旧迭代器的引用。
The best way to do that is with a peekable
from more_itertools
.
from more_itertools import peekable
iterator = peekable(iterator)
if iterator:
# Iterator is non-empty.
else:
# Iterator is empty.
Just beware if you kept refs to the old iterator, that iterator will get advanced. You have to use the new peekable iterator from then on. Really, though, peekable expects to be the only bit of code modifying that old iterator, so you shouldn’t be keeping refs to the old iterator lying around anyway.
回答 5
关于什么:
In [1]: i=iter([])
In [2]: bool(next(i,False))
Out[2]: False
In [3]: i=iter([1])
In [4]: bool(next(i,False))
Out[4]: True
What about:
In [1]: i=iter([])
In [2]: bool(next(i,False))
Out[2]: False
In [3]: i=iter([1])
In [4]: bool(next(i,False))
Out[4]: True
回答 6
__length_hint__
估计-的长度list(it)
-这是私有方法,但是:
x = iter( (1, 2, 3) )
help(x.__length_hint__)
1 Help on built-in function __length_hint__:
2
3 __length_hint__(...)
4 Private method returning an estimate of len(list(it)).
__length_hint__
estimates the length of list(it)
– it’s private method, though:
x = iter( (1, 2, 3) )
help(x.__length_hint__)
1 Help on built-in function __length_hint__:
2
3 __length_hint__(...)
4 Private method returning an estimate of len(list(it)).
回答 7
这是一个过大的迭代器包装器,通常可以检查是否存在下一项(通过转换为布尔值)。当然效率很低。
class LookaheadIterator ():
def __init__(self, iterator):
self.__iterator = iterator
try:
self.__next = next (iterator)
self.__have_next = True
except StopIteration:
self.__have_next = False
def __iter__(self):
return self
def next (self):
if self.__have_next:
result = self.__next
try:
self.__next = next (self.__iterator)
self.__have_next = True
except StopIteration:
self.__have_next = False
return result
else:
raise StopIteration
def __nonzero__(self):
return self.__have_next
x = LookaheadIterator (iter ([]))
print bool (x)
print list (x)
x = LookaheadIterator (iter ([1, 2, 3]))
print bool (x)
print list (x)
输出:
False
[]
True
[1, 2, 3]
This is an overkill iterator wrapper that generally allows to check whether there’s a next item (via conversion to boolean). Of course pretty inefficient.
class LookaheadIterator ():
def __init__(self, iterator):
self.__iterator = iterator
try:
self.__next = next (iterator)
self.__have_next = True
except StopIteration:
self.__have_next = False
def __iter__(self):
return self
def next (self):
if self.__have_next:
result = self.__next
try:
self.__next = next (self.__iterator)
self.__have_next = True
except StopIteration:
self.__have_next = False
return result
else:
raise StopIteration
def __nonzero__(self):
return self.__have_next
x = LookaheadIterator (iter ([]))
print bool (x)
print list (x)
x = LookaheadIterator (iter ([1, 2, 3]))
print bool (x)
print list (x)
Output:
False
[]
True
[1, 2, 3]
回答 8
有点晚了,但是…您可以将迭代器变成一个列表,然后使用该列表:
# Create a list of objects but runs out the iterator.
l = [_ for _ in iterator]
# If the list is not empty then the iterator had elements; else it was empty.
if l :
pass # Use the elements of the list (i.e. from the iterator)
else :
pass # Iterator was empty, thus list is empty.
A little late, but… You could turn the iterator into a list and then work with that list:
# Create a list of objects but runs out the iterator.
l = [_ for _ in iterator]
# If the list is not empty then the iterator had elements; else it was empty.
if l :
pass # Use the elements of the list (i.e. from the iterator)
else :
pass # Iterator was empty, thus list is empty.