【问题标题】:Java and Python printing variables differentlyJava 和 Python 打印变量的方式不同
【发布时间】:2013-07-13 03:03:16
【问题描述】:

当我看到以下 Python 代码时,我正在学习变量作用域,并且正在查看一些线程:

a = 1
b = 2
c = 3

def foo():

    print a
    print b
    print c
    c = c + 1

def main():

    foo()

main()

打印出1 2UnBoundLocalError: local variable 'c' referenced before assignment。当我把它翻译成 Java 时

public class Test1 {

static int a = 1;
static int b = 2;
static int c = 3;

public static void foo()
{
    System.out.println(a);
    System.out.println(b);
    System.out.println(c);
    c = c + 1;
}   

public static void main(String[] args)
{
    foo();
}   
}

它打印出123。我很确定我翻译正确(如果不是,那就太尴尬了)。我的问题是为什么 Python 会给出错误而 Java 不会?是否与不同的作用域或它们的解释和编译方式有关?

【问题讨论】:

  • 我认为这是 python 2.7?
  • 希望你能通过我的回答了解更多

标签: java python scope


【解决方案1】:

Python 没有变量声明。相反,它定义了一个规则,即您在函数中分配的任何名称都是该函数的局部变量。这意味着该行

c = c + 1

foo 中使c 成为局部变量,所以

print c

尝试打印未分配的局部变量并引发异常。

Java 有变量声明。您的 Java 代码在 main 之外声明了 c 并且没有在内部重新声明它,因此 Java 知道 c 是一个静态变量,并且程序可以工作。将 Python 代码更好地翻译成 Java 可能是

public class Test1 {

    static int a = 1;
    static int b = 2;
    static int c = 3;

    public static void foo() {
        int c; // Now c is local, like in the Python
        System.out.println(a);
        System.out.println(b);
        System.out.println(c);
        c = c + 1;
    }   

    public static void main(String[] args) {
        foo();
    }   
}

【讨论】:

  • 所以python在开始执行里面的代码之前会分析函数的内容,拿出一个本地符号表?
  • 是的。 (我花了一段时间试图在此评论中想出更多要说的内容,以达到最少 15 个字符。我最终选择了退出。)
  • @BlackVegetable 我想是的。请参阅 “函数定义不执行函数体;仅在调用函数时才会执行。” (docs.python.org/2/reference/…) 和我的回答,其中我解释了我们必须如何理解模棱两可的术语“定义”以及在执行定义时会发生什么
【解决方案2】:

您的不理解和惊讶,就像许多学习 Python 的人一样(在 stackoverflow.com 上使用“赋值前引用”表达式进行研究),我认为是因为文档有时写得不好。

这个错误的解释在这里:

如果名称绑定操作发生在代码块中的任何位置,则所有 块内名称的使用被视为对 当前块。这可能会在名称中使用时导致错误 在绑定之前阻塞。这个规则很微妙。 Python缺乏 声明并允许名称绑定操作在任何地方发生 在一个代码块内。代码块的局部变量可以是 通过扫描块的整个文本来确定名称绑定 操作。

http://docs.python.org/2/reference/executionmodel.html

在我看来,这段摘录错误地表达了执行代码时所执行的操作:
说“可以通过扫描确定”是骗人的,给人的印象是这种扫描是可选的。

虽然我从未读过任何关于这一点的内容可以证实我的观点,但我个人认为:
- 事实上,这种扫描IS总是执行,这不是一个选项
- 更重要的是,此扫描是在块定义的可调用对象的任何调用之前完成的

.

确实,首先要理解一个重要的概念:

可调用对象的定义是在对象被调用之前完成的

必须意识到“定义”一词是模棱两可的,因为它可以通过两种方式来理解:
1/ 定义 = “定义”某些东西的脚本中的代码块
2/ 定义 = 在执行脚本的时刻执行此代码块以创建定义的可调用对象

我的这些断言基于:

是作为一个单元执行一段 Python程序文本。 以下是块:模块、函数体和类 定义。

http://docs.python.org/2/reference/executionmodel.html

.

函数定义定义了用户定义的函数对象 (...)
函数定义 [意义 1] 是一个可执行语句。它的执行绑定 当前本地命名空间中的函数名到函数对象 (...)
函数定义[意义2]不执行函数体; this 仅在调用函数时执行。

http://docs.python.org/2/reference/compound_stmts.html#function-definitions

.

A function definition defines a user-defined function object: 好漂亮的重言式!这句话说明了什么。我认为分析以下内容更有用:
[意义 1] 那里,“定义”的意思是“定义的代码块(=文本)”
[sens 2] 那里,“定义”的意思是“定义代码块的执行”;文本(定义意义 1)不执行任何操作,它作为文本被动地存在......

你看到“定义”这个名字是模棱两可的,而且文档有时写得不好.....

最后一个提取涉及函数定义,但这些概念显然可以扩展到类,其他可调用对象。类也由代码块定义,然后它们也存在这两个步骤:定义(意义 2= 执行定义代码块)然后调用。

.

所以我的主张是,我认为扫描可调用对象中的标识符并确定它们的范围是在执行代码块 [= 定义意义 1] 的那一刻执行的,这个执行是所谓的“定义”[意义 2]。
这就是我想指出的关键点。

.

PS:在上述文档摘录中使用术语“变量”是令人遗憾的,因为在 Python 中使用“变量”是另一个高度含糊的术语。
令人遗憾的证据是,OP 提出了他的问题,比较 Java 中发生的事情和 Python 中发生的事情。
如果在基本官方文档中的某个地方有一个可靠的解释,即在 Python 中编码器无法访问充当“内容可以更改的内存块”的实体,那么这种混淆应该很少发生。
但那是另一回事了

【讨论】:

  • 说得很好。感谢您抽出宝贵时间进行这次探索。
【解决方案3】:

Python 检查变量的局部作用域,如果它没有在局部作用域中声明或引用,它会搜索更高的作用域。通过在函数中使用c=c+1,Python 会在本地范围内看到c,并在您尝试打印时抛出错误,因为它没有被声明。如果您删除 c=c+1 它应该打印 c。要获得您期望的行为,请将 global c 放入您的函数中。

注意:通常使用全局变量不是一个好主意,因此 pythonic 的替代方法可能是将变量作为函数的参数传递,或者如果你正在做的事情最终适合类使变量self

例如

class myclass:
    def __init__(self):
        self.a = 1
        self.b = 2
        self.c = 3

    def count(self):
        print self.a
        print self.b
        print self.c
        self.c = self.c + 1

def main():
    thing = myclass()
    thing.count()
    thing.count()

main()

给予

nero@ubuntu:~/so$ python -i so.py 
1
2
3
1
2
4
>>> 

【讨论】:

  • 但是 print 语句看到 c 在更高的范围内......为什么分配没有尝试做同样的事情?
  • 查看@user2357112 的答案以获得更好的解释。
  • 我的回答试图明确“Python 检查变量的局部范围”背后的含义
【解决方案4】:

我认为混淆之处在于尽管 Python 是一种解释语言,但它确实分析了整个函数范围。查看示例:

>>> a=1;b=2;c=3
>>> def foo():
...     print a, b, c #c refers to the c in the outer scope
... 
>>> foo()
1 2 3
>>> 
>>> def foo():
...     print a, b, c #c refers to the local c defined later
...     c = 2
... 
>>> foo()
1 2
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 2, in foo
UnboundLocalError: local variable 'c' referenced before assignment
>>>

Python作用域规则可以参考LEGB

【讨论】:

    猜你喜欢
    • 2013-10-08
    • 2011-04-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-12-15
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多