4

I am learning about variable scoping and was looking through some threads when I saw the following Python code:

a = 1
b = 2
c = 3

def foo():

    print a
    print b
    print c
    c = c + 1

def main():

    foo()

main()

which prints out 1 2 and UnBoundLocalError: local variable 'c' referenced before assignment. When I translated this to 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();
}   
}

It prints out 1 2 3. I am pretty sure I translated it correctly(highly embarrassing if it isn't). My question is why does Python give an error whereas Java does not? Is it something to do with different scoping or the way that they are interpreted and compiled?

Nestle Crunch
  • 77
  • 2
  • 6

4 Answers4

4

Python has no variable declarations. Instead, it defines a rule that any name you assign to in a function is a local variable of that function. That means that the line

c = c + 1

in foo makes c a local variable, so

print c

tries to print an unassigned local variable and raises an exception.

Java has variable declarations. Your Java code declares c outside main and doesn't redeclare it inside, so Java knows that c is a static variable, and the program works. A better translation of the Python code to Java might be

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();
    }   
}
user2357112
  • 260,549
  • 28
  • 431
  • 505
  • So python analyzes the contents of the function to come up with a local symbol table before it starts executing the code within? – BlackVegetable Jul 13 '13 at 03:14
  • 2
    Yup. (I spent a while trying to think of more to say in this comment to hit the minimum 15 characters. I ended up copping out.) – user2357112 Jul 13 '13 at 03:20
  • @BlackVegetable I think so. See _"The function definition does not execute the function body; this gets executed only when the function is called."_ (http://docs.python.org/2/reference/compound_stmts.html#function-definitions) and my answer in which I explain how we must understand the ambiguous term "definition" and what happens at the moment of the execution of a definition – eyquem Jul 13 '13 at 05:42
3

The incomprehension and astonishment you had, like many people learning Python (do a research on stackoverflow.com with "referenced before assignment" expression), me comprised, is due to the fact that the documentation is sometimes badly written.

The explanation of this error is here:

If a name binding operation occurs anywhere within a code block, all uses of the name within the block are treated as references to the current block. This can lead to errors when a name is used within a block before it is bound. This rule is subtle. Python lacks declarations and allows name binding operations to occur anywhere within a code block. The local variables of a code block can be determined by scanning the entire text of the block for name binding operations.

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

In my opinion, this extract badly expresses what is performed when a code is executed:
saying "can be determined by scanning" is deceiving, it gives the impression that this scanning is optional.

Though I've never read anything about this point that would have confirmed my opinion, I personnaly think that:
- in fact this scanning IS always performed, that's not an option
- more importantly, this scanning is done before any call of the callable object that the block is defining

.

Indeed, there's an important notion to understand first:

the definition of a callable object is done before the object can be called

One must realize that the term "definition" is ambiguous because it can be understood in two ways:
1/ definition = code block in a script that "defines" something
2/ definition = the execution of this code block at the moment of the execution of the script to create the defined callable object

I base these asertions on:

A block is a piece of Python program text that is executed as a unit. The following are blocks: a module, a function body, and a class definition.

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

.

A function definition defines a user-defined function object (...)
A function definition [sense 1] is an executable statement. Its execution binds the function name in the current local namespace to a function object (...)
The function definition [sense 2] does not execute the function body; this gets executed only when the function is called.

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

.

A function definition defines a user-defined function object : such a beautiful tautology ! This sentence explains NOTHING. I think more useful to analyze what follows:
[sense 1] there, "definition" means "the code block (=text) that defines"
[sens 2] there, "definition" means "the execution of the defining code block"; a text (definition sense 1) doesn't execute anything, it passively lies as a text...

You see that the name "definition" is ambiguous and the doc is sometimes badly written.....

The last extract concern functions definitions but the notions can evidently be extended to classes, other callable objects. Classes are also defined by code blocks and then these two steps exist for them too: definition (sense 2= execution of the defining code block) and then call.

.

So my claim is that I'm founded to think that the scanning of the identifiers in a callable object and the determination of their scopes is performed at the moment the code block [= definition sense 1] is executed, this execution being so called "definition" [sense 2] too.
That's what I wanted to point out as the key point in my opinion.

.

PS: the use of the term "variable" in the above extract of the doc is deplorable, because "variable" is another highly ambiguous term when used in Python.
The proof it is deplorable is that the OP presents his question doing comparison of what happens in Java and what happens in Python.
If there were somewhere in the basic official doc a solid explanation of the fact that in Python the coder has no access to entities acting as "chunk of memory whose content can change" , this kind of confusion should more rarely happen.
But that's another story

eyquem
  • 26,771
  • 7
  • 38
  • 46
1

Python checks local scope for variables and if it has not been declared or referenced in the local scope it searches higher scopes. By using c=c+1 in the function Python sees c in the local scope and throws an error when you try to print because it isn't declared. If you remove the c=c+1 it should print c. To obtain the behavior you expect put global c inside your function.

note: it's usually not a good idea to use global variables, so pythonic alternates could be to pass the variable as an argument of the function, or if what you're doing ends up being suited to classes make the variable self.

e.g.

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()

gives

nero@ubuntu:~/so$ python -i so.py 
1
2
3
1
2
4
>>> 
seth
  • 1,778
  • 16
  • 17
1

I think the confusion lies in that though Python is a interpretation language but it does analyze the whole function scope. See the examples:

>>> 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
>>>

For the Python scope rules, you can refer LEGB

Community
  • 1
  • 1
zhangyangyu
  • 8,520
  • 2
  • 33
  • 43