Why can functions in Python print variables in enc

2019-01-11 03:38发布

If I run the following code:

x = 1

class Incr:
    print(x)
    x = x + 1
    print(x)

print(x)

It prints:

1
2
1

Okay no problems, that's exactly what I expected. And if I do the following:

x = 1

class Incr:
    global x
    print(x)
    x = x + 1
    print(x)

print(x)

It prints:

1
2
2

Also what I expected. No problems there.

Now if I start making an increment function as follows:

x = 1

def incr():
    print(x)

incr()

It prints 1 just as I expected. I assume it does this because it cannot find x in its local scope, so it searches its enclosing scope and finds x there. So far no problems.

Now if I do:

x = 1

def incr():
    print(x)
    x = x + 1

incr()

This gives me the following error in the traceback:

UnboundLocalError: local variable 'x' referenced before assignment.

Why does Python not just search the enclosing space for x when it cannot find a value of x to use for the assignment like my class Incr did? Note that I am not asking how to make this function work. I know the function will work if I do the following:

x = 1

def incr():
    global x
    print(x)
    x = x + 1
    print(x)

incr()

This will correctly print:

1
2

just as I expect. All I am asking is why it doesn't just pull x from the enclosing scope when the keyword global is not present just like it did for my class above. Why does the interpreter feel the need to report this as an UnboundLocalError when clearly it knows that some x exists. Since the function was able to read the value at x for printing, I know that it has x as part of its enclosing scope...so why does this not work just like the class example?

Why is using the value of x for print so different from using its value for assignment? I just don't get it.

标签: python scope
7条回答
女痞
2楼-- · 2019-01-11 03:40

From Python scopes and namespaces:

It is important to realize that scopes are determined textually: the global scope of a function defined in a module is that module’s namespace, no matter from where or by what alias the function is called. On the other hand, the actual search for names is done dynamically, at run time — however, the language definition is evolving towards static name resolution, at “compile” time, so don’t rely on dynamic name resolution! (In fact, local variables are already determined statically.)

Which means that, the scope for x = x + 1 is determined statically, before the function is called. And since this is an assignment, then 'x' becomes a local variable, and not looked up globally.

That is also the reason why from mod import * is disallowed in functions. Because the interpreter won't import modules for you in compile time to know the names you are using in the function. i.e, it must know all names referenced in the function at compile time.

查看更多
看我几分像从前
3楼-- · 2019-01-11 03:41

As an extra example to the newly created A.x created within the class. The reassigning of x to 'inner' within the class does not update the global value of x because it is now a class variable.

x = 'outer'
class A:
    x = x
    print(x)
    x = 'inner'
    print(x)

print(x)
print(A.x)
查看更多
乱世女痞
4楼-- · 2019-01-11 03:49

It's the rule Python follows - get used to it ;-) There is a practical reason: both the compiler and human readers can determine which variables are local by looking only at the function. Which names are local has nothing to do with the context in which a function appears, and it's generally a Very Good Idea to follow rules that limit the amount of source code you have to stare at to answer a question.

About:

I assume it does this because it cannot find x in its local scope, so it searches its enclosing scope and finds x.

Not quite: the compiler determines at compile time which names are and aren't local. There's no dynamic "hmm - is this local or global?" search going on at runtime. The precise rules are spelled out here.

As to why you don't need to declare a name global just to reference its value, I like Fredrik Lundh's old answer here. In practice, it is indeed valuable that a global statement alerts code readers to that a function may be rebinding a global name.

查看更多
冷血范
5楼-- · 2019-01-11 03:49

I can try and make an educated guess why it works this way.

When Python encounters a string x = x + 1 in your function, it has to decide where to look up the x.

It could say "the first occurrence of x is global, and the second one is local", but this is quite ambiguous (and therefore against Python philosophy). This could be made part of the syntax, but it potentially leads to tricky bugs. Therefore it was decided to be consistent about it, and treat all occurrences as either global or local variables.

There is an assignment, therefore if x was supposed to be global, there would be a global statement, but none is found.

Therefore, x is local, but it is not bound to anything, and yet it is used in the expression x + 1. Throw UnboundLocalError.

查看更多
看我几分像从前
6楼-- · 2019-01-11 03:55

Because that's the way it was designed to work.

Basically, if you have an assignment anywhere in your function, then that variable becomes local to that function (unless you've used global, of course).

查看更多
beautiful°
7楼-- · 2019-01-11 04:05

Because it would lead to very hard to track down bugs!

When you type that x = x + 1, you could have meant to increment an x in the enclosing scope... or you could have simply forgotten that you already used x somewhere else and been trying to declare a local variable.

I would prefer the interpreter to only allow you to change the parent namespace if you intend to- this way you can't do it by accident.

查看更多
登录 后发表回答