【问题标题】:Compilation vs. Interpretation, Python has unexpected behaviour编译 vs. 解释,Python 有意想不到的行为
【发布时间】:2017-04-11 03:07:17
【问题描述】:

我得到了 python 执行的意外行为。

if True:
    print("Hello")
else:
    I am an error. what can you do about it?

现在,此代码不会引发 SyntaxError,因为控件永远不会进入 else 语句。在像 C++ 这样的编译语言中,它会出错。即使在 Java 中,未使用的代码也是一个错误。但不是在 Python 中。

现在在这种情况下:

x = 10
def foo():
    print(x)
    x += 1

打印语句引发了一个 UnboundLocalError 指定 here。 按照前面的逻辑,这个错误应该直到控件遇到x+=1才出现。但它确实如此,就像在任何其他编译语言中一样。

那么如何确定代码何时以编译或解释的方式运行?

编辑: 如果它被编译成字节码 .pyc 文件然后被解释。那为什么没有检测到第一个例子的else语句呢?

【问题讨论】:

  • 第一个例子中的else是语法错误。如果你把它放在一个 .py 文件中并尝试执行它,你会看到。
  • 我认为这与您的问题有关,您*在解释器外壳中运行代码,即 REPL。
  • 如果我们把它们都放在交互式shell中..会出现差异..你知道为什么吗?

标签: python python-3.x exception interpreted-language compiled-language


【解决方案1】:

它总是按照解释运行;但第一步是在整个文件上调用语法检查器,生成字节码文件。

在 python 2.7 和 3.5 中,您放在顶部的代码将导致语法错误:

python3 junk.py
  File "junk.py", line 4
    I am an error. what can you do about it?
              ^
SyntaxError: invalid syntax

在 python 中应该不可能出现“运行时”语法错误;实现这一点的唯一方法是动态导入有语法错误的模块。

我不确定我是否理解了您的问题,但第二种情况的错误是运行时错误;这就像在 C 中做int x = 0; y = 10/x;;变量作用域(“此时 x 是否存在?”)在 python 中的语法解析期间未解析。

--

编辑;这是我终端的转储:

Clank:tmp doug$ cat junk.py
if True:
    print("Hello")
else:
    I am an error. what can you do about it?

Clank:tmp doug$ python junk.py
  File "junk.py", line 4
    I am an error. what can you do about it?
       ^
SyntaxError: invalid syntax

Clank:tmp doug$ python3 junk.py
  File "junk.py", line 4
    I am an error. what can you do about it?
       ^
SyntaxError: invalid syntax

【讨论】:

  • 你能给我看看代码吗,因为我知道第一个不会导致语法错误。错误仅在控件到达语句时出现。这就是我的问题所在。
  • @AbhishekBera 将其添加到问题中。我很困惑你如何能够让它运行而不会出现语法错误。你能提供任何其他细节吗?您使用的是什么 Python 版本以及什么平台?
  • 好的..我想我可能已经弄清楚出了什么问题..当我在现场解释器中编写第一个时,它工作正常。但是当我把代码写到一个文件中时,SyntaxError 终于出现了。第二个是范围阴影。在全局范围内有一个变量 x,但每当遇到像 x+=1 这样的编辑时,它只会在本地范围内查找 x,而所有其他变量都会被消除。由于本地范围内没有变量,我们有 UnboundLocalError。
【解决方案2】:

相反。 Python 在执行前将源代码编译成字节码。第一种情况会在编译阶段引发语法错误。

在第二种情况下,编译器看到 x 在函数中被修改,因此它将 x 绑定到函数对象。每次调用函数时都会创建函数命名空间,但变量仅在分配时才出现。只有当您执行print(x) 时,python 才会意识到您正在请求一个尚未分配的局部变量。当并非所有执行路径都设置了应有的变量时,这是一个常见错误。

稍微修改示例,有时设置了局部变量,有时则没有。设置后,您会在局部变量中看到变量并且打印有效。不设置时,变量不在本地,打印失败。

x = 10
def foo(val):
    if val:
        x = 1
    print(val, 'before', locals())
    print(x)
    print(val, 'after')
    x += 1

foo(True)
foo(False)

输出

True before {'val': True, 'x': 1}
1
True after
False before {'val': False}
Traceback (most recent call last):
  File "o.py", line 11, in <module>
    foo(False)
  File "o.py", line 6, in foo
    print(x)
UnboundLocalError: local variable 'x' referenced before assignment

【讨论】:

    【解决方案3】:

    如果您通过一些简短的交互式 REPL(而不是 Python 的默认 REPL)运行此程序,您可能会使其实际执行而不会出现错误(例如,IPythons' qtconsole 允许它没有问题)。为什么允许这样做完全取决于 REPL 及其实现。

    在 Python 中,这个 SyntaxError;不能为它生成任何字节码:

    s ="""
    i= 1
    if i:
        print("Hello")
    else:
        I am an error. what can you do about it?
    """
    c = compile(s, '', mode='exec')
      File "<string>", line 6
        I am an error. what can you do about it?
           ^
    SyntaxError: invalid syntax
    

    语法特别不允许,只有字符串文字允许在它们之间放置空格(which Python later concatenates),名称不是(它自己的单个名称是当然允许)。简而言之,这在解析阶段失败了:

    from parser import suite
    st = suite(s)
      File "<string>", line 6
        I am an error. what can you do about it?
           ^
    SyntaxError: invalid syntax
    

    当 Python 看到两个名字被空格隔开时,它不知道该怎么做,空格并不意味着任何操作(str 文字除外)。

    【讨论】:

    • 是的,我正在使用 Ipython。但即使我在 REPL 中同时执行了这两个代码。第一个和第二个之间存在差异。我的问题是为什么会出现这种差异。实际上,我无法找到一个独特的理论来解释这两种情况。我也尝试过 Python2.7 和 3.5 的默认 REPL,结果是一样的……为什么这两个语句的行为不同?
    【解决方案4】:

    好的,在阅读了 Doug 和 Jim 的回答后,我想我对它的工作原理有所了解。 首先,所有示例都在 REPL 中工作(Ipython,默认)

    文件: 如果你把它写在一个文件中:

    if True:
        print("Hi")
    else:
        I am an error. What can you do about it?
    

    然后运行文件,它会抛出一个 SyntaxError。这证明了每当我们从文件中执行 python 代码时,它都会生成一个字节码,并且由于 else 中的语句不是有效的 python 表达式,我们会得到一个 SyntaxError。

    REPL: 使用 REPL,事情变得有点依赖。如果你在 python 解释器中输入

    >>>def foo():
           if True:
               print("Hey")
           else:
               I am an error. What can you do about it?
    >>>foo()
    Hey
    

    成功执行意味着没有字节码,对吧?等等。

    如果你这样写:

    >>>x = 10
    >>>def foo():
           print(x)
           x += 1
    >>>foo()
    

    然后轰隆隆!一切都崩溃了,您在 print(x) 语句中得到 UnboundLocalError 。这意味着存在字节码。

    那么这里到底发生了什么?

    如果 python 发现一个变量出现了一次,它会尝试通过首先读取所有变量来优化其工作。因此,在第二个示例中,当代码遇到 print(x) 时,它会尝试查找 x 上的所有操作。很快它就找到了语句 x+=1。由于在局部范围内没有提及 x 并且如果没有明确提及,python 永远不会在全局范围内查找变量,因此我们有

    UnboundLocalError: local variable 'x' is referenced before assignment 
    

    确凿的证据

    如果我们这样写:

    >>>x = 10
    >>>def foo():
          if True:
              print(x)
          else:
              x+=1
    >>>foo()
    UnboundLocalError: local variable 'x' referenced before assignment
    

    就是这样!

    x+=1 永远不会被执行,但是由于 print 语句会打印 x 并且另一个引用 (x+=1) 是问题所在,因此在打印该值之前遇到了错误。第一种情况在 REPL 中没有 SyntaxError 时运行良好,因为它从不费心查看 else 语句,因为它从不重要。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-02-22
      • 1970-01-01
      • 2011-03-18
      • 2019-08-05
      • 1970-01-01
      • 2010-11-12
      相关资源
      最近更新 更多