Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/336.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 有没有比通过变量更好的方法来确定循环块是否被执行(甚至一次)?_Python_For Loop - Fatal编程技术网

Python 有没有比通过变量更好的方法来确定循环块是否被执行(甚至一次)?

Python 有没有比通过变量更好的方法来确定循环块是否被执行(甚至一次)?,python,for-loop,Python,For Loop,我知道python有else循环特性: for item in items: # loop block else: # will execute if there is no exception or break in loop block 由于这个特性,我想知道python中循环是否还有其他聪明的地方。现在,我想找到一种更好的方法(而不是通过变量)来确定是否执行了循环块(甚至一次): 如果项目没有在其他任何地方定义,您只需检查项目是否已分配: items = [] for i

我知道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:
    pass
try:
    del item
except NameError:
    print("loop wasn't executed")
else:
    print("loop was executed")
因此,如果
items
为空,则不会执行循环,因此
item
不会被定义,您会得到异常

delitem
调用确保第二次执行此代码时,
item
不存在


(可以不使用另一个变量,但它仍然过于复杂:)

有点神奇的方法:

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
    ,然后进行相同的直接真实性测试)(与
    项目
    为非空时的成本类似,为空时的成本更低)
  • 缺点是:

  • 需要创建一个新对象作为哨兵(不是什么大问题;普通
    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
    (从函数常量中提取
    False
    的C级数组查找操作)
  • STORE\u FAST
    (一种C级数组存储操作,用于将刚加载的值推入分配给
    空变量的帧数组插槽)
  • 只要你在循环中做任何实际的工作,一个额外的
    加载常量
    /
    存储快速
    的成本是毫无意义的;在我的机器上,每个循环的成本增加了12-13纳秒。像首先创建
    清空continel
    对象这样简单的事情的开销大约是90纳秒;在简单的micr中obenchmarks,基于
    emptysentel
    的方法不会领先于基于
    empty
    标记的方法,直到
    items
    至少包含25个元素(如果将sentinel创建更改为
    emptysentel=item=[],则可以提前8个元素)
    避免了加载全局函数和通过一般函数调用机制进行构造,但对哨兵使用空的
    列表会使意图更加不明显)

    如果常见情况是
    为非空,则稍微修改(对于样式/最小目标
    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
    时,它比flag方法快一点,并且随着
    items
    变大而变快;当
    items
    为非空时,它也比sentinel方法快,但这是一个固定开销的问题;每个附加项的成本显然是相同的)

    问题是,当
    项目
    为空时,成本要高得多。每种方法的微基准(用
    打印
    循环块
    替换为
    传递
    )都有空
    项目
    的成本,如下所示:

  • 基于标志的:64.8纳秒
  • 基于哨兵:159纳秒(如果使用
    []
    而不是
    对象()创建
    清空哨兵
    ,则为87.6纳秒)
  • 尝试
    /
    除了
    /
    其他
    基于:661 ns
  • 因此,我将为您提供使用哪种方法的最终规则:

  • 使用基于标志的方法
  • 认真地说,使用基于标志的方法;如果
    在相当长的时间内是空的,这是最快的,而非空的时间不会太长,更重要的是,这是显而易见的
  • 真的吗?那么好吧:如果你的非空输入比较大,而且你真的需要速度,但是空输入仍然是半常见的,那么使用基于哨兵的方法;它与
    try
    /
    一样可伸缩,除了
    ,并且处理空输入时不会在病理上减慢速度
  • 如果您的输入几乎总是非空的,您可以使用
    try
    /
    方法,但
    /
    else
    方法除外,该方法始终是非空
    的最快方法,但代价是在
    为空时支付75个长循环的开销

  • 旁注:另一个相对明显的方法(相当于我所熟悉的标志)是使用
    枚举
    ;如果您想知道有多少项,这更有用,而不仅仅是“有没有项?”,但这并不可怕:

    numitems = 0
    for numitems, item in enumerate(items, 1):
        # loop block
    
    if numitems:
        print("Big brother is watching you!")
    

    缺点是它比所有其他方法都慢,但只有一种情况除外;它比
    try
    /
    快,除了
    /
    else
    项为空时。它比基于标志的方法具有更高的每循环开销,比所有其他选项具有更高的固定开销。因此很明显,标志也是如此sed方法和标志更快,所以只需使用它们。

    另一种解决方案是检查局部变量()中的循环变量:

    但它也有一些局限性:

    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!")
    numitems = 0
    for numitems, item in enumerate(items, 1):
        # loop block
    
    if numitems:
        print("Big brother is watching you!")
    
    for item in items:
        # loop block
    
    # loop block was executed
    if 'item' in locals():
        print("Big brother is watching you!")