编程语言中变量提升的利弊是什么?

3
以下Python程序A输出1,正如预期一样,而以下Python程序B会出现未绑定本地变量x错误,这与直觉相悖。
  • 程序A:
def f(): print(x)
x = 1
f()
  • 程序 B:
def f(): print(x); x = 2
x = 1
f()

Javascript有完全相同的行为。

  • 程序A:
function f() { console.log(x); }
let x = 1;
f();
  • 程序B:
function f() { console.log(x); let x = 2; }
let x = 1;
f();

然而,C++在这两种情况下输出预期的结果1

  • 程序A:
#include <iostream>
int x;
void f() { std::cout << x; }
int main() { x = 1; f(); return 0; }
  • 程序 B:
#include <iostream>
int x;
void f() { std::cout << x; int x = 2; }
int main() { x = 1; f(); return 0; }

所有程序A的输出都是1。Python和Javascript与C++之间在程序B中的差异源于它们不同的作用域规则:在C++中,变量的作用域从其声明开始,而在Python和Javascript中,它从变量声明所在块的开头开始。因此,在函数f中打印变量x的C++解析为全局变量x的值1,因为它是执行此时上下文中唯一的变量。在Python和Javascript中,在函数f中打印变量x会解析为空,并引发未绑定本地变量x错误,因为此时执行上下文中已经存在本地变量x,并且因此掩盖了全局变量x,但还没有绑定到值2。Python和Javascript的这种反直觉行为也称为变量提升,因为它将变量声明(但不是定义)“提升”到块的开头。
编程语言中变量提升的优点和缺点是什么?

“_反直觉的行为_”是由于块/函数作用域引起的。在底层,当解析嵌套作用域中的变量时,从其作用域顶部查找声明更快。最初在JS(解释性语言,仅限于var和函数作用域),您可以运行“愚蠢”的代码而不会中断执行。缺点是难以调试的逻辑错误。 - Teemu
@Teemu 是的,var 不像 let 那样会中断执行,因为它将值 undefined 分配给本地变量 x 而不是什么都不分配,但是本地变量 x 的作用域仍然与 let 一样遮蔽全局变量 x - Géry Ogam
是的,这是提升的一个后果。在 let(和 const)的上下文中,使用了一个术语 暂时性死区 - Teemu
1
Guido van Rossum(Python创建者)本人在此回答:链接 - Géry Ogam
1个回答

1
这更多是语言本身的特点,而不是面向程序员的特性。
对于Python和JavaScript,新变量意味着在名称字典中分配一个条目,直到您实际创建对象并进行分配。在C++中,运行时没有名称字典,定义需要实际为对象分配内存(它可能是一个10MB的数组,我们都不知道)。
如果您确实需要这样做,这确实可以使C ++适合更小的内存占用。否则,没有太多理由考虑它。
从开发人员的角度来看,您有一个错误。您的“x”有2个含义。编程已经很难了,没有必要让变量的含义改变,所以我会尽可能避免。我认为C ++在某些情况下会给您警告。
在实践中,您将习惯其中任何一种设置。

有趣。那么你是说变量提升是一种可取的编程特性,因为它可以防止将名称绑定到同一块中的非本地实体和本地实体吗?不,无论是Clang还是GCC都不会对C++程序发出警告。 - Géry Ogam
https://dev59.com/YF8e5IYBdhLWcg3w_-hM - Sorin
@Maggyero,我更喜欢明确的警告或错误提示来告诉你正在发生什么。在您的提升示例中遇到的错误可能会被误解并绕过。 - Sorin
1
我在官方Python论坛上提出了关于变量提升动机的问题,最终Guido本人给出了解释。如果您感兴趣,帖子在这里:https://discuss.python.org/t/why-does-python-have-variable-hoisting-like-javascript/4944。 - Géry Ogam

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接