【问题标题】:Breakpoint on continue in if statement not hit如果语句未命中,则继续断点
【发布时间】:2020-10-04 19:54:20
【问题描述】:

在以下代码中,ab 都是生成器函数的输出,并且可以计算为 None 或具有值。

def testBehaviour(self):
  a = None
  b = 5

  while True:
    if not a or not b:
      continue
    print('blaat')

如果我在continue 语句所在的行和print 语句所在的行上放置断点(使用Visual Studio Code),则两者都不会命中。 print 语句没有被调用,循环只是按预期无限期地运行,但我希望断点被命中。

如果我将代码更改为以下之一:

def testBehaviour(self):
  a = None
  b = 5

  while True:
    if not a:
      continue
    print('blaat')

或:

def testBehaviour(self):
  a = None
  b = 5

  while True:
    if not a or not b:
      c = 'dummy'
      continue
    print('blaat')

再次在continueprint 语句的行上放置断点,断点被命中。

谁能告诉我为什么没有命中断点?这似乎不仅仅发生在 Visual Studio Code 中,因为我们的代码覆盖工具还指出 continue 语句未被调用。

这是在 32 位 Windows 7 上的 python 2.7 上。

【问题讨论】:

  • 您可以通过查看为不同版本的函数生成的字节码获得线索——您可以通过以下方式查看:dis.dis(testBehaviour)
  • @martineau 感谢您的建议!在字节码中我也没有看到太大的区别,它们都使用POP_JUMP_IF_FALSEJUMP_ABSOLUTEJUMP_FORWARD。你知道要寻找哪些具体的东西吗?
  • 这是python-3打印语句
  • @rioV8 print('Whatever') 也是有效的 Python 2.7 代码......但似乎无关紧要的语句类型。

标签: python python-2.7 visual-studio-code


【解决方案1】:

操作码中有优化,编译器识别if expr: continue

当使用if not a or not b:

 11          12 SETUP_LOOP              35 (to 50)
        >>   15 LOAD_GLOBAL              1 (True)
             18 POP_JUMP_IF_FALSE       49

 12          21 LOAD_FAST                1 (a)
             24 UNARY_NOT
             25 POP_JUMP_IF_TRUE        15
             28 LOAD_FAST                2 (b)
             31 UNARY_NOT
             32 POP_JUMP_IF_FALSE       41

 13          35 JUMP_ABSOLUTE           15
             38 JUMP_FORWARD             0 (to 41)

 14     >>   41 LOAD_CONST               2 ('blaat')
             44 PRINT_ITEM
             45 PRINT_NEWLINE
             46 JUMP_ABSOLUTE           15
        >>   49 POP_BLOCK
        >>   50 LOAD_CONST               0 (None)
             53 RETURN_VALUE

如果not a 为真,25 POP_JUMP_IF_TRUE 15 行将跳回到 while 循环的开头(第 15 行)。它永远不会到达 continue 语句的行 35 JUMP_ABSOLUTE 15

如果您切换ab 的值,则此测试用例有效。

如果将测试表达式重写为if not ( a and b ):,则会得到以下操作码

 11          12 SETUP_LOOP              33 (to 48)
        >>   15 LOAD_GLOBAL              1 (True)
             18 POP_JUMP_IF_FALSE       47

 12          21 LOAD_FAST                1 (a)
             24 JUMP_IF_FALSE_OR_POP    30
             27 LOAD_FAST                2 (b)
        >>   30 POP_JUMP_IF_TRUE        39

 13          33 JUMP_ABSOLUTE           15
             36 JUMP_FORWARD             0 (to 39)

 14     >>   39 LOAD_CONST               2 ('blaat')
             42 PRINT_ITEM
             43 PRINT_NEWLINE
             44 JUMP_ABSOLUTE           15
        >>   47 POP_BLOCK
        >>   48 LOAD_CONST               0 (None)
             51 RETURN_VALUE

这总是有效的,而且速度更快。 not 操作优化为跳转测试语句

【讨论】:

  • 哇,感谢您的详细回答!现在我明白了!
  • @DrDonut 这种行为也适用于 Python 3。这使得调试程序变得困难。单步执行有一些奇怪的行为,从您的问题设置断点不会总是命中。让新手程序员首先检查生成的操作码,看看是否没有执行优化是不正确的。仅当您请求进行优化的编译时才应该这样做。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-11-02
  • 2014-07-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-07-30
相关资源
最近更新 更多