Novice Python often makes mistakes (part two)
Forwarded from: http://blog.jobbole.com/43826/
Scope
In this article, let's focus on the scope where Python is misused. Usually, when we define a global variable (well, I'm saying this because the need for explanation--the global variables are bad), we use a function to access them that can be understood by Python:
123 |
bar = 42 def foo(): print bar |
Here we use the global variable bar in the Foo function, and it works as expected:
This is cool. In general, after we use this feature, we want to use it in all the code. If the following example is used, it will work:
1234567 |
bar = [ 42 ] def foo(): bar.append( 0 ) foo() >>> print bar [ 42 , 0 ] |
But what if we change the bar a bit:
123456 |
< Code class= "Python plain" >>>> bar = 42 def foo (): ... bar = 0 ... Foo () print bar 42 |
We can see that the Foo function works well and does not throw an exception, but when we print the value of bar we find that its value is still 42. The reason for this is that bar=0 this line of code, which does not change the value of the global variable bar, but instead creates a local variable whose name is also called bar and its value is 0. This is a hard-to-find bug, which makes it very painful for beginners who don't really understand the python scope. To understand how Python handles local and global variables, let's look at a more rare, but potentially confusing, error, where we define a local variable called bar after we print the bar value:
1234 |
bar = 42 def foo(): print bar bar = 0 |
It's not going to go wrong, is it? We define a variable with the same name after the value is printed, so this should not be affected (Python is, after all, an interpreted language), is that really the case?
Something went wrong.
How is that possible? OK, here are two mistakes. The 1th is about Python, as an interpreted language (very cool, which we all agree on), and is executed one line at a a-line. In fact, Python is a declaration of a declaration executed. To make you feel a little bit about what I want to say, open your favorite shell and enter the following code:
Press ENTER. As you can see, the shell does not print any output but waits for you to continue with the definition of the function. The shell will always be like this until you stop defining the function. This is because defining a function is a declaration. Well, it's a mixed statement that contains some other declarations, but it's still a statement. Until the function is called, the contents of this function will not be executed. The object that is actually executing a function type is created.
This leads us to focus on the 2nd. Again, the dynamic and interpretive nature of Python makes us believe that when the print bar line is executed, Python will first look for the variable called Bar in the local scope and then look for the global scope. But what actually happens is that the local scope is not completely dynamic. When the DEF statement executes, Python will statically retrieve the information from the local scope of the function. When it comes to bar=0 (not executing this line of code, but when the Python interpreter reads this line of code), it adds the ' bar ' variable to the local variable list of the Foo function. When the Foo function executes and Python prepares to execute the print bar line, it looks for the variable in the local scope, because the process is static, and Python knows that the variable has not been assigned a value, and the variable has no value, so it throws an exception.
You might ask: Why not throw this exception when declaring a function? Python knows beforehand that the bar variable is referenced before it is assigned to a value. The answer to this question is that Python has no way of knowing whether the local variable bar has been assigned a value. Take a look at the following example:
12345 |
bar = 42 def foo (baz): if baz > 0 : print bar bar = 0 |
Python plays a subtle game between dynamic and static. The only thing it knows is that bar is assigned, but it doesn't know if the exception was quoted before the assignment until it really happened. Well, honestly, it doesn't even know if this variable is assigned!
12345678910111213 |
bar
=
42
def
foo():
print
bar
if
False
:
bar
=
0
>>> foo()
Traceback (most recent call last):
File
"<pyshell#17>"
, line
1
,
in
<module>
foo()
File
"<pyshell#16>"
, line
3
,
in
foo
print
bar
UnboundLocalError: local variable
‘bar‘
referenced before assignment
|
See the above code, although we as a smart creature can know very well not to assign value to bar. Python ignores that fact but still declares the local variable of bar.
I've said it long enough about this question. What we need is a solution, and I'll give you two solutions here.
12345678910 |
>>> bar = 42 def foo (): ... global bar ... print bar ... bar = 0 ... ... foo () 42 >>> bar 0 |
The first is to use the Global keyword. This is self-evident. This will let Python know that bar is a global variable rather than a local variable.
The second method, which is also more recommended, is not to use global variables. The Global keyword has never been used in many of my python development efforts. It's OK to know how to use it, but in the end try to avoid it. If you want to save the value from the beginning to the end of the code, define it as a property of a class. In this way, it is completely unnecessary to use global, when you want to use this value, through the properties of the class to access it:
1234567891011121314 |
>>>
class
Baz(
object
):
... bar
=
42
...
...
def foo():
...
print
Baz.bar
# global
... bar
=
0
# local
... Baz.bar
=
8
# global
...
print
bar
...
... foo()
...
print
Baz.bar
42
0
8
|
Novice Python often makes mistakes