我知道python有else
循环功能:
for item in items:
# loop block
else:
# will execute if there is no exception or break in loop block
由于这个功能,我想知道在python中是否还有其他关于循环的聪明之处。现在我想找到一个更好的方法(而不是变量)来查找循环块是否被执行(甚至一次):
items = []
for item in items:
# loop block
if #loop block was executed:
print("Big brother is watching you!")
答案 0 :(得分:1)
如果item
未在其他任何地方定义,您只需检查是否已分配item
:
items = []
for item in items:
pass
try:
del item
except NameError:
print("loop wasn't executed")
else:
print("loop was executed")
因此,如果items
为空,则不执行循环,因此item
未定义,您将获得异常。
del item
调用可确保在您第二次执行此代码时item
不存在。
(确定没有使用其他变量,但它仍然过于复杂:))
答案 1 :(得分:1)
items = []
for item in items:
# loop block
if items:
print("Big brother is watching you!")
答案 2 :(得分:1)
一些神奇的方法:
emptysentinel = item = object()
for item in items:
# loop block
if item is not emptysentinel:
print("Big brother is watching you!")
此方法提供了两个好处:
True
,False
的变量进行直接真实性测试,或None
;这是一个字节代码,执行直接C指针比较并存储True
或False
,然后进行相同的直接真实性测试)(类似于Jean-François Fabre's approach的成本items
为非空值,而便宜为空时更便宜)缺点是:
object
的开销非常低,仅使用16个字节的内存,而CPU时间却可以忽略不计)我个人会坚持标记的循环:
empty = True
for item in items:
empty = False
# loop block
if not empty:
print("Big brother is watching you!")
是的,它必须一遍又一遍地存储False
,但是(至少在CPython 3上,只要您在函数作用域内),这只是几个非常便宜的字节码:
LOAD_CONST
(C级数组查找操作,用于从函数常量中拉出False
)STORE_FAST
(C级数组存储操作,用于将刚加载的值推入分配给empty
变量的帧的数组插槽中)只要您在循环中进行任何实际工作,额外的LOAD_CONST
/ STORE_FAST
的花费就毫无意义;在我的机器上,每个循环的成本增加了12-13纳秒。首先创建一个emptysentinel
对象这样简单的事情的开销大约是90 ns。在简单的微基准测试中,基于emptysentinel
的方法不会比基于empty
标志的方法领先,直到items
包含至少25个元素(您可以提前八个元素,如果您将哨兵创建方式更改为emptysentinel = item = []
,则可以避免加载全局变量并通过常规函数调用机制进行构造,但是对哨兵使用空的list
会使意图变得不那么明显。)
如果常见的情况是items
是非空的,则Jean-François Fabre's approach的稍微修改(对于样式/最低定位的try
块)版本是最快的:
for item in items:
pass
try:
del item
except NameError:
pass
else:
# Everything but the del should go here, not the try block, so you don't
# accidentally catch NameErrors from other sources
print("Big brother is watching you!")
不需要初始化标记或标志,并且不会引发异常的try
块非常便宜(当items
是一个tuple
时)元素,它比标志方法要快一点,并且随着items
的增大而变得更快;当items
为非空时,它也比哨兵方法更快,但这是固定开销的问题;每增加一件商品的成本显然是相同的。
问题是,当items
为空时,它要昂贵得多。每种方法的微基准(将print
和# loop block
替换为pass
)的空items
的成本为:
emptysentinel
而非[]
创建object()
,则为87.6)try
/ except
/ else
:661 ns 因此,我为您提供使用哪种方法的最终规则是:
items
在很短的时间内为空,则最快,而在不为空时,则不要太长,更重要的是,它显而易见 try
/ except
一样缩放,并且可以处理空输入,而不会降低病理学速度try
/ except
/ else
方法,这对于非空items
始终是最快的,每当items
为空时,都要付出75个长循环的开销。旁注:另一种相对明显的方法(相当于对我而言明显的标志)是使用enumerate
;如果您想知道许多物品的数量,这不仅很有用,而且不仅是/不是“是否有物品?”,但它并不可怕:
numitems = 0
for numitems, item in enumerate(items, 1):
# loop block
if numitems:
print("Big brother is watching you!")
缺点是,在除一种情况以外的所有情况下,它都比所有其他方法慢。当try
为空时,它比except
/ else
/ items
快。与基于标志的方法相比,它的每个循环开销更高,并且与所有其他选项相比,其固定开销更高。显而易见,基于标志的方法也是如此,并且标志速度更快,因此只需使用它们即可。
答案 3 :(得分:0)
另一种解决方案是检查locals()中的循环变量:
for item in items:
# loop block
# loop block was executed
if 'item' in locals():
print("Big brother is watching you!")
尽管有一些限制: